androidalarmmanagerandroid-alarmsrepeatingalarm

AlarmManager set as PM firing at AM time


The problem

I'm using Android's AlarmManager to fire a notification on my app periodically based on a time set by the user on a settings screen. I've followed the documentation to implement that. It's done like the snippet below:

fun setDailyAlarm(time: Calendar, id: Int) {
    val cal = Calendar.getInstance().apply {
        timeInMillis = System.currentTimeMillis()
        set(Calendar.HOUR_OF_DAY, time.get(Calendar.HOUR_OF_DAY))
        set(Calendar.MINUTE, time.get(Calendar.MINUTE))
    }
    val pi = PendingIntent.getBroadcast(context, id, intent, PendingIntent.FLAG_UPDATE_CURRENT)

    alarmManager.setRepeating(
        AlarmManager.RTC_WAKEUP,
        cal.timeInMillis,
        AlarmManager.INTERVAL_DAY,
        pi
    )
}

I'm also using the Material Dialogs library to invoke a time dialog, as follows:

MaterialDialog(this).show {
    // time here is a Calendar instance
    timePicker { _, time ->
        setDailyAlarm(time, ALARM_ID)
    }
}

The screen looks like the example below:

Settings Screen

The alarm itself is firing, but there's a weird bug happening.

Say that it's 9 PM and I set the alarm to 10 AM. I believe the expected behavior should be to fire the alarm the next day at 10 AM but once the clock reaches 10 PM sometimes I get a "false positive". (By sometimes I mean that this behavior is not even consistent. I managed to replicate it a few times, and my clients also complained about it).

Note: The app's minimum API level is 21.

What I've tried so far

At first, I thought the calendar object might've been getting the wrong AM/PM flag, or the Calendar.HOUR_OF_DAY property might've been wrong, but when I checked those values in the debugger everything seemed to be fine. So now I'm clueless to what the problem might be.

Is there something that I'm missing here?


Solution

  • I've found a solution to this problem.

    After doing some tests I noticed that the problem only happened when I set the time to something previous to the current hour. Then it occurred to me that perhaps the AlarmManager might automatically fire "old alarms" so that might've been why I was getting that false positive.

    Then I changed my setDailyAlarm() function to the snippet below:

    fun setDailyAlarm(time: Calendar, id: Int) {
        val now = Calendar.getInstance()
    
        val cal = Calendar.getInstance().apply {
            timeInMillis = System.currentTimeMillis()
            set(Calendar.HOUR_OF_DAY, time.get(Calendar.HOUR_OF_DAY))
            set(Calendar.MINUTE, time.get(Calendar.MINUTE))
        }
    
        if (time.get(Calendar.HOUR_OF_DAY) <= now.get(Calendar.HOUR_OF_DAY)) {
            cal.add(Calendar.DATE, 1)
        }
    
        val pi = PendingIntent.getBroadcast(context, id, intent, PendingIntent.FLAG_UPDATE_CURRENT)
    
        alarmManager.setRepeating(
            AlarmManager.RTC_WAKEUP,
            cal.timeInMillis,
            AlarmManager.INTERVAL_DAY,
            pi
        )
    }
    

    TL;DR: I'm checking whether or not the chosen time is before the current time, and if that's the case I add one day to the Calendar instance before creating the PendingIntent. It seems to have solved the issue since the alarm is not firing at the wrong time anymore.