I am attempting to raise a notification that a message has arrived. I have added an action expecting an icon (smallredball) to show on the notification. I expect that if the user hits smallredball, main activity will startup and the activity, checking the extras bundle, will see the orders to do something different than if it were just started up normally.
The notification shows on the target phone (running KitKat) along with the text but the smallredball icon never shows. When the user touches the notification the activity executes with no extra. EDIT: THE ACTIVITY IS NOW GETTING THE EXTRA BUNDLE.
This is the code sending the notification:
private void raiseNotification( String username, String mesText)
{
DebugLog.debugLog("GCMIntentService: Attempting to Raise Notification ", false);
NotificationCompat.Builder b = new NotificationCompat.Builder(this);
Intent intent = new Intent(this, MainActivity.class);
intent.putExtra("whattodo", "showmessage");
intent.setAction(Long.toString(System.currentTimeMillis())); //just to make it unique from the next one
PendingIntent pIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
b.setContentTitle("New SafeTalk Message")
.setSmallIcon(R.drawable.note24x24)
.setContentText("From " + username + " " + mesText)
.setTicker("New SafeTalk Message")
.setContentIntent(pIntent)
.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION))
.setAutoCancel(true)
.addAction(R.drawable.smallredball, "Read Now", pIntent);
NotificationManager mgr = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
mgr.notify(0, b.build());
}
this is a code snippet from the activity:
Bundle extras = getIntent().getExtras();
if (extras == null)
{
GlobalStuff.mpBad.start();
}
else
{
String myOrders = extras.getString("whattodo");
if (myOrders.equals("showmessage"))
GlobalStuff.mpBeep.start();
}
Why isn't the icon showing in the notification? Since I setAutoCancel to true, I expected that simply touching the notification would make it just go away. But instead it runs the app providing no extra bundle?
Thanks,
Dean
This topic is covered in an existing question
Since the points that will solve this problem and similar problems I have had, are spread around a bit in that topic, here is my two point cheat sheet:
Point 1: use code like the following to create a pending intent. The choice of flags in the last argument is important:
PendingIntent pIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
Point 2: pending intents are stored in a global system table, and only certain parts of the intent they are created from are part of the "key" that is used to look things up in this table. Extras are not part of the key, so if you want two intents to map to two different pending intents, make sure they are different in some other way, for example having different actions, data, or types.
This example changes the action:
Intent intent = new Intent(this, MainActivity.class);
intent.putExtra("whattodo", "showmessage");
// add this:
intent.setAction("showmessage");
(The action can be anything as long as it is different than what you use with the same class elsewhere.
)
There is a good explanation in the latest version of the Javadoc for pending intents., especially this quote I pulled out:
... 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.
Related
I am able to generate and display multiple notifications, all starting the same activity but displaying different information depending on the extra that is attached to the intent that starts up the Activity. When I am within my application, all the notifications are able to start the Activity, but when I am outside of the application, only one is able to start the Activity (the first that I click on), the rest do nothing once clicked on (except disappear). This is my code for generating the notifications. The "reqCode" represents unique numbers.
Resources r = getResources();
Intent i = new Intent(this, DisplaySearchTickerAlert_Activity.class);
i.putExtra(NewArticleNotification_Service.QUERY_TICKER, searchQuery);
PendingIntent pi = PendingIntent.getActivity(this, reqCode, i, 0);
Notification notification = new NotificationCompat.Builder(this)
.setTicker(r.getString(R.string.ticker_alert))
.setSmallIcon(android.R.drawable.ic_menu_call)
.setContentTitle(r.getString(R.string.ticker_alert))
.setContentText("Newly published for search term: " + "\""
+ searchQuery + "\"").setContentIntent(pi).setAutoCancel(true).build();
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
notificationManager.notify(reqCode, notification);
That is because your activity is already being displayed. Only the first notification will bring it to the foreground. The rest of notifications will dispatch a new intent that depending on your activity android:launchMode you might be able to catch on the onNewIntent callback.
My question is a bit complicated so I am going to describe it briefly what I want to achieve. My application receives messages from GCM without any issue. When application is running and is visible to the user I am not displaying notification in Action Bar, I am displaying this message in dialog alert. I use BroadcastReceiver to achieve this.
The main issue is that, when app gets a few notifications in no time. When main activity is visible to the user, the first notification will be shown in dialog alert. And next will be placed in Action Bar as usual Android notifications. And at this moment user disposes the dialog alert and is going to choose a next notification from Action Bar. At this moment I am using in my CloudService (extends IntentService) intent with flag Intent.FLAG_ACTIVITY_CLEAR_TOP and also PendingIntent.FLAG_UPDATE_CURRENT flag for pendingIntent. PendingIntent is just the contentIntent for my NotificationCompat.Builder.
It works in this way that for each user click on the notification from Action Bar, my activity is refreshing (floats to the lower device edge and then floats from the upper edge with dialog alert with message - I am getting the extras from the intent in onResume method). This action is quite OK. Activity has only one instance in that case - I don't have to break through the few instances of the same activity while I have opened few notifications. But the big problem is that when user chooses any of the notifications in each case the last one will open in dialog alert. Activity seems to has the same intent despite of PendingIntent.FLAG_UPDATE_CURRENT.
There are two methods in CloudService which handle cloud messages:
private void showNotification(Bundle extras) {
notificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
String message = extras.getString(CloudMetaData.MESSAGE);
if (App.isMyActivityVisible() && !CloudMessageDialogFragment.isAttached()) {
sendBroadcast(message, NOTIFICATION_ID);
} else {
Intent intent = new Intent(this, MyParentActivity.class);
intent.putExtra(CloudMetaData.MESSAGE, message);
intent.putExtra(CloudMetaData.NOTIFICATION_ID, NOTIFICATION_ID);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setSmallIcon(R.drawable.ic_launcher);
builder.setContentTitle(getString(R.string.app_name));
builder.setStyle(new NotificationCompat.BigTextStyle().bigText(alert));
builder.setContentText(alert);
builder.setAutoCancel(true);
builder.setDefaults(Notification.DEFAULT_SOUND);
builder.setContentIntent(contentIntent);
notificationManager.notify(NOTIFICATION_ID, builder.build());
}
NOTIFICATION_ID++;
}
And method which send broadcast is just like that:
private void sendBroadcast(String message, int notificationId) {
Intent intent = new Intent();
intent.setAction(ACTION_FROM_CLOUD_SERVICE);
intent.putExtra(CloudMetaData.MESSAGE, message);
intent.putExtra(CloudMetaData.NOTIFICATION_ID, notificationId);
sendBroadcast(intent);
}
In which way can I achieve similar solution? But it is important for user of course to open notification from Action Bar and shown him its correct message.
EDIT
I have switched my dialog alert into the dialog activity. This is its definition in AndroidManifest:
<activity
android:name="com.myapp.activity.CloudMessageDialogActivity"
android:theme="#android:style/Theme.Dialog"
android:parentActivityName="com.myapp.activity.MyParentActivity"/>
But the result is still the same - when app receives a few notifications, clicking on one of them will open the dialog with the intent for the last received notification.
But the big problem is that when user chooses any of the notifications in each case the last one will open in dialog alert. Activity seems to has the same intent despite of PendingIntent.FLAG_UPDATE_CURRENT.
That's because you are using the same request code (i.e., 0) for all PendingIntents here:
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
By doing so, you are ultimately receiving the same intent, because the platform is failing to see the difference between the intents and delivering the same object everytime:
... 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, instead of a constant value, please use a unique request code. For e.g. change the above line with:
PendingIntent contentIntent = PendingIntent.getActivity(this, NOTIFICATION_ID,
intent, PendingIntent.FLAG_UPDATE_CURRENT);
Hope this helps.
I want to create several notifications that launches an activity (or refresh it) to display a product description.
Notification notification = new Notification(R.drawable.applicationicon,
Resources.getString("NewSaleNotification", context),
System.currentTimeMillis());
// Hide the notification after its selected
notification.flags |= Notification.FLAG_AUTO_CANCEL;
Intent intent = new Intent(context, MainApplication.class);
intent.putExtra("saleid", saleid);
// to be sure the activity won't be restarted
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
notification.setLatestEventInfo(context, SaleTitle, SaleMessage, pendingIntent);
notificationManager.notify(saleid, notification);
When I create the PendingIntent, I have 4 choices : FLAG_CANCEL_CURRENT, FLAG_NO_CREATE, FLAG_ONE_SHOT and FLAG_UPDATE_CURRENT.
The definition of the last one (http://developer.android.com/reference/android/app/PendingIntent.html#FLAG_UPDATE_CURRENT) is what I want to do but it doesn't work as it should. If I create 2 notifications, they both have the same 'saleid' extra which is the latest one. How can I make more than one notification with different 'saleid' extra?
but it doesn't work as it should
Yes, it does.
If I create 2 notifications, they both have the same 'saleid' extra which is the latest one.
This is precisely what the documentation says is supposed to happen.
How can I make more than one notification with differents 'saleid' extra?
Option #1: Put a different action string in each of your Intents. This will make them different (from the standpoint of filterEquals()) and give them separate PendingIntents. However, since you are specifying the component in the Intent (MainApplication.class), the action will not affect how the Intent is routed.
Option #2: Use a different requestCode (2nd parameter) on your getActivity() calls. While this is documented as "currently not used", it does result in different PendingIntent objects being returned. However, since this behavior is undocumented, it may change in the future.
A few days ago I was struggling to find a way to use custom intents for my alarms. Although I got clear answer that I have to customize the Intents based on some unique ID eg. setAction() still have some problems.
I define a PendingIntent this way:
Intent intent = new Intent(this, viewContactQuick.class);
intent.setAction("newmessage"+objContact.getId());//unique per contact
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK ).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP );
intent.putExtra("id", Long.parseLong(objContact.getId()));
intent.putExtra("results", result.toArray());
PendingIntent contentIntent = PendingIntent.getActivity(context, 0, intent, 0);
then this is used by a notification manager
NotificationManager mNotificationManager = (NotificationManager) context.getSystemService(ns);
// first try to clear any active notification with this contact ID
mNotificationManager.cancel(Integer.parseInt(objContact.getId()));
// then raise a new notification for this contact ID
mNotificationManager.notify(Integer.parseInt(objContact.getId()), notification);
This works like this:
application creates a message for a contact
an intent is provided with the contact id and details about the message
notification is raised with the message
user actiones on the notification and the app displays the message passed by the intent
The problem
This can happen more than once for a contact. And when the second message is generated, the notification is raised well (message is fine there) but the intent when the user actions the notification it uses old data, so previous message is passed and not the brand new message.
So someway the intent is caching and reusing previous extras. How can I make it unique per contact and per action?
If only one of your PendingIntents for this contact will be outstanding at any point in time, or if you always want to use the latest set of extras, use FLAG_UPDATE_CURRENT when you create the PendingIntent.
If more than one contact-specific PendingIntent will be outstanding at once, and they need to have separate extras, you will need to add a count or timestamp or something to distinguish them.
intent.setAction("actionstring" + System.currentTimeMillis());
UPDATE
Also, the lightly-documented second parameter to getActivity() and kin on PendingIntent apparently can be used to create distinct PendingIntent objects for the same underlying Intent, though I have never tried this.
I usually specify unique requestCode to prevent my PendingIntents from overriding each other:
PendingIntent pending = PendingIntent.getService(context, unique_id, intent, 0);
And in your case I agree with CommonsWare you just need FLAG_UPDATE_CURRENT flag. New extras will override old values.
I have a alarm thing going on in my app and it launches a notification that then when pressed launched an activity.
The problem is that when I create more than one alarm then the activity launched from the notification gets the same extras as the first one. I think the problem is either with the intent i put in the pending intent or in the pending intent itself. I think I might need to put a flag on one of these but I dont know which one.
Intent showIntent =new Intent(context, notificationreceiver.class);
showIntent.putExtra("details", alarmname);
PendingIntent contentIntent = PendingIntent.getActivity(context, 0,
showIntent, 0);
notification.setLatestEventInfo(context, "The event is imminent",
alarmname, contentIntent);
And the receiver of the notification
Bundle b = getIntent().getExtras();
String eventname = b.getString("details");
details.setText(eventname);
The "details" extra is the same to every the next time a notification happens instead of having the different value.
Until I set the intents I am sure that the correct value goes to the "details" so its a problem of getting the first intent everytime i press any notification.
How can I make it to launch the correct intents?
Hope I was as clear as i could
Thanks!
The way I solved that problem was by assigning a unique requestCode when you get the PendingIntent:
PendingIntent.getActivity(context, requestCode, showIntent, 0);
By doing so you are registering with the system different/unique intent instances.
Tip: A good way of making the requestCode unique would be by passing to it the current system time.
int requestID = (int) System.currentTimeMillis();
The problem is that when I create more
than one alarm then the activity
launched from the notification gets
the same extras as the first one.
Correct.
How can I make it to launch the
correct intents?
That depends on whether you have two alarms that will be registered at once, or not.
If not, you can use FLAG_ONE_SHOT or one of the other PendingIntent flags to have your second PendingIntent use the newer extras.
If, however, you will have two alarms registered at once, with different Intent extras, you will need to make the two Intents be more materially different, such that filterEquals() returns false when comparing the two. For example, you could call setData() or setAction() and provide different values for each Intent.
I had this issue in my app and just generated a random number to over come overriding notifications intent:
int random= new Random().nextInt();
PendingIntent resultPendingIntent =
stackBuilder.getPendingIntent(
random,
PendingIntent.FLAG_UPDATE_CURRENT
);
I followed the solution provided by U-ramos and this worked for me
int requestID = (int) System.currentTimeMillis();
PendingIntent pendingIntent = PendingIntent.getActivity(this, requestID, showIntent, 0);
another solution:
use the PendingIntent.FLAG_UPDATE_CURRENT like this:
PendingIntent contentIntent = PendingIntent.getActivity(this, 0,i, PendingIntent.FLAG_UPDATE_CURRENT);
this worked for me