97
  protected void displayNotification(String response) {
    Intent intent = new Intent(context, testActivity.class);
    PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, Intent.FLAG_ACTIVITY_NEW_TASK);

    Notification notification = new Notification(R.drawable.icon, "Upload Started", System.currentTimeMillis());
    notification.setLatestEventInfo(context, "Upload", response, pendingIntent);

    nManager.notify((int)System.currentTimeMillis(), notification);
}

This function will be called multiple times. I would like for each notification to launch testActivity when clicked. Unfortunately, only the first notification launches testActivity. Clicking on the rest cause the notification window to minimize.

Extra information: Function displayNotification() is in a class called UploadManager. Context is passed into UploadManager from the activity that instantiates. Function displayNotification() is called multiple times from a function, also in UploadManager, that is running in an AsyncTask.

Edit 1: I forgot to mention that I am passing String response into Intent intent as an extra.

  protected void displayNotification(String response) {
    Intent intent = new Intent(context, testActivity.class);
    intent.putExtra("response", response);
    PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);

This makes a big difference because I need the extra "response" to reflect what String response was when the notification was created. Instead, using PendingIntent.FLAG_UPDATE_CURRENT, the extra "response" reflects what String response was on the last call to displayNotification().

I know why this is from reading the documentation on FLAG_UPDATE_CURRENT. However, I am not sure how to work around it at the moment.

Kapil Rajput
  • 11,429
  • 9
  • 50
  • 65

14 Answers14

140

Don't use Intent.FLAG_ACTIVITY_NEW_TASK for PendingIntent.getActivity, use FLAG_ONE_SHOT instead


Copied from comments:

Then set some dummy action on the Intent, otherwise extras are dropped. For example

intent.setAction(Long.toString(System.currentTimeMillis()))
Samuel
  • 9,883
  • 5
  • 45
  • 57
ognian
  • 11,451
  • 4
  • 35
  • 33
  • This flag actually did not work either for the same reason, I think, that my extra is not working correctly (check my Edit 1). –  Jul 05 '10 at 23:39
  • 34
    Then set some dummy action on the Intent, otherwise extras are dropped. For example intent.setAction("foo") – ognian Jul 06 '10 at 05:45
  • 21
    Excellent. That worked. I setAction(Long.toString(System.currentTimeMillis())) in conjunction with using FLAG_UPDATE_CURRENT that mbauer suggested. Using FLAG_ONE_SHOT only allowed me to click the notification once (which makes sense). Thanks a lot ognian. –  Jul 06 '10 at 16:14
  • 5
    "Then set some dummy action on the Intent, otherwise extras are dropped" - is this documented somewhere ? – Mr_and_Mrs_D Nov 17 '13 at 14:25
  • The setAction mechanism worked for me. As far as documented, not sure, but the source for Android is available at https://android.googlesource.com/ ;-) – Norman H Jan 14 '14 at 14:06
  • And also need to consider the Update provided by ObjectiveTruth in the answer http://stackoverflow.com/a/24582168/3099185 ... combining @ognian and ObjectiveTruth worked for me ... Thanks.... – Bhuro Sep 13 '16 at 07:33
  • i am using PendingIntent.FLAG_UPDATE_CURRENT and PendingIntent.FLAG_CANCEL_CURRENT both are not working no alarm but if i replace them with 0 they working fine pls help whats the problem here – Sunil Chaudhary Jan 13 '17 at 11:46
  • I avoid `FLAG_ONE_SHOT` like the plague. Especially because if you want to see if the `PendingIntent` exists, you can't check it using `PendingIntent.getXXX(...,PendingIntent.FLAG_NO_CREATE)` because that always returns `null` if you create a `PendingIntent` with `FLAG_ONE_SHOT`. – David Wasser Dec 02 '19 at 21:50
  • 1
    Also, you still need to set `FLAG_ACTIVITY_NEW_TASK` on the `Intent` if you are creating the `PendingIntent` from a non-`Activity` context (like from a `Service`. – David Wasser Dec 02 '19 at 21:51
67

Was struggling with RemoteViews and several different Intents for each Button on HomeScreen Widget. Worked when added these:

1. intent.setAction(Long.toString(System.currentTimeMillis()));

2. PendingIntent.FLAG_UPDATE_CURRENT

        PackageManager pm = context.getPackageManager();

        Intent intent = new Intent(context, MyOwnActivity.class);
        intent.putExtra("foo_bar_extra_key", "foo_bar_extra_value");
        intent.setAction(Long.toString(System.currentTimeMillis()));
        PendingIntent pendingIntent = PendingIntent.getActivity(context, 0,
                intent, PendingIntent.FLAG_UPDATE_CURRENT);
        RemoteViews views = new RemoteViews(context.getPackageName(),
                R.layout.widget_layout);
        views.setOnClickPendingIntent(my_button_r_id_received_in_parameter, pendingIntent);
Kapil Rajput
  • 11,429
  • 9
  • 50
  • 65
ViliusK
  • 11,345
  • 4
  • 67
  • 71
  • +1 Cool Thanks. Any idea why adding intent.setAction() made it work? – AjOnFire Oct 08 '13 at 19:18
  • setAction works but what if I actually have to set my intents Action to something else? Why is the framework so buggy? – b.lyte Jul 08 '14 at 03:16
  • 3
    I just love how Android SDK is so intuitive to developers... (:♥️ BTW read @ObjectiveTruth answer below for explanation on the reason for `setAction` – Aviel Gross Dec 14 '14 at 13:21
  • 1
    Was getting strange behavior, without setAction method call intent extras would work whilst debugging, but when not debugging the intent extras would always be the same as the initial extras passed in the first call. I found that whilst debugging, onCreate was always called when navigating out of app but whilst not debugging, onCreate wasn't called, only onStart. Calling setAction method resolved the issue, I'm guessing it is something to do with intents not being 'different' if only the extras value has changed. – MaxJ Sep 30 '15 at 22:42
  • @clu Since I'm already using `setAction`, what you can do is `addCategory`. `PendingIntent` uses `Intent.filterEquals` to check for equality of the action, data, type, class and categories. https://developer.android.com/reference/android/content/Intent.html#filterEquals(android.content.Intent) – iamreptar Jul 27 '16 at 16:06
  • Should be considered the real answer. – ivanleoncz Mar 17 '17 at 07:12
  • only using PendingIntent.FLAG_ONE_SHOT worked for me .. is this can create any other issue? – Jishant Apr 05 '17 at 11:26
50

Set Action Solved this for me. Here's my understanding of the situation:


I have multiple widgets that have a PendingIntent attached to each. Whenever one got updated they all got updated. The Flags are there to describe what happens with PendingIntents that are exactly the same.

FLAG_UPDATE_CURRENT description reads much better now:

If the same PendingIntent you are making already exists, then update all the old ones to the new PendingIntent you are making.

The definition of exactly the same looks at the whole PendingIntent EXCEPT the extras. Thus even if you have different extras on each intent (for me I was adding the appWidgetId) then to android, they're the same.

Adding .setAction with some dummy unique string tells the OS. These are completely different and don't update anything. In the end here's my implementation which works as I wanted, where each Widget has its own configuration Intent attached:

Intent configureIntent = new Intent(context, ActivityPreferences.class);

configureIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);

configureIntent.setAction("dummy_unique_action_identifyer" + appWidgetId);

PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, configureIntent,
    PendingIntent.FLAG_UPDATE_CURRENT);

UPDATE


Even better solution in case you're working with broadcasts. Unique PendingIntents are also defined by unique request codes. Here's my solution:

//Weee, magic number, just want it to be positive nextInt(int r) means between 0 and r
int dummyuniqueInt = new Random().nextInt(543254); 
PendingIntent pendingClearScreenIntent = PendingIntent.getBroadcast(context, 
    dummyuniqueInt, clearScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT);
ObjectiveTruth
  • 878
  • 10
  • 17
23

I see answers but no explanations. Also none of the answers address all possible solutions, so I'll try to make that clear.

Documentation:

If you truly need multiple distinct PendingIntent objects active at the same time (such as to use as two notifications that are both shown at the same time), then you will need to ensure there is something that is different about them to associate them with different PendingIntents. This may be any of the Intent attributes considered by Intent.filterEquals, or different request code integers supplied to getActivity(Context, int, Intent, int), getActivities(Context, int, Intent[], int), getBroadcast(Context, int, Intent, int), or getService(Context, int, Intent, int).

Cause of the problem:

You create 2 notifications with 2 pending intents. Each pending intent is associated with an intent:

Intent intent = new Intent(context, testActivity.class);

However, these 2 intents are equal, therefore when your 2nd notification arrives it will launch the first intent.

Solution:

You have to make each intent unique, so that no pending intents will ever be equal. How do you make the intents unique? Not by the extras you put with putExtra(). Even if the extras are different, the intents might still be equal. To make each intent unique, you must set a unique value to the intent action, or data, or type, or class, or category, or request code: (any of those will work)

  • action: intent.setAction(...)
  • data: intent.setData(...)
  • type: intent.setType(...)
  • class: intent.setClass(...)
  • category: intent.addCategory(...)
  • request code: PendingIntent.getActivity(context, YOUR_UNIQUE_CODE, intent, Intent.FLAG_ONE_SHOT);

Note: Setting a unique request code might be tricky because you need an int, while System.currentTimeMillis() returns long, which means that some digits will be removed. Therefore I would recommend to either go with the category or the action and setting a unique string.

steliosf
  • 3,669
  • 2
  • 31
  • 45
  • This is what ultimately worked for me, using a unique ID for each notification (needed anyway for cancelability) and a custom category per action (will never have multiple actions of the same type on the same notification). – MandisaW Aug 07 '18 at 22:07
  • Yup, I'm using a unique category for each intent as well, it works great. – steliosf Aug 08 '18 at 00:00
  • Got same problem. two notification triggered at same time. when i click second notification nothing happened. after set this setAction(Long.toString(System.currentTimeMillis())); . its working like charm. thanks for nice explanation @MScott – Anantha Babu Nov 07 '19 at 05:39
13

I had the same problem, and was able to fix it by changing the flag to:

PendingIntent contentIntent = PendingIntent.getActivity(context, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
mbauer14
  • 1,217
  • 2
  • 13
  • 18
  • Thanks a lot for taking the time to post what fixed the problem for you. I forgot to mention that I am passing an extra into the Intent. This makes the problem a bit more complex. Check my Edit 1. –  Jul 05 '10 at 23:38
9

As documentation said use unique request code:

If you truly need multiple distinct PendingIntent objects active at the same time (such as to use as two notifications that are both shown at the same time), then you will need to ensure there is something that is different about them to associate them with different PendingIntents. This may be any of the Intent attributes considered by Intent.filterEquals, or different request code integers supplied to getActivity(Context, int, Intent, int), getActivities(Context, int, Intent[], int), getBroadcast(Context, int, Intent, int), or getService(Context, int, Intent, int).

Tomasz
  • 1,406
  • 1
  • 18
  • 28
8

Fwiw, I have had better luck with PendingIntent.FLAG_CANCEL_CURRENT than with PendingIntent.FLAG_UPDATE_CURRENT.

Jon Shemitz
  • 1,235
  • 13
  • 29
  • I totally agree with this. There is no need to fill intents with useless extras if we can make it cancel the old one and then create a new one. It is true that it may be useless sometimes if nothing changed, but now the question is "to save memory or to save time". – zeroDivider Sep 24 '18 at 14:16
6

I had the same problem and i fixed it by the below steps

1) Clear any flag for intent

intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);

2) insert intent.setAction by the below code

 intent.setAction(Long.toString(System.currentTimeMillis()));

3) for Pendingintent ,insert the below code

   PendingIntent Pintent = PendingIntent.getActivity(ctx,0, intent,PendingIntent.FLAG_UPDATE_CURRENT);

I hope to work with you

  • 1
    Any one care to explain why this answer was downvoted. This worked for me. I dont know if this is legit answer, but this solution is the perfect solution. Atleast for me. – Sandeep R Jun 08 '16 at 05:31
  • Worked for me also! Thanks! – Andres May 17 '17 at 15:16
2
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, Intent.FLAG_ACTIVITY_NEW_TASK);

In PendingIntent is two int parameters, second one and the last one. Second one is "request code" and it must be unicue number (for example id of your notifiction), else if (as in your example it equals zero, it always will be overwritten).

1
// Use pending Intent and  also use unique id for display notification....
// Get a PendingIntent containing the entire back stack
PendingIntent notificationPendingIntent = stackBuilder.getPendingIntent(0,PendingIntent.FLAG_UPDATE_CURRENT);
NotificationManager mNotificationManager = (NotificationManager)  sqlitewraper.context.getSystemService(Context.NOTIFICATION_SERVICE);
// Issue the notification
mNotificationManager.notify(id, builder.build());
Thusitha
  • 3,393
  • 3
  • 21
  • 33
MIkka Marmik
  • 1,101
  • 11
  • 29
1

While create the PendingIntent object we call the PendingIntent.getActivity(mContext, requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT);

So here use the below code to generate the PendingIntent and send this intent to notification. So when you get another notification that will have unique requestCode to get the data of that notification only.

Random objRandom = new Random();
final PendingIntent resultPendingIntent =
        PendingIntent.getActivity(
                mContext,
                objRandom.nextInt(100),
                intent,
                PendingIntent.FLAG_UPDATE_CURRENT
        );
Somnath
  • 159
  • 1
  • 1
  • 7
0

to send data extra correctly you should send with pending intent the notification id like this: PendingIntent pendingIntent = PendingIntent.getActivity(context, (int)System.currentTimeMillis(), intent, PendingIntent.FLAG_UPDATE_CURRENT);

Amal Kronz
  • 1,657
  • 1
  • 18
  • 22
0

I have the same problem, and use the PendingIntent.html.FLAG_UPDATE_CURRENT to fix it.

I have checked the source code. In ActivityManagerService.java, the key method is as follows. When the flag is PendingIntent.FLAG_UPDATE_CURRENT and the updateCurrent is true. Some extras will be replaced by new and we will get an replaced PendingIntent.

    IIntentSender getIntentSenderLocked(int type, String packageName,
            int callingUid, int userId, IBinder token, String resultWho,
            int requestCode, Intent[] intents, String[] resolvedTypes, int flags,
            Bundle bOptions) {

// ... omitted

        final boolean noCreate = (flags&PendingIntent.FLAG_NO_CREATE) != 0;
        final boolean cancelCurrent = (flags&PendingIntent.FLAG_CANCEL_CURRENT) != 0;
        final boolean updateCurrent = (flags&PendingIntent.FLAG_UPDATE_CURRENT) != 0;
        flags &= ~(PendingIntent.FLAG_NO_CREATE|PendingIntent.FLAG_CANCEL_CURRENT
                |PendingIntent.FLAG_UPDATE_CURRENT);

        PendingIntentRecord.Key key = new PendingIntentRecord.Key(
                type, packageName, activity, resultWho,
                requestCode, intents, resolvedTypes, flags, bOptions, userId);
        WeakReference<PendingIntentRecord> ref;
        ref = mIntentSenderRecords.get(key);
        PendingIntentRecord rec = ref != null ? ref.get() : null;
        if (rec != null) {
            if (!cancelCurrent) {
                if (updateCurrent) {
                    if (rec.key.requestIntent != null) {
                        rec.key.requestIntent.replaceExtras(intents != null ?
                                intents[intents.length - 1] : null);
                    }
                    if (intents != null) {
                        intents[intents.length-1] = rec.key.requestIntent;
                        rec.key.allIntents = intents;
                        rec.key.allResolvedTypes = resolvedTypes;
                    } else {
                        rec.key.allIntents = null;
                        rec.key.allResolvedTypes = null;
                    }
                }
                return rec;
            }
            rec.canceled = true;
            mIntentSenderRecords.remove(key);
        }
qin hao
  • 43
  • 6
-5

I had the same problem, and was able to fix it by changing the flag to:

LayoutInflater factory = LayoutInflater.from(this);            
      final View textEntryView = factory.inflate(R.layout.appointment, null);
      AlertDialog.Builder bulider= new AlertDialog.Builder(PatientDetail.this);
      final AlertDialog alert=bulider.create();


        bulider.setTitle("Enter Date/Time");
        bulider.setView(textEntryView);
        bulider.setPositiveButton("Save", new DialogInterface.OnClickListener() {

                public void onClick(DialogInterface dialog, int which) {
                      EditText typeText=(EditText) textEntryView.findViewById(R.id.Editdate);
                      EditText input1 =(EditText) textEntryView.findViewById(R.id.Edittime);
                      getDateAndTime(typeText.getText().toString(),input1.getText().toString());
                }
            });
        bulider.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {

                public void onClick(DialogInterface dialog, int which) {
                    dialog.cancel();
                }
            });

        bulider.show();

    }