I've implemented a notification listener to look out for a Gmail notification.
I want to collect the expanded text (bigtext) from the notification as shown in the notification below:
See "----Forwarded message---", etc. which only appears when the user expands the notification to show the action buttons.
This string value does not appear in the notification's "EXTRAS" data...
http://developer.android.com/reference/android/app/Notification.html
After viewing the above link, I further investigate the bundle (EXTRAS) data. When you debug it and look at the variable, you can find that all information regards the notification are stored in the bundle and you can get the detail by
notifyBundle.extras.getCharSequence("android.textLines") for multi line notification and
notifyBundle.extras.getString("android.text") for single line notification.
For more information, look at the variable in eclipse debugging mode
Image of variables in bundle for single line notification
Image of variables in bundle for multi line notification
Note: The extra bundle only available in API19 (Kitkat). I've never tried in device which lower than API19.
I ran into a similar problem with Gmail running on Android 7.
Eventually, I've realized (with some help from another thread) that he solution was different from the existing answers here - what you're looking for can be accessed in a different way:
Bundle extras = statusBarNotification.getNotification().extras;
extras.get(Notification.EXTRA_BIG_TEXT) == null ? null : extras.get(Notification.EXTRA_BIG_TEXT).toString();
This way you will always get either a String or null, even if the value you're looking for isn't originally a string. This is done because calling getString(Notification.EXTRA_BIG_TEXT) directly would return null instead, at least in some cases.
If there are any other values you're not sure where they might be stored, you can try iterating through the entire extras bundle, as explained here.
Finally, I am able to solve this issue by using this code.
ArrayList<StatusBarNotification> groupedNotifications = new ArrayList<>();
for(StatusBarNotification statusBarNotification : mNotificationManager.getActiveNotifications()) {
if(statusBarNotification.getNotification().getGroup().equals(NOTIFICATION_GROUP)) {
groupedNotifications.add(statusBarNotification);
}
}
CharSequence stackNotificationMultiLineText[] = groupedNotifications.get(ZEROTH_INDEX).getNotification().extras.getCharSequenceArray(NotificationCompat.EXTRA_TEXT_LINES);
If you try to use getCharSequence("android.textLines"), it returns you null because NotificationCompat.EXTRA_TEXT_LINES returns you the array of CharSequence and not a single CharSequence object.
I found a shorter way of accessing the expanded notification. This works in API 24.
If you are getting null while accessing getCharSequence("android.textLines"), this is because it actually returns an array of CharSequence as rightly pointed out by Puneet above.
So rather access them like this:
if (extras.getCharSequenceArray("android.textLines") != null)
text = Arrays.toString(extras.getCharSequenceArray("android.textLines"));
Related
This will be a self-answered question, because I'd like to clearly document how to determine if a search intent was triggered by text input or voice recognition. My reason for needing this was because I was trying to log searches in my app to Google Analytics, and I needed to know whether the user was typing their searches in on the keyboard as text, or if they were using the voice search feature.
I found a few questions on StackOverflow that addressed this question, but I found them to be hard to find and poorly documented. So hopefully my answer will provide the right keywords and details to help others find this topic more quickly and clearly in the future.
The search Activity in my app is running in singleTop mode, and so I'm handling incoming search intents through an #Override of onNewIntent(). Within this method, the first thing you need to do is check to see if the incoming intent is, in fact, a search intent. We accomplish this in the following way:
if (intent.getAction().equals(Intent.ACTION_SEARCH)){}
The next part is a little tricky, and has caused me some false positives on older APIs. There are two extras that come attached to the ACTION_SEARCH intent: QUERY and USER_QUERY. If we read about these two values in the SearchManager docs, we find the following info:
public static final String QUERY:
Intent extra data key: Use this key with
content.Intent.getStringExtra() to obtain the query string from
Intent.ACTION_SEARCH.
public static final String USER_QUERY:
Intent extra data key: Use this key with
content.Intent.getStringExtra() to obtain the query string typed in
by the user. This may be different from the value of QUERY if the
intent is the result of selecting a suggestion. In that case, QUERY
will contain the value of SUGGEST_COLUMN_QUERY for the suggestion,
and USER_QUERY will contain the string typed by the user.
Essentially what we can gather from these two doc entries is that the value of QUERY will always contain the actual value of the search, regardless of how the search was executed. So it doesn't matter if the user typed in the search, spoke it in with voice recognition, or selected a search suggestion -- this field will contain the raw String value of what they were searching for.
On the other hand, USER_QUERY may or may not be null, depending on how the search was executed. If the user types in their search using text, this field will contain the value of their search. If the user executes the search with voice recognition, this value will be null.
This gives us a great (albeit hacky, to some extent) way to determine if the user typed in the search with text or used a voice search feature. But here is the tricky part: as you can see in the documentation above, it is suggested to use getStringExtra() to retrieve this value from the intent. This is not reliable, because the value of USER_QUERY is actually a SpannableString, not a String. This will likely lead to a ClassCastException, because if you use getStringExtra(), you are trying to cast a SpannableString to a String, which will not work. The exception will be automatically caught for you, but you may receive a false-positive null back as the result, leading your code to believe that the search was executed through voice recognition, when in reality the typed-in text value simply got lost because of the ClassCastException.
So the solution is to instead work with the SpannableString coming from USER_QUERY, and check for a null on that instead so you don't trigger the ClassCastException and the false-positive null. But you'll notice that Intent doesn't have a method to get SpannableStrings, but it does have a getCharSequenceExtra() method. Since SpannableString implements CharSequence, we can simply use this method to retrieve our USER_QUERY SpannableString, and cast it on the way out. So we'll do that like this:
SpannableString user_query_spannable = (SpannableString) intent.getCharSequenceExtra(SearchManager.USER_QUERY);
We can now check for a null on this value to safely determine if the user typed in text or executed the search in some other way such as voice recognition.
Putting it all together, we end up with something like this:
#Override
protected void onNewIntent(Intent intent) {
if (intent.getAction().equals(Intent.ACTION_SEARCH)) {
SpannableString user_query_spannable = (SpannableString) intent.getCharSequenceExtra(SearchManager.USER_QUERY);
//if this was a voice search
if (user_query_spannable == null) {
//Log, send Analytics hit, etc.
//Then perform search as normal...
performSearch(intent.getStringExtra(SearchManager.QUERY));
} else {
//Log, send Analytics hit, etc.
//Then perform search as normal
performSearch(intent.getStringExtra(SearchManager.QUERY));
}
}
}
As I mentioned earlier, for me this information was not easily found through searches on Google and on StackOverflow, especially when the official Android documentation provides misleading information on how to work with these values. Hopefully, this answer will provide a much more easily accessbile source for others looking for similar info.
Bundle[{android.speech.extra.RESULTS=[hello], query=hello, android.speech.extra.CONFIDENCE_SCORES=[0.9299549]}]
THIS WILL BE IN THE BUNDLE FOR VOICE
My program consists of a MainActivity and two fragment activities. I need one fragment to take a String value from the user and pass it to the second fragment.
I am trying to wrap my head around how to do this. Since I am familiar with intents, I found this answer on another post and decided to try it out. Everything looks fine until I get to step 4, when I try to use Intent i = getIntent(); in my second fragment, Studio won't let me use it and says "getIntent(java.lang.String) is deprecated".
This doesn't make sense to me since I have used getIntent() in other programs without issue, and it is letting me use it in my MainActivity (step 2 from the other post) without screaming at me.
I know this can be done without using intents, but I can't figure it out and can't find any really thorough tutorials in order to do so. So I guess my questions are:
Can I make intents work for this purpose still? What should I do to get around this deprecation issue?
Any other advice, explanations, or links to "explain it like I'm 5" tutorials would be very helpful and welcome. I have Googled and read a few, but I am still not understanding this and am becoming increasingly frustrated. It seems like this should be a relatively simple concept.
It is too late for answer but still I am providing my answer for other persons. It is happen because Intent is basically work with an activity. And fragments are not activity, but attached to activity. So simply you need to do this:
Intent intent=getActivity().getIntent();
Having the same problem while passing Object from an Activity to a Java Class.
Here is what I did
This Activity sends data
Salary newSalary = new Salary();
Intent intent = new Intent(ViewData.this,Data.class);
intent.putExtra("SalaryObj", newSalary);
It recieves data(In Data.class)
Here I tried this but Android Studio says getIntent is deprecatedIntent intent = Intent.getIntent();
So What can I use in place of getIntent(), because all the solutions I find on Internet to this problem, uses getIntent().
EDIT:
I was playing around with this and found that I was trying to receive data in Java Class(Not an Activity). But when I used it in an Activity then it works fine. But it takes me to another question that how to send data from an Activity to Java Class(which is not an Activity).
On your onCreate
val bundle = intent.extras
if (bundle != null) {
idEmployee = bundle?.getString("idEmployee", "")
idClient = bundle?.getString("idClient", "")
listAvailable = bundle?.getStringArrayList("listAvailable") as ArrayList<String>
Log.i("list:", "$listAvailable" )
}
It means, that this method could not be supported in further releases. The method is still in the API for backward compability for an unspecified amount of time. Mostly it is dangerous to use deprecated methods or there is a better way to achieve this.
Like it is described here
In this case you should rather use: parseUri(String, int) to achieve this ( according to the android developer api).
I am writing NotificationListenerService ,
where I want to get the details of notification sent to status bar.
But only thing we get is Ticket text , which is null in some cases.
Correct, ticker text is not a required field when building a notification. In fact, the only required notification contents are:
A small icon - returned by icon
A title - returned by extras.getCharSequence(Notification.EXTRA_TITLE)
Detail text - returned by extras.getCharSequence(Notification.EXTRA_TEXT)
Unfortunately, the extras bundle is only available on Android 4.4 (KitKat) devices - previous versions do not have any easy access to this data - you only have access to the RemoteViews which you'd need to inflate and parse manually (definitely not recommended).
Ticker text is optional, and may be null in some cases.
You haven't posted any code, but are you using the onNotificationPosted(StatusBarNotification) method from NotificationListenerService? That was implemented in API 18 (Android 4.3), so it should work for you. The documentation states that the full Notification object should be returned, which should give you more than just the ticker text.
I want to sent value to another activity. I'm using VS 2012 C# for developing apps. I have done lot of search from google. I have tried almost every methods. in my application getIntent(); giving error. that is, getIntent() does not exist in the current context.
I'm also not getting these below functions in my application.
Intent sender=getIntent();
getApplicationContext();
I have added all references. Help me where Am I wrong?
I got answer, I have to use below line of for getting value:
string extraData = Intent.GetStringExtra("KeyName");
my app searches for new articles and sends a notification like "5 new articles". However when i send another one, i want to have it update the text to lets say there were 3 new so something like "8 new articles" BUT "3 new articles" if the user has dismissed that previous notification. I hope you get it.
Is there a way to know that notification was dismissed so i can reset the count?
Thanks !
This is a rather belated answer but I was looking to find out how to do this myself so perhaps it'll be useful to others. In API level 18 the following service was introduced which should make this straightfoward as you can now get a callback whenever a notification is added or removed:
https://developer.android.com/reference/android/service/notification/NotificationListenerService.html
In particular for the original question see the onNotificationRemoved method
The other option mentioned by jpop above is to use the builder's setDeleteIntent method when creating the notification, which will give a callback when the notification is deleted. This would then require you to maintain the state of the existing raised notifications somewhere else as it only tells you when something is added, not removed.
If your app has a database you can do this: Create a table that has some fields like id as int, article name as string, and isdismissed as boolean. So, each time you want to send a notification, you should count the records that the isdismissed field equals false.
In the other hand, each time user select a notification, the related isdismissed field must be equal true.
In addition, this sample from developer.android.com maybe can help you:
http://developer.android.com/guide/topics/ui/notifiers/notifications.html
the listener don't work since android Nougat, a new access rule have been added
on android device : parameters -> applications -> gear menu -> spécial access -> notifications access
use : catch on swipe to dismiss event