How to correctly cancel an AlarmClock alarm set by AlarmManager in the Clock app?

Viewed 511

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?

2 Answers

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.

Related