Here is the thing a device that we have transmits regular updates of a custom characteristic of a custom service. The service and characteristic in this device is defined via a XML file. This, of course, is referring to the Bluetooth BLE protocol.
What I'm trying to do is create a simple Qt Android App that connects to the device and monitors the update. I've gotten as far as discovering the service and connecting it signal to it. I've done that using this code:
void BLETest::on_stateChanged(QLowEnergyService::ServiceState state){
Q_UNUSED(state);
// Only printing data if all services are in correct state.
for (qint32 i = 0; i < monitoredServices.size(); i++){
if (monitoredServices.at(i)->state() != QLowEnergyService::ServiceDiscovered){
logger->out("There are still services that have not been discoverd",Logger::LC_ORANGE);
return;
}
}
QString msg = "PRINTING SERVICE DATA<br>";
for (qint32 i = 0; i < monitoredServices.size(); i++){
QLowEnergyService *monitoredService = monitoredServices.at(i);
QList<QLowEnergyCharacteristic> clist = monitoredService->characteristics();
msg = msg + "SERVICE: " + monitoredService->serviceName() + ". UUID: " + monitoredService->serviceUuid().toString() + "<br>";
// Checking if this is the service that need connection.
if (monitoredService->serviceUuid() == QBluetoothUuid(QString("0a464eef-af72-43fd-8a8b-1f26f6319dab"))){
QString res;
if (connect(monitoredService,SIGNAL(characteristicChanged(QLowEnergyCharacteristic,QByteArray)),this,SLOT(on_charastericChanged(QLowEnergyCharacteristic,QByteArray)))) res = "true";
else res = "false";
logger->out("CONNECTED TO TARGET SERVICE: " + res,Logger::LC_ORANGE);
}
for (int i = 0; i < clist.size(); i++){
QString name = clist.at(i).name();
if (name.isEmpty()) name = "UNDEFINED NAME";
QByteArray buffer = clist.at(i).value();
//QString value = QString(clist.at(i).value());
QString value = QByteArray(buffer.toHex()) + " (BS = " + QString::number(buffer.size()) + ")";
QString properties = QString::number(clist.at(i).properties());
msg = msg + "CHARACTERISTIC: " + clist.at(i).uuid().toString() + " - " + name + ": " + value + ". PROPERTIES: " + properties + "<br>";
}
if (clist.isEmpty()){
msg = msg + "No characteristics found<br>";
}
}
logger->out(msg);
}
The above functions waits for all services to be discovered then prints the UUID, name and Value for all characteristics of all services. When the service I want to monitored is processed a connection is done to the changedCharacteristic signal.
When I do this the printed value of the characteristic of the service I want to monitor is the original value for that characteristic. However as that value updates I'm not notified (the signal never triggers) and so the value never changes in my app.
Do I need to write some code to actually trigger the signals?
PD: Using the Blue Gecko Demo App I can see the values changing.
EDIT: I decided to use a timer to Poll the value of the characteristic and it never changes. Which might indicate why the signal is never generated either.
You should connect a chracteristic changed handler to the service:
connect(service, SIGNAL(characteristicChanged(QLowEnergyCharacteristic, QByteArray)), this, SLOT(on_characteristicChanged(QLowEnergyCharacteristic, QByteArray)));
In the slot you can explore the data array.
However, the signal (characteristicChanged()) will only emitted by the service if the for this chracteristic notification is enabled. This works only, if the characteritic has a notify property, that should the case in your application.
Related
I develop an app (push to /system/app)and want to modify some system settings, I do like the
public void changeAnimationSettings(boolean isChecked) {
int result1 = isChecked ? 0 : 1;
Settings.Global.putInt(getContentResolver(), Settings.Global.ANIMATOR_DURATION_SCALE, result1);
Settings.Global.putInt(getContentResolver(), Settings.Global.WINDOW_ANIMATION_SCALE, result1);
Settings.Global.putInt(getContentResolver(), Settings.Global.TRANSITION_ANIMATION_SCALE, result1);
Log.i(TAG, "---->>ANIMATOR_DURATION_SCALE: " + Settings.Global.getString(getContentResolver(), Settings.Global.ANIMATOR_DURATION_SCALE));
Log.i(TAG, "---->>WINDOW_ANIMATION_SCALE: " + Settings.Global.getString(getContentResolver(), Settings.Global.WINDOW_ANIMATION_SCALE));
Log.i(TAG, "---->>TRANSITION_ANIMATION_SCALE " + Settings.Global.getString(getContentResolver(), Settings.Global.TRANSITION_ANIMATION_SCALE));
}
the log show the value has changed successful, but
the problem is the modify will not work immediately untill I open the system settings app again .
I guess the value change is not notify to system in my app.
i have a question to Google Fit.
I am creating a step counter (oh wonder g). This i have already done so far and it not really hard.
But now we come to my problem. I am only reading the steps with the Sensor API. The issue is, i can add new data via for example the Google Fit app and it will be counted in my app too. This introduces cheating and i do not want this.
So i need to have a way to only read "device created" data and not manually added data. Is there a nice way to to this?
From the SDK documentation it is not really clear how to proceed here.
So i need to have a way to only read "device created" data and not
manually added data. Is there a nice way to to this?
You will want to use Private Custom Data Types to achieve that. Read about the different types of Fitness data you can upload to Google Fit here.
1. Public data types
Standard data types provided by the platform, like com.google.step_count.delta. Any app can read and write data of
these types. For more information, see Public Data Types.
2. Private custom data types
Custom data types defined by an specific app. Only the app that defines the data type can read and write data
of this type. For more information, see Custom Data Types.
3. Shareable data types
Custom data types submitted to the platform by an app developer. Once approved, any app can read data of a
shareable type, but only whitelisted apps as specified by the
developer can write data of that shareable type. For more information,
see Shareable Data Types.
I was able to do this with the help of this alogrithm. But remember due to Android fragmentation this code still removes some of the user's data and count it as penalty
private String dumpDataSet(DataSet dataSet, int x) {
List<String> days = new ArrayList<>();
days.add("Monday");
days.add("Tuesday");
days.add("Wednesday");
days.add("Thursday");
days.add("Friday");
days.add("Saturday");
days.add("Sunday");
String day = days.get(Math.round(x / 24));
Log.d(TAG, "\tDay: " + day);
Log.i(TAG, "Data returned for Data type: " + dataSet.getDataType().getName());
DateFormat dateFormat = getTimeInstance();
String text = "";
try {
for (DataPoint dp : dataSet.getDataPoints()) {
Log.i(TAG, "\tStepCount getStreamName: " + dp.getOriginalDataSource().getStreamName());
Log.i(TAG, "\tStepCount getStreamIdentifier: " + dp.getOriginalDataSource().getStreamIdentifier());
Log.i(TAG, "\tStepCount App Type: " + dp.getDataType().getName());
Log.i(TAG, "\tStepCount Type: " + dp.getOriginalDataSource().getType());
for (Field field : dp.getDataType().getFields()) {
Log.i(TAG, "\tField: " + field.getName() + " Value: " + dp.getValue(field));
text += dp.getValue(field);
String si[] = dp.getOriginalDataSource().getStreamIdentifier().toLowerCase().split(":");
if ((((si[si.length - 1].contains("soft")) || (si[si.length - 1].contains("step"))) && si[si.length - 1].contains("counter"))) {
totalSteps += Integer.parseInt(dp.getValue(field).toString());
Log.d(TAG, "\tStepCount" + " Added Steps -> " + dp.getValue(field) + " steps");
text += "\n\n";
} else {
Log.e(TAG, "\tStepCount PENALTY ---------------------------------------------------------------");
Log.e(TAG, "\tDay = " + day + " | Hour Number = " + x + " | StepCount" + " PENALTY DEDUCTED -> " + dp.getValue(field) + " steps");
Log.e(TAG, "\tStepCount PENALTY getStreamIdentifier: " + dp.getOriginalDataSource().getStreamIdentifier());
Log.e(TAG, "\tStepCount PENALTY getStreamName: " + dp.getOriginalDataSource().getStreamName());
Log.e(TAG, "\tStepCount PENALTY App Type: " + dp.getDataType().getName());
Log.e(TAG, "\tStepCount PENALTY Type: " + dp.getOriginalDataSource().getType());
Log.e(TAG, "\tStepCount PENALTY ---------------------------------------------------------------");
}
}
}
} catch (Exception ex) {
ex.getStackTrace();
}
return text;
}
----- UPDATE -----
You can also call
DataPoint.getOriginalDataSource().getAppPackageName()
to filter out smartwatches and other apps.
I tried as suggested by Ali Shah lakhani but
DataPoint.getOriginalDataSource().getAppPackageName();
/*I also tried but could not achieve what I wanted*/
DataPoint.getOriginalDataSource().getStreamName();
DataPoint.getOriginalDataSource().getStreamIdentifier();
did not work at least for me while retrieving data. I ended up using readDailyTotalFromLocalDevice() as shown below in order to capture steps captured by device only.
Fitness.HistoryApi.readDailyTotalFromLocalDevice(mApiClient, DataType.TYPE_STEP_COUNT_DELTA).await(1, TimeUnit.MINUTES)
I cross checked the same with some of the apps that avoids manual entries in their app and the count provided by the function above is exactly the same.
Note: If a user is having multiple devices and is using the app on all of them, readDailyTotalFromLocalDevice() will have different value for each and every device since the function is responsible for returning device specific data only.
I built a pc program that works on your desktop and informs you about notifications, power-level, and lets you interact with your smartphone.
All over the ADB - so no app on the phone is required.
And all that in a stylish way.
But enough for that, it worked until android 5 came along.
The notification dump (adb shell dumpsys notifications) shows different outputs than in sdk <= 19
For example:
The extras region showed me detailed informations about the notifications.
extras={
android.title=String
android.subText=null
android.template=String
android.showChronometer=Boolean (false)
android.icon=Integer (2130837507)
android.text=String
android.progress=Integer (0)
android.progressMax=Integer (0)
android.showWhen=Boolean (true)
android.rebuild.applicationInfo=ApplicationInfo (ApplicationInfo{14a2c165 com.nero.android.htc.sync})
android.rebuild.contentView=Boolean (true)
android.bigText=String
android.infoText=null
android.originatingUserId=Integer (0)
android.progressIndeterminate=Boolean (false)
android.rebuild=Boolean (true)
android.rebuild.bigView=Boolean (true)
}
This is an example of the android 5 Notification dump.
You see, there are only datatypes for strings, not the actual values.
Does anybody know how i can get the actual values? Like some parameters i am missing?
Or do you know an even better way of getting the notifications from the phone to the pc?
Well, I faced the same problem and it seems it has no solution.
I've looked at the source code and compared it to the version when everything was working.
In the version 4.4.4 it was still working. Changes appeared in the version 5.0.0 and the code wasn't changed to the version 5.1.1 (the last source code version we have on the moment I'm writing this post).
So, lets look at the source code that responsible for printing extra section of notification.
As you see, it's everything ok in Android 4.4.4, the code prints string representation of the value.
In Android 5.0.0 - 5.1.1 it prints only a type of value, but not the value itself.
Android 4.4.4.
if (notification.extras != null && notification.extras.size() > 0) {
pw.println(prefix + " extras={");
for (String key : notification.extras.keySet()) {
pw.print(prefix + " " + key + "="); // <====== print key name
Object val = notification.extras.get(key);
if (val == null) {
pw.println("null");
} else {
pw.print(val.toString()); // <====== print value here
if (val instanceof Bitmap) {
pw.print(String.format(" (%dx%d)",
((Bitmap) val).getWidth(),
((Bitmap) val).getHeight()));
} else if (val.getClass().isArray()) {
pw.println(" {");
final int N = Array.getLength(val);
for (int i=0; i<N; i++) {
if (i > 0) pw.println(",");
pw.print(prefix + " " + Array.get(val, i));
}
pw.print("\n" + prefix + " }");
}
pw.println();
}
}
pw.println(prefix + " }");
}
Android 5.0.0 - 5.1.1.
if (notification.extras != null && notification.extras.size() > 0) {
pw.println(prefix + " extras={");
for (String key : notification.extras.keySet()) {
pw.print(prefix + " " + key + "="); // <====== print key name
Object val = notification.extras.get(key);
if (val == null) {
pw.println("null");
} else {
pw.print(val.getClass().getSimpleName()); // <===== print a type, not a value itself
if (val instanceof CharSequence || val instanceof String) {
// redact contents from bugreports
} else if (val instanceof Bitmap) {
pw.print(String.format(" (%dx%d)",
((Bitmap) val).getWidth(),
((Bitmap) val).getHeight()));
} else if (val.getClass().isArray()) {
final int N = Array.getLength(val);
pw.println(" (" + N + ")");
} else {
pw.print(" (" + String.valueOf(val) + ")");
}
pw.println();
}
}
pw.println(prefix + " }");
}
On Android >= 6 use this command to get full text of notification extras:
adb shell dumpsys notification --noredact
it will print both types and values
extras={
android.title=String (Bring The Rain)
android.reduced.images=Boolean (true)
android.subText=null
android.template=String (android.app.Notification$MediaStyle)
toSingleLine=Boolean (false)
android.text=String (Upon A Burning Body)
android.appInfo=ApplicationInfo (ApplicationInfo{c8165e6 org.telegram.messenger})
android.showWhen=Boolean (false)
android.largeIcon=Icon (Icon(typ=BITMAP size=100x100))
android.mediaSession=Token (android.media.session.MediaSession$Token#608a746)
gameDndOn=Boolean (false)
android.compactActions=int[] (3)
[0] 0
[1] 1
[2] 2
}
I built a pc program that works on your desktop and informs you about notifications, power-level, and lets you interact with your smartphone.
All over the ADB - so no app on the phone is required.
And all that in a stylish way.
But enough for that, it worked until android 5 came along.
The notification dump (adb shell dumpsys notifications) shows different outputs than in sdk <= 19
For example:
The extras region showed me detailed informations about the notifications.
extras={
android.title=String
android.subText=null
android.template=String
android.showChronometer=Boolean (false)
android.icon=Integer (2130837507)
android.text=String
android.progress=Integer (0)
android.progressMax=Integer (0)
android.showWhen=Boolean (true)
android.rebuild.applicationInfo=ApplicationInfo (ApplicationInfo{14a2c165 com.nero.android.htc.sync})
android.rebuild.contentView=Boolean (true)
android.bigText=String
android.infoText=null
android.originatingUserId=Integer (0)
android.progressIndeterminate=Boolean (false)
android.rebuild=Boolean (true)
android.rebuild.bigView=Boolean (true)
}
This is an example of the android 5 Notification dump.
You see, there are only datatypes for strings, not the actual values.
Does anybody know how i can get the actual values? Like some parameters i am missing?
Or do you know an even better way of getting the notifications from the phone to the pc?
Well, I faced the same problem and it seems it has no solution.
I've looked at the source code and compared it to the version when everything was working.
In the version 4.4.4 it was still working. Changes appeared in the version 5.0.0 and the code wasn't changed to the version 5.1.1 (the last source code version we have on the moment I'm writing this post).
So, lets look at the source code that responsible for printing extra section of notification.
As you see, it's everything ok in Android 4.4.4, the code prints string representation of the value.
In Android 5.0.0 - 5.1.1 it prints only a type of value, but not the value itself.
Android 4.4.4.
if (notification.extras != null && notification.extras.size() > 0) {
pw.println(prefix + " extras={");
for (String key : notification.extras.keySet()) {
pw.print(prefix + " " + key + "="); // <====== print key name
Object val = notification.extras.get(key);
if (val == null) {
pw.println("null");
} else {
pw.print(val.toString()); // <====== print value here
if (val instanceof Bitmap) {
pw.print(String.format(" (%dx%d)",
((Bitmap) val).getWidth(),
((Bitmap) val).getHeight()));
} else if (val.getClass().isArray()) {
pw.println(" {");
final int N = Array.getLength(val);
for (int i=0; i<N; i++) {
if (i > 0) pw.println(",");
pw.print(prefix + " " + Array.get(val, i));
}
pw.print("\n" + prefix + " }");
}
pw.println();
}
}
pw.println(prefix + " }");
}
Android 5.0.0 - 5.1.1.
if (notification.extras != null && notification.extras.size() > 0) {
pw.println(prefix + " extras={");
for (String key : notification.extras.keySet()) {
pw.print(prefix + " " + key + "="); // <====== print key name
Object val = notification.extras.get(key);
if (val == null) {
pw.println("null");
} else {
pw.print(val.getClass().getSimpleName()); // <===== print a type, not a value itself
if (val instanceof CharSequence || val instanceof String) {
// redact contents from bugreports
} else if (val instanceof Bitmap) {
pw.print(String.format(" (%dx%d)",
((Bitmap) val).getWidth(),
((Bitmap) val).getHeight()));
} else if (val.getClass().isArray()) {
final int N = Array.getLength(val);
pw.println(" (" + N + ")");
} else {
pw.print(" (" + String.valueOf(val) + ")");
}
pw.println();
}
}
pw.println(prefix + " }");
}
On Android >= 6 use this command to get full text of notification extras:
adb shell dumpsys notification --noredact
it will print both types and values
extras={
android.title=String (Bring The Rain)
android.reduced.images=Boolean (true)
android.subText=null
android.template=String (android.app.Notification$MediaStyle)
toSingleLine=Boolean (false)
android.text=String (Upon A Burning Body)
android.appInfo=ApplicationInfo (ApplicationInfo{c8165e6 org.telegram.messenger})
android.showWhen=Boolean (false)
android.largeIcon=Icon (Icon(typ=BITMAP size=100x100))
android.mediaSession=Token (android.media.session.MediaSession$Token#608a746)
gameDndOn=Boolean (false)
android.compactActions=int[] (3)
[0] 0
[1] 1
[2] 2
}
The goal is to connect a guitar to USB host capable Android device, do some processing on the signal and play it through the device.
Problem is I'm not finding much documentation on it. The device shows up can contains 6 interfaces.
However, in all the examples I see, the first interface is always used..
UsbInterface intf = device.getInterface(0);
My device contains 6 interfaces BUT the first interface, i.e. getInterface(0) has no endpoints. 3/6 have no endpoints but the other 3 all have 1 end point.
I read that you need to find the correct interface and endpoint. In my case, I only want an IN endpoint to receive data.
Any advice on how to that would be very appreciated.
Cheers
This is how I got down to the bottom of it.
The last interface, I believe is the one I am looking for.
It has a direction value of USB_DIR_IN (3)
It has a endpoint type value USB_ENDPOINT_XFER_ISOC (1) signifying the regular isochronous connection I was looking for
// Cycle through interfaces and print out endpoint info
StringBuilder builder = new StringBuilder();
for (int i=0; i<device.getInterfaceCount(); i++)
{
String epDirString = "No endpoints";
String epTypeString = "No endpoints";
if (device.getInterface(i).getEndpointCount() > 0)
{
epDirString = String.valueOf(device.getInterface(i).getEndpoint(0).getDirection());
epTypeString = String.valueOf(device.getInterface(i).getEndpoint(0).getType());
}
builder.append("Int. " + i + " EP count: " + device.getInterface(i).getEndpointCount() +
" || EP direction: " + epDirString + " || EP type: " + epTypeString + "\n");
}
// Show results in a dialog
Builder dBuilder = new AlertDialog.Builder(USBActivity.this);
dBuilder.setMessage(builder.toString()).show();