Primata Lógico
Primata Lógico

Reputation: 745

Preserve set schedule/date in AlarmManager but stop repeat

This code sets an alarm to fire at a specified day & time and repeats every 20 minutes. Can I stop the repeat (when the user stops it or when it successfully performs a job) but keep the alarm set without having to reinitialize ?

Example, the following sets an alarm to fire at 8:30 am & repeats every 20 min after, if the user stops the alarm, I would like the alarm to fire the next day at 8:30 again

private AlarmManager alarmMgr;
private PendingIntent alarmIntent;
...
alarmMgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, AlarmListener.class);
alarmIntent = PendingIntent.getBroadcast(context, AlarmListener.REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT);

// Set the alarm to start at 8:30 am
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, 8);
calendar.set(Calendar.MINUTE, 30);

// 20 minutes repeating.
alarmMgr.setInexactRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
        60 * 1000 * 20, alarmIntent);

A use case of this would be a monthly reminder where you set an Alarm for the 25th of every month. On the 25th, your Alarm would fire and repeat every 20 min but when you stop it, it should keep the schedule of firing an alarm on the 25th !

Also, any optimizations, notices or warnings regarding using AlarmManager would be highly appreciated.

Upvotes: 1

Views: 407

Answers (2)

David Wasser
David Wasser

Reputation: 95636

Of course this is possible. When you want to reschedule the alarm to start at 8:30 am the next day just do this:

// Set the alarm to start at 8:30 am next day
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, 8);
calendar.set(Calendar.MINUTE, 30);
calendar.add(Calendar.DAY, 1);

// 20 minutes repeating.
alarmMgr.setInexactRepeating(AlarmManager.RTC_WAKEUP, 
        calendar.getTimeInMillis(), 60 * 1000 * 20, alarmIntent);

This will replace your repeating alarm with an alarm that goes off tomorrow at 8:30am and then repeats every 20 mins after that.

Upvotes: 1

Ticherhaz FreePalestine
Ticherhaz FreePalestine

Reputation: 2387

Yes you can achieve that. My suggestion is your alarm manager, you set it to setExact and you also need to check if the calendar is today or not like below:

 //check whether the time is earlier than current time. If so, set it to tomorrow. Otherwise, all alarms for earlier time will fire
 if (calendar.before(now)) {
     calendar.add(Calendar.DATE, 1)
 }
 val timeTrigger = calendar.timeInMillis

 if (Build.VERSION.SDK_INT >= 23) {
     alarmManagerActivity.setExactAndAllowWhileIdle(
         AlarmManager.RTC_WAKEUP,
         timeTrigger, //timeTrigger is Systemtimeinmillis
         pendingIntentActivity
      )
  } else if (Build.VERSION.SDK_INT >= 19) {
         alarmManagerActivity.setExact(
         AlarmManager.RTC_WAKEUP,
         timeTrigger,
         pendingIntentActivity
     )
  } else {
       alarmManagerActivity[AlarmManager.RTC_WAKEUP, timeTrigger] = pendingIntentActivity
  }

Separate the method AlarmManager for 8:30PM with the AlarmManager repeat for 20minutes. Make sure you use the same BroadcastReceiver so it will just trigger here for both of them. See example at below:

fun alarmManager20Minutes(context: Context) {
    val calendar = Calendar.getInstance()
    calendar[Calendar.HOUR_OF_DAY] = 0
    calendar[Calendar.MINUTE] = 0
    calendar[Calendar.SECOND] = 0
    val intent = Intent(context, BroadcastReceiverHealthDeclaration::class.java)
    val timeTrigger = System.currentTimeMillis() + 20 * 60 * 1000 //20 mins
    val pendingIntentActivity = PendingIntent.getBroadcast(
        context,
        ALARM_MANAGER_ID_20_MINUTES,
        intent,
        PendingIntent.FLAG_UPDATE_CURRENT
    )
    val alarmManagerActivity = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
    if (Build.VERSION.SDK_INT >= 23) {
        alarmManagerActivity.setExactAndAllowWhileIdle(
            AlarmManager.RTC_WAKEUP,
            timeTrigger,
            pendingIntentActivity
        )
    } else if (Build.VERSION.SDK_INT >= 19) {
        alarmManagerActivity.setExact(
            AlarmManager.RTC_WAKEUP,
            timeTrigger,
            pendingIntentActivity
        )
    } else {
        alarmManagerActivity[AlarmManager.RTC_WAKEUP, timeTrigger] = pendingIntentActivity
    }
}

Next, at your BroadcastReceiver, onReceive, once it trigger 8.30AM for today, you need to call the your Alarm Manager method again and also don't forget to call alarmManager20Minutes().

override fun onReceive(context: Context?, intent: Intent?) {
    //Check if user want to receive the daily or not
    val receiveNotification = QuickSave.instance.getBoolean(
        Constant.RECEIVE_NOTIFICATION,
        true
    )
    if (receiveNotification) {
        AlarmManagerUtil.alarmManager20Minutes(context)
        //And then call method for 8:30AM
        AlarmManagerUtil.alarmManager830AM(context)
    }
}

So, when onReceive triggered, so you call method alarmManager20minutes. It will call again onReceive for next 20minutes and also it will set the alarmManager830AM for next day.

Upvotes: 1

Related Questions