I will try to explain this as best as I can. Basically, I have Activity 1 that uses an ExternalClass to do various things. Activity 2 also references Activity 1's object of said ExternalClass. From both of these activities I can set alarms using the AlarmManager, but I want to be able to cancel all the alarms created from either activity, from Activity 1.
All alarms are set using the same intent and the same AlarmManger (both created in the ExternalClass), but when I click my button in Activity 1 that is supposed to call myAlarms.cancel(intent) it only cancels the alarms that were created using the Activity 1 class.
The ExternalClass is referenced in Activity 2 by referencing the object of that class that was created in Activity 1 so they should both be using the same instance of the ExternalClass. I'm pretty sure it isn't canceling the alarms because of the context that was used when setting the alarms, but I can't figure out how to get around that.
To solve this issue I used to following code:
timerAlarmIntent = PendingIntent.getBroadcast(myContext, i, alarmIntent, 0);
ArrayList<PendingIntent> intentArray = new ArrayList<PendingIntent>();
intentArray.add(timerAlarmIntent);
myAM.set(AlarmManager.RTC_WAKEUP, alarmTime, timerAlarmIntent);
I set the requestCode to a unique id. This is within a for loop and i represents 0, 1, 2...
To cancel the alarms I had to add each alarm to a list and loop through the list when I wanted to cancel all alarms.
private void cancelAlarms(){
if(intentArray.size()>0){
for(int i=0; i<intentArray.size(); i++){
myAM.cancel(intentArray.get(i));
}
intentArray.clear();
}
}
To cancel and alarm you need to pass an equivalent PendingIntent (meaning p1.equals(p2) returns true) to the one used to create it. It doesn't matter from where you created the AlarmManager reference. How are you initializing the PendingIntent in both cases?
Two PendingIntents are considered equal if they both represent the same operation from the
same package. Basically if you initialize two PendingIntents with equivalent Intents, they would be considered equal. EDIT: the documentation is obviously wrong about this, the requestCode is also used when comparing PendingIntents. See comments and other answer.
Related
I am developing an alarm application in Android. The flow is really simple, Im just creating a PendingIntent and then I call the setExact() method in the AlarmManager much like below.
Intent myIntent = new Intent(context, BroadcastReceiver.class);
pendingIntent = PendingIntent.getBroadcast(context, 0, myIntent, 0);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(ALARM_SERVICE);
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP,
System.currentTimeMillis() + difference, pendingIntent);
After some tests I realized that with the above snippet I can set only one alarm because I set the requestCode of the pendingIntent to zero, and if I set another alarm with the requestCode set to 0 then it will overwrite the previous one. Is there a way to fix this without changing the requestCode? I was thinking maybe the flags can help me but I didn't find a flag that doesn't overwrite the previous pendingIntent.
I know that the obvious solution is to change 0 to another int and then keep track of all my ints, picking one that is not used. That solution would be fine if I was just starting the project, however I am already in the middle and I use as request codes predefined Enums. It is very difficult to change this mechanic and keep track of individual ints thats why I am asking if there is a way of not overwriting a pendingIntent when a new one with the same requestCode is registered. Thank you in advance.
You can make the Intents unique be setting a different ACTION on each one. Then you could still use the same requestCode and would have different PendingIntents.
You'll need to keep track of the ACTIONs you use if you want to be able to cancel the alarms later.
I am asking if there exists a certain type of flag that will be able to differentiate them
There is not, and it makes sense because this is already the purpose of the requestCode parameter.
For information, these are your options regarding flags:
You will have to change your mechanism to make it possible to have different requestCodes for Pending Intents. It may be a lot of work, but it is what you have to do.
I am trying to cancel an alarm that was set last time my app was run. This alarm has a PendingIntent that was set with PendingIntent.getBroadcast and an inner Intent that contains some variables set by intent.putExtra. My question is this, I know that alarms can be canceled by calling alarmManager.cancel(pendingIntent) where pendingIntent is the same as the one used to set the alarm. But, if the variables placed into the intent are changed will the alarm still be canceled? For example, I set an alarm with intent.putExtra("Joe") where Joe is a contact name. Later my app is closed and when it is re-run I try and cancel the alarm for "Joe" but the user has changed the name of the contact to "Jones". Can I cancel the alarm without knowing the variables I put into the intent?
Thanks!
I think it should cancel the alaram anyway, even though some data is different. The cancel method says:
Any alarm, of any type, whose Intent matches this one (as defined by filterEquals(Intent)), will be canceled.
And filterEquals 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.
Anyhow, I'd still test it myself.
According to this question (which references the documentation), anything you add using putExtra is not taken into account when checking if an intent is equal to another one.
It shouldn't matter if the extra data is changed.
I register a broadcastreceiver in AndroidMainfest.xml
And in my app, a function is that User can set a time and at this time the app will send a notification. I get the arguments User set ,and use alarmManager to set a task which will happened at the time user set.
But I find that GOOGLE API said that:
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.
So if I want set two or more task,the Intent will be replaced , and at end I can only get one notification,it's not the result I want.
And then I found the intent was identified by action, data, type, class, and categories,
but I can't change action(the intent's action is the intent-filter's action was registred in the AndroidMainfest.xml ),but at the time that I change the other arguments I can't even receive a broadcast.
I thought there are four ways to solve this problem,but I only made one..
create lots of broadcastreceiver and register these in
AndroidMainfest.xml,and in this way I could change the intent's
action
register the broadcastreceiver in the program ,but I didn't
make it
use service + Timer class ..
To make two intent different without change action.
Any help will be appreciated!!
Intent intent = new Intent("aaa"); //there was a broadcastreceiver's intent-filter "aaa"
intent.putExtra("title", title);
intent.putExtra("table", "计划");
PendingIntent pi = PendingIntent.getBroadcast(Alarm.this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager am = (AlarmManager)getSystemService(Alarm.ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()+time, pi);
The code is in onClickListener{onClick(){}}
Instead of Broadcasting which is not letting you to set repeat alarm you can use the calendar Object and set the alarm at whatever desired time. Once the alarm gets ON, your code will run automatically and as many times you have set the alarm.
You can also take a help of the following tutorial. I am sure it will help you out somehow.
http://blog.mikesir87.io/2013/04/android-creating-an-alarm-with-alarmmanager/
I'm new in this whole Android environment and I usually have some doubts that maybe you can consider very basic knowledge and a bit stupid. I will try to do my best explaining the doubt I have and why i have it to make me understand.
I'm doing an application where you can set notifications to remind you the scholar classes you want. I have done a class that extends BroadcastReceiver so it can reset all the alarms after the device has booted. I have a database where I keep information about the alarms: the class, the time it has to be configured, etc. I retrieve all the alarms and set them to the alarmManager this way:
intent = new Intent(ctxt.getApplicationContext(), Notificacion.class);
intent.putExtra("TAG", tag);
intent.putExtra("SUBJECT", cursor2.getString(0));
intent.putExtra("AULA", cursor2.getString(1));
displayIntent = PendingIntent.getBroadcast(ctxt, Integer.parseInt(tag), intent, PendingIntent.FLAG_UPDATE_CURRENT );
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY*7, displayIntent);
Well, I guess this should work fine until here. The problem is that when you use the app and you want to set a notification, you are doing it from the class "Schedule.class" so the intent would have this context:
Intent intent = new Intent(getApplicationContext(), Notification.class);
PendingIntent pend = PendingIntent.getBroadcast(this, Integer.parseInt(tag), intent, PendingIntent.FLAG_UPDATE_CURRENT);
In the app, you can delete an alarm, and you have to call alarmManager.cancel(pend) in order to do that. So my doubt is if it will be able to cancel it.
If the contexts are different, it won't find the match with the pending intent, because it was set from the context I got in my extension of BroadCastReceiver (ctxt), and the alarm was set with the context I got from Schedule.class.
So.. is the application context always the same? I know that the context is set in order to give information to other classes about what has been going on, but I'm not sure if the Intent filter will differentiate where the context was given.
Thank you in advance!
Looking at the AlarmManager documentation for the cancel method you're using:
public void cancel (PendingIntent operation)
Added in API level 1
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.
So, the Intent.filterEquals documentation says the following:
public boolean filterEquals (Intent other)
Added in API level 1
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.
I can't think of any reason why the action, data, type, class, or category would be different from one explicit Intent to another (unless, obviously you went out of your way to change those things). The contexts do not appear to be in the criteria for the matching, so I think you can be fairly confident that it will be cancelled no matter which context was used to create it in the first place.
I am trying to create intents that will be set using alarmmanager. Currently, I can do this with one intent, add extra data to it (strings, but i send them as one string with a seperator), and everything works fine and goes off at the correct time. However, when I try to send multiple intents like this, they are overwritten and only one goes off at the correct time. How can i structure my intents so that they appear different to the alarmmanager (i think they are getting deleted when filterIntent() is run).
long story short- putExtra() makes all the intents look the same still... how can i make them look different so they wont get deleted (and keep track of them in case i want to delete a specific one)
This is how I scheduled updates for my widgets. Each one allowed to be unique because they have a unique widget id number.
Intent widgetUpdate = new Intent();
widgetUpdate.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
widgetUpdate.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] { appWidgetId });
widgetUpdate.putExtra(EXTRA_POSITION,0);
widgetUpdate.putExtra(EXTRA_URL, URL);
// make this pending intent unique by adding a scheme to it
widgetUpdate.setData(Uri.withAppendedPath(Uri.parse(URI_SCHEME + "://widget/id/"), String.valueOf(appWidgetId)));
PendingIntent newPending = PendingIntent.getBroadcast(context, 0, widgetUpdate, PendingIntent.FLAG_UPDATE_CURRENT);
// schedule the updating
AlarmManager alarms = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarms.setRepeating(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime(), updateRateSeconds * 1000, newPending);
According to the docs, AlarmManager checks if two Intents are equivalent using filterEquals(). Check out the docs for filterEquals() to see how it decides whether two Intents are equivalent. Also, chris324's solution is a pretty good one.