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
}
Related
I am working on a inventory management system where it's required to write asset ids (with length of maximum 17) in to an RFID tag.
First the problem was that when I write on a tag an ID with shorter length than the already written one, it keeps the non overridden characters in the tag.
for ex: if the tag has the ID "123456789" written on it and I write id "abc" on the tag. The tag's asset id becomes abc456789. I tried killing and erasing the tag before writing but it didn't work.
After that, I though of appending zeros before the target ID until it reaches the maximum length (17) so that this way no asset id with shorter length will be written on the tag and after reading I remove all preceding zero's. This worked well with a certain tag but not with another one, I figured out that the other tag can't be written on with more than 12 characters but I don't get why and the problem isn't in the RFID tag since it works well in another application and can be written on with more than 12 characters.
I would be really thankful if anyone could help me identify why this tag has only 12 characters written on it and the rest of the asset ID is neglected even though the same code works with another RFID tag.
Here's the write tag method:
fun writeTag(sourceEPC: String?, targetData: String): TagData? {
errorMessage = ""
try {
val tagData = TagData()
val tagAccess = TagAccess()
val writeAccessParams = tagAccess.WriteAccessParams()
writeAccessParams.accessPassword = 0
writeAccessParams.memoryBank = MEMORY_BANK.MEMORY_BANK_EPC
writeAccessParams.offset = 2
var paddedTargetData = padLeftZeros(targetData,17)
val targetDataInHex = HexStringConverter.getHexStringConverterInstance().stringToHex(if (paddedTargetData.length % 2 != 0) "0$paddedTargetData" else paddedTargetData)//if ODD
val padded = targetDataInHex + RFID_ADDED_VALUE
writeAccessParams.setWriteData(padded)
writeAccessParams.writeRetries = 1
writeAccessParams.writeDataLength = padded.length / 4 // WORD EQUALS 4 HEX
reader!!.Actions.TagAccess.writeWait(sourceEPC, writeAccessParams, null, tagData)
return tagData
} catch (e: InvalidUsageException) {
errorMessage = "InvalidUsageException=" + e.vendorMessage + " " + e.info
println(errorMessage)
return null
} catch (e: OperationFailureException) {
errorMessage = "InvalidUsageException=" + e.vendorMessage + " " + e.results
println(errorMessage)
return null
} catch (e: UnsupportedEncodingException) {
errorMessage = if (e.message == null) "" else e.message!!
println(errorMessage)
return null
}
}
Read Full Tag method:
fun readFullTag(sourceEPC: String): TagData? {
errorMessage = ""
try {
val tagAccess = TagAccess()
val readAccessParams = tagAccess.ReadAccessParams()
readAccessParams.accessPassword = 0
readAccessParams.memoryBank = MEMORY_BANK.MEMORY_BANK_TID
readAccessParams.offset = 0
return reader?.Actions?.TagAccess?.readWait(sourceEPC, readAccessParams, null, false)
} catch (e: InvalidUsageException) {
errorMessage = "InvalidUsageException=" + e.vendorMessage + " " + e.info
println(errorMessage)
return null
} catch (e: OperationFailureException) {
errorMessage = "InvalidUsageException=" + e.vendorMessage + " " + e.results
println(errorMessage)
return null
}
}
Handle Tag Data method:
override fun handleTagData(tagData: Array<TagData?>?) {
var readValue = ""
if (!tagData.isNullOrEmpty()) readValue = tagData[0]!!.tagID.trimIndent().replace("\n", "")
if (isWritingRFID) {
isWritingRFID = false
if (currentRFIDAssetCode.isNotEmpty())
writeRFID(readValue, currentRFIDAssetCode)
} else {
CoroutineScope(Dispatchers.Main).launch {
if (!pDialog.isShowing) readFullRFID(readValue)
}
}
}
Feel free to ask for any additional code or info.
Another way to solve this problem instead of padding with zeros is use a technique used a lot in NFC and the first byte of your data is the value of the length of the data (in Hex).
Therefore it does not matter if the old data is not zero'd out and you won't have a problem detecting if it is a zero for blanking or a real zero
e.g.
0A 31 32 33 34 35 36 37 38 39 30
or in text
10 1 2 3 4 5 6 7 8 9 0
would be overwritten with
03 41 42 43
or in text
3 A B C
resulting in text in memory of
3 A B C 4 5 6 7 8 9 0
But you would read the first byte to get the length of 3 and then read 3 more bytes to get in text
A B C
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.
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 am using Android PDF Write(APW) to create a PDF, but it doesn't work with some special characters(portuguese).
mypdf.addText(170, 50, 40,"Coração");
The standard enconding is:
mypdf.setFont(StandardFonts.SUBTYPE, StandardFonts.COURIER, StandardFonts.WIN_ANSI_ENCODING);
outputToFile("helloworld.pdf",pdfcontent,"ISO-8859-1");
I'v tried
outputToFile("helloworld.pdf",pdfcontent,"UTF-8");
outputToFile("helloworld.pdf",pdfcontent,"UTF-16");
outputToFile("helloworld.pdf",pdfcontent,"Cp1252");
and didn't succeed.
Any ideas what should I do?
EDIT
The method outputToFile is defined as:
private void outputToFile(String fileName, String pdfContent, String encoding) {
File newFile = new File(Environment.getExternalStorageDirectory() + "/" + fileName);
try {
newFile.createNewFile();
try {
FileOutputStream pdfFile = new FileOutputStream(newFile);
pdfFile.write(pdfContent.getBytes(encoding));
pdfFile.close();
} catch(FileNotFoundException e) {
//
}
} catch(IOException e) {
//
}
}
The method addText is defined as:
public void addText(int leftPosition, int topPositionFromBottom, int fontSize, String text, String transformation) {
addContent(
"BT\n" +
transformation + " " + Integer.toString(leftPosition) + " " + Integer.toString(topPositionFromBottom) + " Tm\n" +
"/F" + Integer.toString(mPageFonts.size()) + " " + Integer.toString(fontSize) + " Tf\n" +
"(" + text + ") Tj\n" +
"ET\n"
);
}
Besides, I change the font color to white adding the following rawcontent:
mypdf.addRawContent("1 1 1 rg\n");
Then I come back to the black font color:
mypdf.addRawContent("0 0 0 rg\n");
I took all the information provided, wrote the following simple unit test method and ran it.
public void test19192108()
{
PDFWriter mPDFWriter = new PDFWriter(PaperSize.FOLIO_WIDTH, PaperSize.FOLIO_HEIGHT);
mPDFWriter.setFont(StandardFonts.SUBTYPE, StandardFonts.COURIER, StandardFonts.WIN_ANSI_ENCODING);
mPDFWriter.addText(170, 50, 40,"Coração");
String pdfcontent = mPDFWriter.asString();
outputToFile("helloworld19192108.pdf",pdfcontent,"ISO-8859-1");
}
(outputToFilebeing the helper method from the APW PDFWriterDemo class)
The result looks like this:
This seems pretty much to fulfill the expectations.
Thus, in whichever way it doesn't work with some special characters(portuguese) for the OP, some vital information is missing for reproducing the issue.
PS: Depending on the setup of the development environment, there might be an issue with non-ASCII characters in the source code. Thus, it might be a good idea to replace
mPDFWriter.addText(170, 50, 40,"Coração");
with
mPDFWriter.addText(170, 50, 40,"Cora\u00e7\u00e3o");
PPS: Adobe Reader after viewing a file generated like this wants to repair it. The reason is that the cross reference table is broken. The code generating entries for it is this:
public void addObjectXRefInfo(int ByteOffset, int Generation, boolean InUse) {
StringBuilder sb = new StringBuilder();
sb.append(String.format("%010d", ByteOffset));
sb.append(" ");
sb.append(String.format("%05d", Generation));
if (InUse) {
sb.append(" n ");
} else {
sb.append(" f ");
}
sb.append("\r\n");
mList.add(sb.toString());
}
(from CrossReferenceTable.java)
Counting the characters in this entry we get 10 + 1 + 5 + 3 + 2 = 21.
According to the specification, though:
Each entry shall be exactly 20 bytes long, including the end-of-line marker
(from section 7.5.4 Cross-Reference Table of ISO 32000-1)
When using (the current version of) the Android PDF Writer, you should fix this code, too.