Extract notification text from parcelable, contentView or contentIntent
I've wasted a few hours of the last days figuring out a way to do what you (and, me too, by the way) want to do. After looking through the whole source of RemoteViews twice, I figured the only way to accomplish this task is good old, ugly and hacky Java Reflections.
Here it is:
Notification notification = (Notification) event.getParcelableData();
RemoteViews views = notification.contentView;
Class secretClass = views.getClass();
try {
Map<Integer, String> text = new HashMap<Integer, String>();
Field outerFields[] = secretClass.getDeclaredFields();
for (int i = 0; i < outerFields.length; i++) {
if (!outerFields[i].getName().equals("mActions")) continue;
outerFields[i].setAccessible(true);
ArrayList<Object> actions = (ArrayList<Object>) outerFields[i]
.get(views);
for (Object action : actions) {
Field innerFields[] = action.getClass().getDeclaredFields();
Object value = null;
Integer type = null;
Integer viewId = null;
for (Field field : innerFields) {
field.setAccessible(true);
if (field.getName().equals("value")) {
value = field.get(action);
} else if (field.getName().equals("type")) {
type = field.getInt(action);
} else if (field.getName().equals("viewId")) {
viewId = field.getInt(action);
}
}
if (type == 9 || type == 10) {
text.put(viewId, value.toString());
}
}
System.out.println("title is: " + text.get(16908310));
System.out.println("info is: " + text.get(16909082));
System.out.println("text is: " + text.get(16908358));
}
} catch (Exception e) {
e.printStackTrace();
}
This code worked fine on a Nexus S with Android 4.0.3. However, I didn't test if it works on other versions of Android. It's very likely that some values, especially the viewId changed. I think there should be ways to support all versions of Android without hard-coding all possible ids, but that's the answer to another question... ;)
PS: The value you're looking for (referring to as "(3)" in your original question) is the "text"-value.
Read content from notification parcelable objects for consequent notification
Yes, finally after few hours of googling I design a code which does work for me.
Bundle extras = sbn.getNotification().extras;
CharSequence[] lines = extras.getCharSequenceArray(Notification.EXTRA_TEXT_LINES);
JSONArray s = new JSONArray();
for (CharSequence msg : lines) {
msg = removeSpaces(msg);
if (!TextUtils.isEmpty(msg)) {
s.put(msg.toString());
}
}
private static String removeSpaces(@Nullable CharSequence cs) {
if (cs == null)
return null;
String string = cs instanceof String ? (String) cs : cs.toString();
return string.replaceAll("(\\s+$|^\\s+)", "").replaceAll("\n+", "\n");
}
here JSONArray s contains all messages that I want
Retrieve text from a RemoteViews Object
Taken from Extract notification text from parcelable, contentView or contentIntent :
Notification notification = (Notification) event.getParcelableData();
RemoteViews views = notification.contentView;
Class secretClass = views.getClass();
try {
Map<Integer, String> text = new HashMap<Integer, String>();
Field outerFields[] = secretClass.getDeclaredFields();
for (int i = 0; i < outerFields.length; i++) {
if (!outerFields[i].getName().equals("mActions")) continue;
outerFields[i].setAccessible(true);
ArrayList<Object> actions = (ArrayList<Object>) outerFields[i]
.get(views);
for (Object action : actions) {
Field innerFields[] = action.getClass().getDeclaredFields();
Object value = null;
Integer type = null;
Integer viewId = null;
for (Field field : innerFields) {
field.setAccessible(true);
if (field.getName().equals("value")) {
value = field.get(action);
} else if (field.getName().equals("type")) {
type = field.getInt(action);
} else if (field.getName().equals("viewId")) {
viewId = field.getInt(action);
}
}
if (type == 9 || type == 10) {
text.put(viewId, value.toString());
}
}
System.out.println("title is: " + text.get(16908310));
System.out.println("info is: " + text.get(16909082));
System.out.println("text is: " + text.get(16908358));
}
} catch (Exception e) {
e.printStackTrace();
}
Getting Data from Notification
Ok, I fount simple way to do it, but it is available on android 19+ (KitKat 4.4.2):
If you want for example title of Notification, you can use something like this:
String title = sbn.getNotification().extras.getString(Notification.EXTRA_TITLE);
There is more information in extras, like text, prgress, time etc. - details here
sbn object is StatusBarNotification class object (the way how to reach this you can find in this tutorial
If you need to do it on earlier version of Android you have to try any answer from this topic.
PendingIntent from second action overwrites the first action and the contentIntent for Notification
From the doc:
Because of this behavior, it is important to know when two Intents are considered to be the same for purposes of retrieving a PendingIntent. A common mistake people make is to create multiple PendingIntent objects with Intents that only vary in their "extra" contents, expecting to get a different PendingIntent each time. This does not happen. The parts of the Intent that are used for matching are the same ones defined by Intent.filterEquals. If you use two Intent objects that are equivalent as per Intent.filterEquals, then you will get the same PendingIntent for both of them.
So you have to make those intents different as in Intent.filterEquals
method:
That is, if their action, data, type, class, and categories are the same. This does not compare any extra data included in the intents.
As a side note, I had the same problem while working on the download notifications of firefox for android here
Facebook messenger accessibility
You can try this to see if it works with Facebook messenger notifications. And even if this does work, I would suggest that you wait for a better solution.
From API 19 and above, Notification
objects carry bundled extras
- the inputs passed to Notification.Builder
when the Notification
was first created. So, information such as the title
, context
, summary
etc. can be extracted from this Bundle
using keys of form Notification.EXTRAS_XXXX
. The keys can be found here: Link.
In the overriden onAccessibilityEvent(AccessibilityEvent event)
method:
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
Parcelable data = event.getParcelableData();
if (data != null && data instanceof Notification) {
Log.i("", "We have a notification to parse");
Notification notification = (Notification) data;
// For API 19 and above, `Notifications` carry an `extras` bundle with them
// From this bundle, you can extract info such as:
// `EXTRA_TITLE` - as supplied to setContentTitle(CharSequence)
// `EXTRA_TEXT ` - as supplied to setContentText(CharSequence)
// `EXTRA_INFO_TEXT` - as supplied to setContentInfo(CharSequence)
// ... more at: http://developer.android.com/reference/android/app/Notification.html
Bundle b = noti.extras;
Log.i("Notification", "Title: " + b.get(Notification.EXTRA_TITLE));
Log.i("Notification", "Text: " + b.get(Notification.EXTRA_TEXT));
Log.i("Notification", "Info Text: " + b.get(Notification.EXTRA_INFO_TEXT));
/////////////////////////////////////////////////////////////////
// For API 18 and under:
// Pass `notification` to a method that parses a Notification object - See link below
List<String> notificationText = extractTextFromNotification(notification);
....
....
}
}
extractTextFromNotification(Notification)
can be a method from here: Link. Needless to say that this is a workaround, and will require quite a bit of testing to ensure that it works as required.
How to add an element to the pull down notification bar of an existing app?
That's quite an undertaking.
So the link you provided has a bit of a walkthrough in its description about where to start. The trick is that because you're trying to program a notification service for an EXISTING app, you don't really have control over the notifications the app itself creates. I suspect what you're going to have to do is program a NotificationListenerService, listen for gmail notifications, and somehow cancel the gmail notification and replace it with one of your own, as created through the android documentation.
For a good example of how NotificationListenerService works please take a look at this:
https://github.com/kpbird/NotificationListenerService-Example
The canceling is something I have not tested but you asked for ideas, not code. NotificationListenerService has a method cancelNotification(String pkg, String tag, int id)
which looks like you can use to cancel the gmail notification.
Related Topics
Android: Velocity-Based Viewpager Scrolling
Trying to Fix Networkonmainthreadexception But Gives Toast Error
Using Katzer Local Notification in Ibm Worklight
How to Develop Android App Completely Using Python
How to Access Adb in Os X Through Terminal, "Command Not Found"
Get Android .Apk File Versionname or Versioncode Without Installing APK
How to Show a Preview of a Recyclerview's Contents in The Android Studio Editor
How to Bring a Background Task to The Front Below Honeycomb
Read Logs from All Apps on Android from Within an App for Android 4.2+
Android Webview Jellybean -> Should Not Happen: No Rect-Based-Test Nodes Found
Using Asynctask to Speed Up Android App Launch Time
Turning on Wifi Using Wifimanager Stops to Work on Android 10
How to Pass Image Data from One Activity to Another Activity
Android M Write to Sd Card - Permission Denied
Phonegap & Android - How to Use The New Select List Style
Android Studio on a Hardware That Does Not Support Virtualization Technology
Android/Java: How to Delay Return in a Method
Import .R Cannot Be Resolved When I Import Actionbarsherlock