How to use requestCode in PendingIntents - android

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.

Related

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);
}

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.

PendingIntent get requestCode

I use an AlarmManager to start a service. When i set up the AlarmManager i use the PendingIntent and use a unique requestCode which is equal to a id row in my database.
PendingIntent pendingIntent = PendingIntent.getBroadcast(SettingsActivity.this,
lecture.getId(), myIntent, 0);
How can I retrieve that id in my service when it starts? I basically need only the requestCode parameter. I want to use that id to retrieve data from my database and show it in a notification. I have implemented all the stuff, I just need that requestCode. Is it possible to get it?
Thanks
You need to put lecture.getId() into extras of your myIntent. According to Javadoc requestCode is not even used yet.
// store id
myIntent.putExtra("id", lecture.getId());
// read id or -1, if there is no such extra in intent
int id = myIntent.getIntExtra("id", -1);

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

Issue with cancelling the AlarmManager - PendingIntent

I have an app which reminds people to do their tasks. So there is one PendingIntent, now the user can delete the alarm when he wants to. In this code, there is just one PendingIntent for multiple user alarms so I am confused on cancelling that particular alarm where the intent extras is "pill". The remaining alarms should not be cancelled. I have no clue on this problem. Hope I am clear. Thanks
Intent intent = new Intent(this, AlarmNotifyReceiver.class);
intent.putExtra("Name_pill", "pill");
sender = PendingIntent.getBroadcast(this,
DatabaseConstants.NOTIFICATION_ID + 1, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP,cal.getTimeInMillis(), sender);
updateTheFlag(pillName[(pillName.length-1)]);
According to the Android documentation, in order to stop an alarm, you should create an Intent with the same data, but not necessarily the same extras:
public void cancel (PendingIntent operation)
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.
filterEquals(Intent)
public boolean filterEquals (Intent other)
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.
As I stated in my comment, it appears that you simply need to recreate the exact same PendingIntent object, and put the same Extras into it. Then, you call
am.cancel(sender);
And your specific alarm should be canceled. I can't find a better way of doing it, personally. I found this information to confirm my expectation elsewhere.
It reads:
Repeating alarms have to be cancelled to stop them. AlarmManager provide a cancel() method that requires the same intent class with which the intent is created. This is how you can cancel the alarm.
alarmManager.cancel(pendingIntent);
Note the pendingIntent object does not need to be same object. The intent fields like action, class, category etc should be same while creating the alarm. The intent is used to identify the alarm to cancel it.
It is in the context of repeating alarms, but one-time alarms should be canceled in the same manner, if I am not mistaken. I am unable to test it more thoroughly on my own because I am at work, but this should work.
I think the requestCode parameter in getBroadcast() needs to be mentioned. I agree that all the alarms will be canceled matching with the given Intent. But an alarm can be made to be unique by using unique requestCode when defining the PendingIntent for canceling. So only those alarms will be canceled which has the same intent and requestCode:
int TIMER_1 = 1;
int TIMER_2 = 2;
AlarmManager am = (AlarmManager)getSystemService(ALARM_SERVICE);
Intent i = new Intent(this, AppReciever.class);
i.putExtra("timer", "one");
PendingIntent pending = PendingIntent.getBroadcast(this, TIMER_1, i,
PendingIntent.FLAG_CANCEL_CURRENT);
Calendar cal = Calendar.getInstance();
am.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), pending);
then check that the PendingIntent exist according to this:
PendingIntent pending1 = PendingIntent.getBroadcast(this, TIMER_2, i,
PendingIntent.FLAG_NO_CREATE);
boolean alarmUp = (pending1 != null);
alarmUp will be false (note FLAG_NO_CREATE is used not to create a new one if not exist) so trying with same requestCode:
PendingIntent pending2 = PendingIntent.getBroadcast(this, TIMER_1, i,
PendingIntent.FLAG_NO_CREATE);
alarmUp = (pending2 != null);
alarmUp will be true, now trying with a new intent contains different extra:
Intent i2 = new Intent(this, AppReciever.class);
i2.putExtra("timer", "two");
pending2 = PendingIntent.getBroadcast(this, TIMER_1, i2,
PendingIntent.FLAG_NO_CREATE);
alarmUp = (pending2 != null);
alarmUp will be true as well since the i and i2 are the same although the extra is not, so now you can remove this alarm:
am.cancel(pending2);
So there is one pending intent,now the user can delete the alram when
he wants to. Ib this code there is just one pending intent for
multiple user alarms so I am confused on cancelling that particular
alarm where extras is pill
intent.putExtra("Name_pill", "pill");
The extra wont wont work to cancel your pending intent .
pendingIntent.cancel() will only remove that pending intent which triggered with same filterEquals(Intent) and that method is not compare any extra data given to intent .
this is the contain from developer site of android filterEquals(Intent)
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 we consider your scenario , when you will pass that Extra to intent at that time you only need to save unique ID in some sharedpreference which given in parameter and one thing your should keep in mind that ID has to be an unique .
and and when you suppose to cancel that alarm , just pass same intent with that saved ID and cancel that pendingintent .
Create
preference_saved_value = DatabaseConstants.NOTIFICATION_ID + 1
sender = PendingIntent.getBroadcast(this,
preference_saved_value, intent,
PendingIntent.FLAG_UPDATE_CURRENT)
CANCEL
sender = PendingIntent.getBroadcast(this,
preference_saved_value, intent,PendingIntent.FLAG_UPDATE_CURRENT);
sender.cancel()
As it's stated in android documentation pending intents with intent that are equivalent as per Intent.filterEquals but have different request code are considered different:
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, you can assign different request code and cancel the pending intents base on them and forget about the extra.
There was an interesting scenario that I figured out this behavior:
I scheduled an alarm in my code and run it on the device but never canceled it. Then I changed the request code and run it again. So a new alarm was created. I canceled the new alarm but the alarm was still executing from previous code. I get confused why the alarm is not canceled. After I found out it's from the previous code with different request code I uninstalled the app and installed it again and problem was solved.

Categories

Resources