I am making an application that uses AlarmManager.setInexactRepeating() method, that takes a PendingIntent as a paramater.
I start this by calling my setAlarm() method
public void setRepeatingAlarm() {
Intent intent = new Intent(this, AlarmReceiver.class);
String url = getAssembledUrl();
intent.putExtra("url", url);
pendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), interval, pendingIntent);
}
and stop it by stopAlarm()
private void stopRepeatingAlarm() {
alarmManager.cancel(pendingIntent);
pendingIntent.cancel();
}
Works just fine. However when activity gets destroyed, and user decides to stop the alarm, obviously the pending intent is null, as it gets created in the setRepeatingAlarm() method that wasnt called during current activities life.
Whats the correct way to get around this?
I could be creating the pending intent in Activity.onCreate() and that would solve this problem, however I will not be able to start the alarm again as the pending intent got canceled and needs to be recreated again (i think, unless there is a way to check the intent was canceled that i dont know about)
Actually, as it turns out
PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
returns the same pending intent, if the intent is the same so, all i needed was
private void stopRepeatingAlarm() {
if(pendingIntent == null) {
pendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
alarmManager.cancel(pendingIntent);
}
Related
This question already has answers here, here and here. But they were not confirmed by OPs to be working, and in my case, the alarm set by the same PendingIntent doesn't get canceled. Is there a way to cancel an AlarmClock alarm?
#Override
protected void onHandleIntent(#Nullable Intent i) {
Intent i = new Intent(AlarmClock.ACTION_SET_ALARM);
i.putExtra(AlarmClock.EXTRA_SKIP_UI, true);
i.putExtra(AlarmClock.EXTRA_HOUR, 6);
i.putExtra(AlarmClock.EXTRA_MINUTES, 0);
i.putExtra(AlarmClock.EXTRA_MESSAGE, "Good Morning");
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
AlarmManager alarmMgr = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
PendingIntent alarmIntent = PendingIntent.getActivity(
this, 0, i, PendingIntent.FLAG_UPDATE_CURRENT);
alarmMgr.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime(),
alarmIntent);
Log.d(TAG, "Alarm set");
try {
Thread.sleep(15000);
alarmMgr.cancel(alarmIntent);
Log.i(TAG, "Alarm canceled");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
This code outputs as expected:
Alarm set
Alarm canceled
But it does not cancel the alarm that was set. What am I doing wrong?
So, I found that I needed to use ACTION_DISMISS_ALARM instead of ACTION_SET_ALARM. It lets you cancel already set alarms in the alarm clock.
The answers here, here, and here suggested cancelling the PendingIntent which didn't work for me and was not confirmed to be working for the question authors either.
The explanation for why cancelling the PendingIntent from AlarmManager doesn't work, I think, could be that a PendingIntent is there to let you do something or send the intent later on. Once you've already done it (already sent the intent) you can't undo it. For example, you can cancel a notification, but can't undo opening the app from the notification (the intent or action performed) as it's already done. Moreover, in my case, it was another app that I needed to unset the alarm for so maybe I shouldn't have expected to be able to undo that action.
So in order to dismiss or cancel the alarm, you need to send a new intent with the action ACTION_DISMISS_ALARM.
Replacing the try/catch block as follows sets and cancels the alarm correctly for me:
try {
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.M) {
return;
}
Thread.sleep(15000);
Intent ci = new Intent(AlarmClock.ACTION_DISMISS_ALARM);
ci.setData(i.getData());
alarmIntent = PendingIntent.getActivity(this, 0, ci, 0);
alarmMgr.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime(),
alarmIntent);
Log.i(TAG, "Alarm cancelled");
} catch (InterruptedException e) {
e.printStackTrace();
}
However, this only works for API level 23+ and doesn't let you SKIP_UI like in ACTION_SET_ALARM.
I had the same issue and I read those thread you mentioned in your question. Yes they don't work. That's how I managed to get it working:
Set Alarm:
public static void setAlarm(Context context, int requestCode, int hour, int minute){
AlarmManager alarmManager =( AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context//same context should be used when canceling the alarm
, AlarmReceiver.class);
intent.setAction("android.intent.action.NOTIFY");
//setting FLAG_CANCEL_CURRENT makes some problems. and doest allow the cancelAlarm to work properly
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 1001, intent, 0);
Calendar time = getTime(hour, minute);
//set Alarm for different API levels
if (Build.VERSION.SDK_INT >= 23){
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP,time.getTimeInMillis(),pendingIntent);
}
else{
alarmManager.set(AlarmManager.RTC_WAKEUP,time.getTimeInMillis(),pendingIntent);
}
Cancel Alarm:
public static void cancelAlarm(Context context, int requestCode){
AlarmManager alarmManager =( AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
//define the same intent and pending intent
Intent intent = new Intent(context//same activity should be used when canceling the alarm
, AlarmReceiver.class);
intent.setAction("android.intent.action.NOTIFY");
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 1001, intent, 0);
alarmManager.cancel(pendingIntent);
pendingIntent.cancel();
}
This works like a charm. The point is, for me, if I use the FLAG_CANCEL_CURRENT I couldn't cancel the alarm. When I deleted the flag and passed in 0 in my code, then I could successfully delete the alarm.
I am not sure about the other flags since I didn't try them. You can also try to change your flag or delete them.
I am trying to stop the alarmManager in the MainActivity from the onBackPressed() method in the Map activity. I have tried the code below but the alarmManager is not being stoped and still firing. How can I fix it?
Code in the MainActivity:
Intent intent = new Intent(MainActivity.this, GetLLRD.class);
intent.putExtra("json_data", json);
PendingIntent pendingIntent = PendingIntent.getService(
getApplicationContext(), 123, intent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarm = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Calendar cal = Calendar.getInstance();
alarm.setRepeating(AlarmManager.RTC_WAKEUP,
System.currentTimeMillis(), 20 * 1000, pendingIntent);
startService(intent);
Code in the Map Activity:
#Override
public void onBackPressed() {
Intent intent = new Intent(Map.this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getService(
getApplicationContext(), 123, intent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarm = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarm.cancel(pendingIntent);
}
u problem is u use two different classes for intent to create and stop alarm:
Intent intent = new Intent(context,
GetLLRD.class);
Intent intent = new Intent(context,
MainActivity.class);
/** as in source code - new intent constructor */
public Intent(Context packageContext, Class<?> cls) {
mComponent = new ComponentName(packageContext, cls);
}
if u want to check if u got the same pending intent as before you can try to use:
Intent.filterEquals(oherIntent);
to cancel alarm you have two options use flag or use the same intent on alarm:
PendingIntent.FLAG_CANCEL_CURRENT
& i advice to make pending intent as final - example:
/**
* create pending intent
*/
final PendingIntent pIntent(Intent alarmIntent) {
// Create a PendingIntent to be triggered when the alarm goes off
return PendingIntent.getBroadcast(getApplicationContext(), AlarmReceiver.REQUEST_CODE,
alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT);
}
/**
* cancel alarm
*/
public void cancelAlarm(Intent alarmIntent, Context context) {
try {
AlarmManager alarm = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
/** use flag cancel here */
PendingIntent pIntent = PendingIntent.getService(context, AlarmReceiver.REQUEST_CODE, alarmIntent, PendingIntent.FLAG_CANCEL_CURRENT);
/** cancel alarm */
alarm.cancel(pIntent);
} catch (Exception e) {
// handle exception here
}
}
why to make pending intent final ?
because to cancel alarm u need:
Create pending intent with the same id and appropriate intent FLAG.
(to get reference to current pending intent)
PendingIntent.getBroadcast(context, REQUEST_CODE, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
Cancel that pending intent.
PendingIntent.cancel();
Cancel the alarm using alarm manager.
AlarmManager.cancel(PendingIntent);
A PendingIntent itself is simply a reference to a token maintained by the system describing the original data used to retrieve it. This means that, even if its owning application's process is killed, the PendingIntent itself will remain usable from other processes that have been given it. If the creating application later re-retrieves the same kind of PendingIntent (same operation, same Intent action, data, categories, and components, and same flags), it will receive a PendingIntent representing the same token if that is still valid, and can thus call cancel() to remove it.
if you are using activity, use
PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(),
12345, intent,0);
My code
public class BackgroundIntentService extends IntentService {
public BackgroundIntentService() {
super("BackgroundIntentService");
}
#Override
protected void onHandleIntent(Intent intent) {
scheduleNextUpdate();
Log.w("Blabla", "asldad111");
Log.w("Blabla", "asldad");
Log.w("Blabla", "asldad");
Log.w("Blabla", "asldad555");
}
private void scheduleNextUpdate() {
Intent intent = new Intent(this, this.getClass());
PendingIntent pendingIntent =
PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
// The update frequency should often be user configurable. This is not.
AlarmManager alarm = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarm.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), 5000, pendingIntent);
}
}
To start the service
Intent serviceIntent = new Intent(this, BackgroundIntentService.class);
startService(serviceIntent);
In the MainActivity.
The problem is that I can see in the logcat those logs spamming, not every 5 seconds but twice a second or more.
Where I'm wrong?
The flag you are using PendingIntent.FLAG_UPDATE_CURRENT in
PendingIntent pendingIntent =
PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
should be PendingIntent.FLAG_CANCEL_CURRENT
as quoted on the developer docs
FLAG_CANCEL_CURRENT - Flag indicating that if the described PendingIntent already exists, the current one should be canceled before generating a new one.
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.
so in your case the current pending intent exists and the new one updates it and fires at the exact moment it is updating the existing pending since you have defined the trigger time to be at System.currentTimeMillis()
So what is happening is that the current Pending intent is firing up before the new pending intent updates it ..and once it does that works as per the alarm logic , after the 5000ms interval the pending intent is fired. So there is a race condition here with interleaved alarm triggers and updates via the pending intents.
I have a service which uses PendinIntent and AlarmManager to launch another activity after a fixed period of time.
Here is the relevant code of the service:
Calendar cal = Calendar.getInstance(); //Create a calendar
cal.add(Calendar.MINUTE, 1); //Add the set minutes to the alarm
Intent dialogIntent = new Intent(this, alarmRingLayout.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 1234, dialogIntent, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager am = (AlarmManager)getSystemService(Activity.ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), pendingIntent);
When the service starts, I also set up a notification which has the a button that can cancel the service incase the user does not want the activity to launch.
On click of the "cancel" button, stopService() is called:
stopService(new Intent(StopPowerNapAlarmService.this, PowerNapAlarmService.class));
onDestroy() has the following code which cancels the notification and calls stopSelf()
It also tries to cancel the PendingIntent and AlarmManager.
The problem is that the Activity opens up even after after onDestroy is called. I believe the PendingIntent and/or AlarmManager are not getting canceled.
Here is the code for onDestroy():
#Override
public void onDestroy() {
Toast.makeText(this, "Alarm Finished", Toast.LENGTH_SHORT).show();
CancelNotification(this, 0);
//Cancel the pending intent and AlarmManager
Intent myntent = new Intent(PowerNapAlarmService.this, PowerNapAlarmService.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this,
1234, myntent, PendingIntent.FLAG_UPDATE_CURRENT);
pendingIntent.cancel();
AlarmManager am = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
am.cancel(pendingIntent);
stopSelf();
}
What is going wrong over here?
What is going wrong over here?
Your Intent objects are different, so your PendingIntent objects are different. This, in turn, means that you are working with different alarms. The first Intent points to an alarmRingLayout.class activity. The second Intent points to a BroadcastReceiver oddly named PowerNapAlarmService.
If you want to cancel() the alarmRingLayout alarm, create an activity PendingIntent for alarmRingLayout, and use that with cancel().
Also, please get rid of stopSelf() in onDestroy(), as that is not needed and could conceivably cause problems.
I am trying to implement an alarm that would display a notification everyday at the same hour of the day.
Here is the function I'm calling in my activity:
private void restartNotify() {
AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
// Intent for our BroadcastReceiver
Intent intent = new Intent(this, AlarmReceiver.class);
// PendingIntent for AlarmManager
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT );
// In case we have already set up AlarmManager, we cancel.
am.cancel(pendingIntent);
am.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()+10000, pendingIntent);
}
And here is my broadcast receiver class
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
NotificationManager nm = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification = new Notification(R.drawable.icon_notif, context.getString(R.string.NotificationLaunchMssg), System.currentTimeMillis());
// This is intent we want to launch when user clicks on the notification.
Intent intentTL = new Intent(context, MyClass.class);
notification.setLatestEventInfo(context, context.getString(R.string.NotificationTitle), context.getString(R.string.NotificationBody),
PendingIntent.getActivity(context, 0, intentTL, PendingIntent.FLAG_CANCEL_CURRENT));
nm.notify(1, notification);
//Here we set next notification, in day interval
AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
am.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()+10000, pendingIntent);
}
}
As you can see in this code I am using a test value (+10000 milliseconds) because I am simply trying to trigger the alarm 10 seconds after my app has started. But it doesn't work, nothing is displayed.
I don't know if the alarm has a problem, or the notification, nothing is happening.
Do you have any idea why?
Thanks for your help
EDIT: after adding some test code in AlarmReceiver method, it turns out this code is never run. So I probably don't call it properly, what is wrong?
Do not use this approach try setInexactRepeating(...) or setRepeating(...) instead. Why are u giving extra work to the BroadcastReceiver for setting alarm every time it receives the intent.
here is a little code:
alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
0, 10000, pendingIntent);
// The pending intent will the same as yours. 10000 is the
// interval for between consecutive alarms
as azertiti mentioned in comments " By the time it's registered that time will already be in the past." so use 0 or System.currentTimeMillis().