Can PendingIntent.FLAG_IMMUTABLE replace PendingIntent.FLAG_UPDATE_CURRENT? - android

I'm dealing with PendingIntent with notification.
In my project, I've been using PendingIntent.FLAG_UPDATE_CURRENT in some of code. and the definition of it is below.
Flag indicating that if the described PendingIntent already exists,
then keep it but replace its extra data with what is in this new
Intent. For use with getActivity(Context, int, Intent, int),
getBroadcast(Context, int, Intent, int), and getService(Context, int,
Intent, int).
This can be used if you are creating intents where only the extras
change, and don't care that any entities that received your previous
PendingIntent will be able to launch it with your new extras even if
they are not explicitly given to it.
FLAG_UPDATE_CURRENT still works even if FLAG_IMMUTABLE is set - the
creator of the PendingIntent can always update the PendingIntent
itself. The IMMUTABLE flag only limits the ability to alter the
semantics of the intent that is sent by send() by the invoker of
send().
But I have to choose between FLAG_IMMUTABLE or FLAG_MUTABLE because of Android12. In my case, i don't need to use PendingIntent.FLAG_MUTABLE. the definition of it is below.
Flag indicating that the created PendingIntent should be immutable.
This means that the additional intent argument passed to the send
methods to fill in unpopulated properties of this intent will be
ignored.
FLAG_IMMUTABLE only limits the ability to alter the semantics of the
intent that is sent by send() by the invoker of send(). The creator of
the PendingIntent can always update the PendingIntent itself via
FLAG_UPDATE_CURRENT.
as you can see, they say "FLAG_UPDATE_CURRENT still works even if FLAG_IMMUTABLE is set..." and "The creator of the PendingIntent can always update the PendingIntent itself via FLAG_UPDATE_CURRENT."
So, what i want to know is that FLAG_IMMUTABLE can perfectly replace FLAG_UPDATE_CURRENT in any Android version?
For example
PendingIntent.getActivity(context, requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT);
=> PendingIntent.getActivity(context, requestCode, intent, PendingIntent.FLAG_IMMUTABLE);

FLAG_IMMUTABLE and FLAT_MUTABLE controls if other apps can modify your PendingIntent. For instance, if you were using a direct reply action in a notification, the system would need you to use FLAT_MUTABLE to allow it to fill in the text the user typed and send it to you.
FLAG_UPDATE_CURRENT is the ability for your app to update its own PendingIntent. This means if you create the same PendingIntent where the only difference is the extras attached to your Intent, FLAG_UPDATE_CURRENT would ensure that your new extras are actually used (instead of just the original set being used again).
So in every case where you were using FLAG_UPDATE_CURRENT, you'd want to continue to use FLAG_UPDATE_CURRENT, adding in the correct mutability flag for your case (which, in 99% of cases, is FLAG_IMMUTABLE).
To apply both flags, use the | symbol in Java (or the word or in Kotlin):
PendingIntent.getActivity(context, requestCode, intent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);

Related

What are all the flags in pending intent

I know the concept of pending intent but the flags are confusing.
Even android documentation is very hard to understand
Can someone provide explanation of pending intent flags particularly FLAG_ONE_SHOT and FLAG_NO_CREATE with examples?
PendingIntents are managed by the Android framework. When you call one of the PendingIntent.getXXX() methods, the framework tries to find an existing PendingIntent that matches the parameters you pass to the getXXX() method. If it finds a matching PendingIntent it will just return that to the caller. If it doesn't find a matching PendingIntent it will (usually) create a new PendingIntent and return that to the caller. You can alter this standard behaviour by using the flags:
FLAG_NO_CREATE is used to get an existing PendingIntent. If a matching PendingIntent exists, it will be returned to the caller. If no matching PendingIntent exists, nothing happens. The framework will not create a new PendingIntent and the method returns null to the caller. You can use this method to determine if a specific PendingIntent exists. You can also use this method to get an existing PendingIntent so that you can cancel it.
FLAG_ONE_SHOT is strange. According to the documentation, this flag should cause the PendingIntent to be deleted after it is used (sent). However, there are other side-effects of this flag. For example, if you create a PendingIntent using this flag, and then try to get this PendingIntent (or test the existence of it) by calling PendingIntent.getXXX() with FLAG_NO_CREATE, the framework will always return null. For this reason I never use it and I also recommend that it never be used.
FLAG_CANCEL_CURRENT is used to delete an existing PendingIntent and create a new one. The framework first tries to find a matching PendingIntent. If it finds one, it cancels (deletes) this PendingIntent. That means that any applications holding this PendingIntent will not be able to trigger (send) it. The framework then creates a new PendingIntent with the provided parameters and returns this to the caller.
FLAG_UPDATE_CURRENT is used to update an existing PendingIntent. The framework first tries to find a matching PendingIntent. If it finds one, the "extras" in the existing PendingIntent are overwritten with the "extras" in the provided Intent parameter. If no matching PendingIntent is found, a new one is created with the provided parameters. The found (or newly created) PendingIntent is returned to the caller.
NOTE: See this answer for information on how the Android framework tries to find a "matching" PendingIntent: https://stackoverflow.com/a/29590084/769265

How to update/remove old alarms?

I have a functionality in my app that creates alarm when it get data from the external API. when API data is refreshed, it again create alarms based on new data. Every time the user visit that specific view, the current data is pulled from the server and alarm is created. How can I ensure that the alarms created using old data get removed? The functionality is a kind of reminder service.
You can either use the same requestCode you used initially when creating the PendingIntent for your alarms if you used the PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_CANCEL_CURRENT to create additional alarms and it will cancel the alarm first and create a new one in the case of PendingIntent.FLAG_CANCEL_CURRENT or it will update the existing one with the new data in the case of PendingIntent.FLAG_UPDATE_CURRENT
From the Docs
FLAG_CANCEL_CURRENT
Flag indicating that if the described PendingIntent already exists, the current one should be canceled before generating a new one. For use with getActivity(Context, int, Intent, int), getBroadcast(Context, int, Intent, int), and getService(Context, int, Intent, int).
You can use this to retrieve a new PendingIntent when you are only changing the extra data in the Intent; by canceling the previous pending intent, this ensures that only entities given the new data will be able to launch it. If this assurance is not an issue, consider FLAG_UPDATE_CURRENT.
FLAG_UPDATE_CURRENT
Flag indicating that if the described PendingIntent already exists, then keep it but replace its extra data with what is in this new Intent. For use with getActivity(Context, int, Intent, int), getBroadcast(Context, int, Intent, int), and getService(Context, int, Intent, int).
This can be used if you are creating intents where only the extras change, and don't care that any entities that received your previous PendingIntent will be able to launch it with your new extras even if they are not explicitly given to it.
Otherwise, you can cancel them yourself manually if those flags weren't specified. You just need the request code
PendingIntent pendingIntent =
PendingIntent.getBroadcast(mContext, requestCode, alarmReceiverClassIntent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager am = (AlarmManager) mContext.getSystemService(ALARM_SERVICE);
if (am != null) {
am.cancel(pendingIntent);
}

Multiple Pending Intents?

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.

What's "requestCode" used for on 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.

Create new Pending Intent every time in Android

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).

Categories

Resources