PendingIntent throughout multiple Instances - android

I have implemented an Alarm class wich should set a new pending Intent and always overriwrite the old one. (I would rather stop/delete all old ones but I dont know how to)
private void startAlarm(){
Intent intent = new Intent(source, Alarm_Activity.class);
// 10000 should be the ID of Intent
PendingIntent pendingIntent = PendingIntent.getActivity(source, 10000, intent, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager am = (AlarmManager)source.getSystemService(Context.ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(),pendingIntent);
}
Unfortionatly I create this class multiple times and from different activities. I think this is the reason why it doesnt cancel the last intent (Flag_cancel_current). How can I make the Flag work throughout multiple instances of this class?

Given your code, so long as all places are using the same Intent (pointing to Alarm_Activity.class) and are using the same PendingIntent ID (10000 in your sample), your code will cancel any current PendingIntent.
This does not cancel any current alarms.
To cancel an alarm, call cancel() on AlarmManager. In particular, if you do this, get rid of PendingIntent.FLAG_CANCEL_CURRENT, so your cancelling of the old PendingIntent does not somehow interfere with your cancelling of the old alarm tied to that PendingIntent.

Related

Multiple Alarms using BroadcastReceiver and AlarmManager

Ive been searching for how to using multiple alarms, and did find post in Stackoverflow, but never a straightful answer.
Im trying to set multiple alarms who will call a receiver class (AlarmBcast in my case) and, according to which alarm triggered the call, take different actions.
At this point I followed my way creating diferent receicer classes for each alarm call (and thus a different Intent, each with his own PendintIntent).
I read someone in other thread suggested making a stack (list) of alarms visible, and so using one receiver class for all alarms, but that is not an approach I would like to take.
There is a way to "see" which pendingintent was responsible for the call?.
only the intent is passed, but as there is only one, cant get which call was from it.
Before taking the way of the multiple receivers, this is how my code(pseudo) looked
Setting the alarms...
public class SelectedService extends Service {
//get dates from main
//do some date math
AlarmManager alarmManager = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
Intent _myIntent = new Intent(this, AlarmBcast.class);
//setting Alarm A
PendingIntent _myPendingIntent = PendingIntent.getBroadcast(this, 123, _myIntent, PendingIntent.FLAG_UPDATE_CURRENT);
alarmManager.set(AlarmManager.RTC_WAKEUP, partidocalendar.getTimeInMillis(),_myPendingIntent);
//setting Alarm B
PendingIntent _myPendingIntent2 = PendingIntent.getBroadcast(this, 124, _myIntent, PendingIntent.FLAG_UPDATE_CURRENT);
alarmManager.set(AlarmManager.RTC_WAKEUP, convopartidocalendar.getTimeInMillis(),_myPendingIntent2);
//setting Alarm C
The receiver as i wa planning it ...
public class AlarmBcast extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
//how to identify call A from B ??
if(AlarmA){}
if(AlarmB){}
}
Maybe i have some conceptual mistake? Back to programming since after 15 years (mostly assembler before)
You can put an "extra" in the Intent that identifies which alarm it is. For example:
_myIntent.putExtra("alarm", "A");
Do this before calling PendingIntent.getBroadcast() for each different alarm.
Then, in your BroadcastReceiver.onReceive() you can check the "extra" in the Intent to determine which alarm got triggered.
I fixed this issue by separating and creating two different Notification Channel and Classes

How to update the data send to a service using intent when service is started by alarm manager?

I am writing an Android application where the user can choose several stocks to watch and gets alerted if an predefined alert condition is matched. The stock data is saved to 5 objects of a custom Parcelable class "alert" (one object per stock and condition). The periodic data update is done via a service started by an AlarmManager. The alert objects are passed to the service via putting them into the Intent which is put into the PendingIntent of the AlarmManager.
Intent intent = new Intent(this, UpdateService.class);
Bundle b = new Bundle();
saveAlertsToBundle(b);
intent.putExtras(b);
intent.setData(Uri.parse("updateManager"));
PendingIntent pendIntent = PendingIntent.getService(this,0,intent,0);
// 1min intervall
long intervall = DateUtils.MINUTE_IN_MILLIS * 1;
// time of first start
long firstStartDelay = DateUtils.SECOND_IN_MILLIS * 30;
long firstStart = System.currentTimeMillis() + firstStartDelay;
AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
// kill running
am.cancel(pendIntent);
//start new
am.setRepeating(AlarmManager.RTC_WAKEUP,firstStart,intervall,pendIntent);
My problem is:
When starting the service for the first time when there is only one object of alert passed to the service everything works fine. As soon as there are more alerts objects existing they also need to be passed to the service but this does not work with the code above. The service does not receive the updated intent with the additional alert objects , but only the initial one with only one alert object. The code above correctly creates an Intent holding the additional alert object, but they never get to the service.
So my question is, how to pass the updated intent to the already running AlarmManager.
I already tried stopping the AlarmManager (the line at the // kill running comment) and restarting it, but this does not work. Perhaps because of the intent not holding the same alert objects as at the time when he was created ? I tried to fix this by setting an uri in the data part of the intent but this also did not help.
Thanks for help.
Your problem is the way PendingIntent works. The system manages a pool of PengingIntents. When your code does:
PendingIntent pendIntent = PendingIntent.getService(this,0,intent,0);
This causes the system to search for a PendingIntent that matches the parameters you've passed in (in this case, your Intent. However, the matching algorithm that PendingIntent uses only compares certain fields of the Intent to determine if it is the one that you are looking for. In particular, it does not compare extras. So this means after you've created the first PendingIntent, the call to PendingIntent.getService() will always return the same PendingIntent from the pool (and not create a new one, which is what you want).
In order to make the call to PendingIntent.getService() create a new PendingIntent every time you call it, try making the parameters you pass to the call unique, like this:
int requestCode = (int) System.currentTimeMillis(); // Create unique request code
PendingIntent pendIntent = PendingIntent.getService(this, requestCode, intent, 0);
Since requestCode will be different for each call to PendingIntent.getService(), this should solve your problem.
EDIT Based on OP's comments below
You want to cancel the existing alarm and create a new one with new data. In that case you don't need to use unique identifiers because you only want to have a single PendingIntent in the pool. But, you want to change the data for that. Try this:
// Create a PendingIntent (or update the existing PendingIntent with new values
PendingIntent pendIntent = PendingIntent.getService(this, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
// cancel any pending alarms
am.cancel(pendIntent);
//start new
am.setRepeating(AlarmManager.RTC_WAKEUP,firstStart,intervall,pendIntent);

AlarmManager.set(...) behavior not matching documentation. Am I doing something wrong?

I'm setting up alarms using this code
//in onCreate()
mAlarmManager = (AlarmManager) getApplicationContext()
.getSystemService(ALARM_SERVICE);
//called for each timer I schedule
Intent intent = new Intent (Intents.MY_INTENT_ACTION);
PendingIntent pendIntent = PendingIntent.getBroadcast(
getApplicationContext(), alert.getID(),
intent, PendingIntent.FLAG_ONE_SHOT);
long delay = 1000 * alert.getDuration();
Calendar cal = Calendar.getInstance();
mAlarmManager.set(AlarmManager.RTC_WAKEUP,
cal.getTimeInMillis() + delay, pendIntent);
But the behavior I'm seeing doesn't match what I should see in the documtation1,
public void set(int type, long triggerAtTime, PendingIntent operation)
If there is already an alarm scheduled for the same IntentSender, it will first be canceled...If there is already an alarm for this Intent scheduled (with the equality of two intents being defined by filterEquals(Intent)), then it will be removed and replaced by this one...
which suggests that calling set(int type, long triggetAtTime, PendingIntent operation) for an already alarmed intent should replace the old alarm for that intent. I'm not seeing any alarms get dropped. Instead, every alarm i set fires, despite the fact that the intents that are fired by the pending intents should all match (by filterEquals(intent)), since all I've set on each intent is an identical action.
Am I doing something wrong, or is the API not behaving as documented?
Note: changing the PendingIntent instantiation to
PendingIntent pendIntent = PendingIntent.getBroadcast(
getApplicationContext(), CONSTANT_ID,
intent, PendingIntent.FLAG_ONE_SHOT);
Behaves as expected, dropping any already set alarm, and replacing it with a new alarm.
Maybe it is because you are giving each alarm a different ID (Does alert.getID() give different ID's or not?). By the documentation, it shouldn't matter but yet you should still try.
If it doesn't work too, hold a reference for your last set alarm, and when you need it to be canceled, cancel it yourself then set the next one.
Have you tried with PendingIntent flag : PendingIntent.FLAG_UPDATE_CURRENT intead of PendingIntent.FLAG_ONE_SHOT?
It appears the consensus is that the documentation for AlarmManager.set(), as well as other AlarmManager methods claiming that Intents (not just the wrapping PendingIntents) are compared to check whether a particular alarm is already set.
Do not rely on AlarmManager matching Intents, instead rely on the matching of PendingIntents, which appears to be working as advertised.

How to cancel Android notifications?

I am making an app where the user can set an alarm based on GPS location. I only want 1 alarm to be active at any one time. So, when the user sets a 2nd alarm, I want to cancel the notification for the 1st alarm (then set a new notification for the 2nd alarm).
Right now, my notifications continue to stack up (as in I can't delete them, so they are all active). Here is my code where I am trying to delete the alarm and notification(s):
// Stop the location alarm activity
Intent intentAlarmService_delete = new Intent(v.getContext(), AlarmService.class);
stopService(intentAlarmService_delete); // I think this calls onDestroy() in AlarmService class ...
mNtf = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
mNtf.cancelAll();
Intent alarmIntent2 = new Intent(getApplicationContext(), OneTimeAlarmReceiver.class);
PendingIntent pendingIntentAlarm = PendingIntent.getBroadcast(getApplicationContext(), PENDING_INTENT_REQUEST_CODE1,
alarmIntent2, PendingIntent.FLAG_CANCEL_CURRENT);
pendingIntentAlarm.cancel();
This is the onDestroy() function in my AlarmService.class (I'm not really sure when this is called...)
public void onDestroy(){
super.onDestroy();
mNtf.cancel(NOTIFICATION_ID1);
Intent alarmIntent = new Intent(getApplicationContext(), OneTimeAlarmReceiver.class);
PendingIntent pendingIntentAlarm = PendingIntent.getBroadcast(getApplicationContext(), PENDING_INTENT_REQUEST_CODE1,
alarmIntent, PendingIntent.FLAG_CANCEL_CURRENT);
pendingIntentAlarm.cancel();
Intent intentAlarmService = new Intent(getApplicationContext(), AlarmService.class);
stopService(intentAlarmService);
mNtf.cancel(NOTIFICATION_ID1);
mNtf.cancelAll();
}
Then, this is how I am setting a new alarm and notification:
Intent intentAlarmService2 = new Intent(v.getContext(), AlarmService.class);
startService(intentAlarmService2);
By the way, my AlarmService.class is working for sure.
Thanks in advance.
First, get rid of getApplicationContext(). You almost never need it and it is frequently the wrong choice. Replace it with this, since whatever you are calling getApplicationContext() on is a Context.
In the code you have listed, you never raise a Notification. Hence, it is difficult to help you figure out why you are getting more than one. Calling cancelAll() on the NotificationManager should get rid of all outstanding notifications from your application.
My best guess is that onDestroy() is not being called on your service. That would occur if something else is keeping the service in memory (e.g., you have an active bound connection to it via bindService()). Or, possibly, you have something strange in your <service> element in the manifest (e.g., an unnecessary android:process attribute) that is fouling up the NotificationManager cancel() operation.
You need to make sure you are always referencing the same instance of your NotificationManager. Different instances will produce different notifications. I'd recommend using a service to manage notifications.
http://developer.android.com/guide/topics/fundamentals.html

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