Extract Notification Text from Parcelable, Contentview or Contentintent

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



Leave a reply



Submit