I have made an alarm manager to schedule some user defined events and it is working successfully
then I have made a setting screen (from preference activity) to let the user to change some setting like (days before alarm)
my question is If I schedule the event before event start date by 3 days, then the user change the days before from setting to one day only
then I schedule the event again before event start date by 1 day, is that mean the user will be notified twice
one before 3 days
one before 1 day
if that is true so how can I prevent that from happening
Thanks in Advance
Each alarm is accompanied by a PendingIntent which has a unique hash identifier. When using the same identifier, that alarm will overwrite the former as stated in the documentation:
http://developer.android.com/reference/android/app/AlarmManager.html#set(int, long, android.app.PendingIntent)
Schedule an alarm. Note: ...If there is already an alarm scheduled for the same IntentSender, it will first be canceled...
Note that PendingIntent are characterized by their parameters and identifiers.
Intent intent = new Intent(mContext, YourTarget.class);
// The hashcode works as an identifier here. By using the same, the alarm will be overwrriten
PendingIntent sender = PendingIntent.getBroadcast(mContext, hashCode, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
mAlarmManager.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), sender);
} else {
mAlarmManager.setExact(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), sender);
}
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.
So you can call cancel on your pending intent alarmManager.cancel(myPendingIntent)
and the create a new one with a new time.
But you dont have to call cancel explicitly because as long as filterEquals returns true when comparing your new PendingIntent with the previous one then your alarm will start only 1 day before.
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.
Related
I'm trying to create a function in my App, which notifies the user at the expiration day of his rented books. I'll work with checkboxes in a listview, as below:
(Dates are for show purposes only)
Now i'm wondering how can i do it the best way. I'm having experiences with AlarmManager and BroadcastReceivers, but I didn't get a clear flowchart yet.
Thats because I need to set an specific alarm to each book and cancel that specific alarm when requested. Also, it needs to reactivate all Alarms when device is restared (by calling BOOT_COMPLETE broadcast).
PS.: Alarms will usually be set to one week after current date.
PS2.: Can I use Calendar to do it? I mean, this way i wouldn't have to reactivate all alarms, or calculate (expirationDate - currentDate) in millis.
Can someone, who has an idea, try to show me the way? Thanks!
I think the key would be to give each and every book its own alarm id as soon as you set the alarm for this book for the first time.
Then you should keep a list of the running alarm ids and timestamps (maybe in SharedPreferences).
With a method like this you can cancel a specific alarm with regards to its alarm id:
public static void cancelAlarm(Context context, int alarmId) {
PendingIntent pi = PendingIntent.getService(context, alarmId,
new Intent(context, YourService.class),
PendingIntent.FLAG_NO_CREATE);
if(pi!=null) {
AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
am.cancel(pi);
}
}
When you receive the BOOT broadcast, you can get the list of alarm ids together with timestamps from SharedPreferences and start all the alarms with their respective alarm ids
in my application users are free to select time and date to set alarms .
What will happen if user choose exactly the same date and time for two alarms.
I am taking input from user(for date and time) and setting the alarm.
GregorianCalendar gc=new GregorianCalendar();
gc.set(2012, 1, 22, 11, 19,0);//values as given by the user
final Intent intent = new Intent(this, AlarmService.class);
gc.set(Calendar.AM_PM,0);
// final PendingIntent sender = PendingIntent.getBroadcast(this, 1, intent, PendingIntent.FLAG_UPDATE_CURRENT);
am.set(AlarmManager.RTC_WAKEUP, gc.getTimeInMillis(),PendingIntent.getBroadcast(this,1, intent, PendingIntent.FLAG_UPDATE_CURRENT));
I have used a broadcastreceiver to recieve the alarm broadcast.
How should I handle this situation. What should I prefer to do?
I want to know the tecchnical aspect as what happens in this situation.
AFAIK,it won't create any technical problem for alarms set for the same time.It will fire all alarms at the same time.
It depends upon you how you want your user to set alarm.If you want them to set same time for various alarms,it's not a problem.
But if you don't want them to repeat the alarm time once it is set for other,then you can store alarms' time in database and at the time of setting new one,you can check for the conflict of newly set alarm time with previously set alarms from database and reject if it is found same.
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.
When setting a service to go off at particular time, I use the AlarmManager system service.
Everything goes off without a problem, service is called and actions take place.
When the alarm time is reached, the service starts, and at this point I get the system time (System.currentTimeMillis()). I'm guessing this wont be the actual time the service start. Is there a way to get the time that was set for this PendingIntent?
ie
Set alarm for 9am.
DoStuffService starts at 9am.
DoStuffService knows it was supposed to start at 9am, and uses this value for future functions.
When you create an intent for your alarm, you could put extra data, including time of the alarm, into it like this:
Intent intent = new Intent("action name");
//put extra data into the intent:
intent.putExtra("alarm_time_hours", hours);
intent.putExtra("alarm_time_minutes", minutes);
PendingIntent sender = PendingIntent.getBroadcast(getApplicationContext(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), sender);
Then in your receiver or service you need to get this extra data from received intent. Use something like this:
Bundle bundle = intent.getExtras();
if(bundle.containsKey("alarm_time_hours")) {
int hours = bundle.getInt("alarm_time_hours");
}
if(bundle.containsKey("alarm_time_minutes")) {
int minutes = bundle.getInt("alarm_time_minutes");
}
Is there a way to get the time that was set for this PendingIntent?
No, sorry.
However, it should not be terribly difficult for you to determine it yourself. Following your example, if your service reports that it is now 09:00:02.36, you should be able to round down to determine that this is the 9am alarm.
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.