My activity creates a couple of notifications.
Here's how I'm currently doing it, on different resultIntents:
PendingIntent resultPendingIntent =
PendingIntent.getActivity(
context,
0,
resultIntent,
PendingIntent.FLAG_UPDATE_CURRENT
);
Now, since a flag is compulsory, I'm forced to select from the four flags. What do I do if I want all of them to work independently, and the newer notification isn't affected by the previous one.
A solution was found here: here.
You've to use setAction on the intent to a unique value so that there will be no matching PendingIntents
Here's what I used:
setAction(Long.toString(System.currentTimeMillis()))
From the official 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).
Intent.filterEquals documentation:
Determine if two intents are the same for the purposes of intent resolution (filtering). That is, if their action, data, type, class, and categories are the same. This does not compare any extra data included in the intents.
If you need an unlimited amount of PendingIntents, I would recommend using Intent.setData to some sort of unique value. If you can group them, use setType or addCategory to reduce the number of unique PendingIntents needed.
Related
Documentation for PendingIntent.FLAG_NO_CREATE reads:
Flag indicating that if the described PendingIntent does not already exist, then simply return null instead of creating it.
My question: What criteria are used to compare PendingIntents?
I'm guessing under the hood this flag uses PendingIntent.equals, but I'm not really sure what criteria that function is using. Is it using the action, requestCode, categories, extras (I'm guessing no), etc.?
Context:
I want to start an alarm with a pending intent if my alarm is not already setup. Specifically, I'm following this answer.
Intent i = new Intent(applicationContext, MyService.class);
i.setAction("myAction");
PendingIntent pi = PendingIntent.getService(applicationContext, /*requestCode*/0, i, PendingIntent.FLAG_NO_CREATE);
if (pi != null) {
AlarmManager alarmMgr = (AlarmManager)applicationContext.getSystemService(Context.AlarmService);
alarmMgr.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, AlarmManager.INTERVAL_HOUR, AlarmManager.INTERVAL_HOUR, pi);
}
To determine if 2 PendingIntents match, the following must be equal:
The requestCode parameter used when the PendingIntent was created
The Intent ACTION
The Intent CATEGORIES
The Intent DATA
The Intent MIMETYPE
The Intent PACKAGE
The Intent COMPONENT
Extras are not taken into consideration.
You can read more in the PendingIntent summary documentation and Intent.filterEquals().
I'm guessing under the hood this flag uses PendingIntent.equals, but
I'm not really sure what criteria that function is using. Is it using
the action, requestCode, categories, extras (I'm guessing no), etc.?
Actually the hint is in class description:
A description of an Intent and target action to perform with it.
Instances of this class are created with
getActivity(android.content.Context,int,android.content.Intent,int),
getActivities(android.content.Context,int,android.content.Intent[],int),
getBroadcast(android.content.Context,int,android.content.Intent,int),
getService(android.content.Context,int,android.content.Intent,int);
the returned object can be handed to other applications so that they
can perform the action you described on your behalf at a later time.
By giving a PendingIntent to another application, you are granting it
the right to perform the operation you have specified as if the other
application was yourself (with the same permissions and identity). As
such, you should be careful about how you build the PendingIntent:
almost always, for example, the base Intent you supply should have the
component name explicitly set to one of your own components, to ensure
it is ultimately sent there and nowhere else.
A PendingIntent itself
is simply a reference to a token maintained by the system describing
the original data used to retrieve it. This means that, even if its
owning application's process is killed, the PendingIntent itself will
remain usable from other processes that have been given it. If the
creating application later re-retrieves the same kind of PendingIntent
(same operation, same Intent action, data, categories, and components,
and same flags), it will receive a PendingIntent representing the same
token if that is still valid, and can thus call cancel() to remove it.
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.
There are two typical ways to deal with this.
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(android.content.Context,int,android.content.Intent,int),
getActivities(android.content.Context,int,android.content.Intent[],int),
getBroadcast(android.content.Context,int,android.content.Intent,int),
or getService(android.content.Context,int,android.content.Intent,int).
If you only need one PendingIntent active at a time for any of the
Intents you will use, then you can alternatively use the flags
FLAG_CANCEL_CURRENT or FLAG_UPDATE_CURRENT to either cancel or modify
whatever current PendingIntent is associated with the Intent you are
supplying.
from:
http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.0.0_r1/android/app/PendingIntent.java#PendingIntent
Background:
I'm using PendingIntent for alarms via AlarmManager.
The problem:
At first I thought that in order to cancel previous ones, I must provide the exact requestCode that I've used before to start the alarm.
But then I've found out I was wrong, as the cancellation API says:
Remove any alarms with a matching Intent. Any alarm, of any type,
whose Intent matches this one (as defined by filterEquals(Intent)),
will be canceled.
looking at "filterEquals", the documentation says:
Determine if two intents are the same for the purposes of intent
resolution (filtering). That is, if their action, data, type, class,
and categories are the same. This does not compare any extra data
included in the intents.
so I don't get what the "requestCode" is for...
The question:
What is "requestCode" used for?
What if I create multiple alarms with the same "requestCode" ? do they override each other?
requestCode is used to retrieve the same pending intent instance later on (for cancelling, etc).
Yes, my guess is the alarms will override each other. I would keep the request codes unique.
I just want to add to #Minhaj Arfin answer
1- requestCode is used to get the same pending intent later on (for cancelling etc)
2- Yes, they will get override as long as your specify the same Receiver to your Intent that you specify on your PendingIntent
example:
Intent startIntent1 = new Intent(context, AlarmReceiverFirst.class);
PendingIntent pendingIntent1 = PendingIntent.getBroadcast(context, 0, startIntent1, 0);
Intent startIntent2 = new Intent(context, AlarmReceiverSecond.class);
PendingIntent pendingIntent2 = PendingIntent.getBroadcast(context, 0, startIntent2, 0);
From above example, they will not override each other because the receiver is different(AlarmReceiverFirst and AlarmReceiverSecond)
Intent startIntent2 = new Intent(context, AlarmReceiverSecond.class);
PendingIntent pendingIntent2 = PendingIntent.getBroadcast(context, 0, startIntent2, 0);
Intent startIntent3 = new Intent(context, AlarmReceiverSecond.class);
PendingIntent pendingIntent3 = PendingIntent.getBroadcast(context, 0, startIntent3, 0);
From above example, they will override each other, because the receiver is same(AlarmReceiverSecond)
Actually, the documentation clearly states what the request code is used for:
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(Intent), 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).
Since it seems that it still isn't that clear, let me try to explain:
When you want to use a PendingIntent object, you don't just instantiate one. Rather, you obtain one from the system using the PendingIntent static methods (getActivity, getBroadcast, getService etc). The system keeps a bunch of PendingIntent instances and gives you one. Which one it gives you, it depends on the input parameters you pass to these getter methods. Those input parameters are: Context, i.e. the target receiver of the intent, the Intent to use, requestCode and flags. When you pass the same Context, the same requestCode and the same Intent (meaning an intent that filterEquals with another intent), you get the same PendingIntent object. The point is that the system wants to have as few PendingIntent objects as possible, so it tends to reuse the existing ones, as much as possible.
For example, you have two calendar notifications, for two different dates. When you click on one of them, you want your app to open to the corresponding date of that notification. In that scenario, you have the same Context target, and the Intent object you are passing differ only in the EXTRA_DATA (which specifies the date that should be open). If you provide the same requestCode when obtaining the PendingIntent object, then you will end up with the same PendingIntent object. So, when creating the second notification, you will replace the old Intent object with the new EXTRA_DATA, and end up with two notifications pointing to the same date.
If you want to have two different PendingIntent objects, as you should in this scenario, you should specify a different requestCode when obtaining the PendingIntent object.
in my case i want to open the same activity with two different intents so if two or more FCMS are there in the tray, any one of them will only open other will not, so i changed the requests codes of pending intent then it worked.
PendingIntent pendingIntent =
PendingIntent.getActivity(this, **Some unique id for all GCMS** /* Request code */, intent,
PendingIntent.FLAG_ONE_SHOT);
one important thing about requestCode that will seriously trouble your app is when using widgets.
widgets will not work after phone reboot if their requestCode are the same.
that means the pendingIndent you set on the remoteViews of your widget must be set unique requestCode, usually the widgetId accompanying a number.
I'm trying to implement a reminder app.I have all reminder details stored in sqlite database such id,title,dateInfo,timeInfo etc.
I want to notify the user at appropriate time about the reminder for which i would be using AlarmManager.
Is the below given steps feasible.
Providing id of row as requestCode in pendentingIntents.
Then setting an alarm that would call a service once triggered.
The service would use this id to get data from the database.
If this is feasible can anyone pls provide me with a code snippet.
Any help would be highly appreciated
You can simply put your rowId as extra in the called intent. Here's a code snippet that might help:
Intent i = new Intent(this, YourServiceOrActivity.class);
i.putExtra("rowId", rowId);
PendingIntent pi = PendingIntent.getActivity(this, 123123, i, 0); //Notice the random requestCode '123123' as it doesn't matter, yet.
And you can set the alarm as follows:
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC_WAKEUP, AlarmManager.INTERVAL_DAY, pi);
//Set the alarm to wake up the device after one day calling the pending intent 'pi'.
Finally you can get the rowId from the intent (onCreate() for example if intent is an Activity) when called as follows:
Bundle extras = getIntent().getExtras();
if (extras != null) rowId = extras.getInt("rowId", 0); //Be safe
Hope this helps.
As others mentioned I would put rowId as an extra, BUT do not be mistaken - as wroten in PendingIntent doc, intents are distinct as below:
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).
So if you put your request with the same id for all intents for the same row, with different data, you can get lost with obsolete/repeated data issues.
According to the docs on PendingIntent, the requestCode of the getService(...) method is not used. It's not clear to me what happens to it, so I wouldn't rely on it in any way.
Just pass the row ID as an extra in the Intent.
How do I create a pending intent everytime? Currently my existing pending intent is getting replaced with a new one. I tried using FLAG_ONE_SHOT as well as CANCEL_CURRENT but it didn't work.
add a random number to the request code like that:
Intent intent = new Intent(context, YourClassname.class);
intent.putExtra("some data", "txt"); // for extra data if needed..
Random generator = new Random();
PendingIntent i=PendingIntent.getActivity(context, generator.nextInt(), intent,PendingIntent.FLAG_UPDATE_CURRENT);
FLAG_CANCEL_CURRENT- if the described PendingIntent already exists, the current one is canceled before generating a new one.
FLAG_NO_CREATE - if the described PendingIntent does not already exist, then simply return null instead of creating it.
FLAG_ONE_SHOT - this PendingIntent can only be used once.
FLAG_UPDATE_CURRENT- if the described PendingIntent already exists, then keep it but its replace its extra data with what is in this new Intent.
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).
I'm trying to distinguish between different instances of PendingIntent by having separate requestCode for each use-case as suggested by this earlier question.
Is this a robust solution? Has requestCode always been supported even though the javadocs still say that it is "currently not used"?
Yes. The requestCode has always been there. It isn't currently used by the Android framework to do anything other than as part of the test for PendingIntent matching. Using requestCode to determine different PendingIntents is robust and supported. The documentation even says so:
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).