Search code examples
androidalarmmanagerandroid-alarms

Android Alarm setExactAndAllowWhileIdle only triggers once


I'm using the setExactAndAllowWhileIdle as follows:

notificationAlarmMgr?.setExactAndAllowWhileIdle(
    AlarmManager.RTC_WAKEUP,
    notificationCalendar.timeInMillis,
    notificationAlarmIntent
)

Before I used setInexactRepeating and I was able to fire the alarm once everyday:

notificationAlarmMgr?.setInexactRepeating(
    AlarmManager.RTC_WAKEUP,
    notificationCalendar.timeInMillis,
    AlarmManager.INTERVAL_DAY,
    notificationAlarmIntent
)

setExactAndAllowWhileIdle doesn't have a intervalMillis parameter like setInexactRepeating does, so I can't pass AlarmManager.INTERVAL_DAY as an argument so that the alarm goes off everyday.

How do I make my alarm go off once everyday using setExactAndAllowWhileIdle?

Update 1:

More code for sending the alarm:

notificationAlarmIntent = Intent(
    context,
    NotificationAlarmReceiver::class.java
).let { intent ->
    PendingIntent.getBroadcast(
        context,
        NOTIFICATION_ID,
        intent,
        PendingIntent.FLAG_IMMUTABLE
    )
}

The BroadcastReceiver() for the notification:

@RequiresApi(Build.VERSION_CODES.O)
class NotificationAlarmReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        val i = Intent(context, SplashActivity::class.java).apply {
            flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
        }

        val pendingIntent = PendingIntent.getActivity(
            context,
            0,
            i,
            PendingIntent.FLAG_IMMUTABLE
        )

        val builder = NotificationCompat.Builder(context, Constants.NOTIFICATION_CHANNEL_ID)
            .setSmallIcon(R.drawable.ic_notification_icon)
            .setColor(ContextCompat.getColor(context, R.color.colorPrimary))
            .setContentTitle(context.getString(R.string.notification))
            .setContentText(NOTIFICATION_CONTEXT_TEXT)
            .setPriority(NotificationCompat.PRIORITY_DEFAULT)
            .setStyle(NotificationCompat.BigTextStyle().bigText(NOTIFICATION_CONTEXT_TEXT))
            .setContentIntent(pendingIntent)
            .setAutoCancel(true)

        with(NotificationManagerCompat.from(context)) {
            notify(Constants.NOTIFICATION_NOTIFICATION_ID, builder.build())
        }
    }

    companion object {
        private val NOTIFICATION_CONTEXT_TEXT: String by lazy { "Lorem ipsum" }
    }
}

Solution

  • As part of the other work being done by whatever notificationAlarmIntent invokes, have it also call setExactAndAllowWhileIdle() for the next time you want to get control.

    For example, you can put your setExactAndAllowWhileIdle() code in a utility function:

    fun setTheCottonPickinAlarm(context: Context, whenDoesItGoOff: Calendar) {
        val alarmManager = context.getSystemService(AlarmManager::class.java)
    
        val notificationAlarmIntent = Intent(
            context,
            NotificationAlarmReceiver::class.java
        ).let { intent ->
            PendingIntent.getBroadcast(
                context,
                NOTIFICATION_ID,
                intent,
                PendingIntent.FLAG_IMMUTABLE
            )
        }
    
        alarmManager?.setExactAndAllowWhileIdle(
            AlarmManager.RTC_WAKEUP,
            whenDoesItGoOff.timeInMillis,
            notificationAlarmIntent
        )
    }
    

    Then, in addition to wherever you are calling the existing AlarmManager code, you can call it from onReceive() of your NotificationAlarmReceiver:

    @RequiresApi(Build.VERSION_CODES.O)
    class NotificationAlarmReceiver : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
            val i = Intent(context, SplashActivity::class.java).apply {
                flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
            }
    
            val pendingIntent = PendingIntent.getActivity(
                context,
                0,
                i,
                PendingIntent.FLAG_IMMUTABLE
            )
    
            val builder = NotificationCompat.Builder(context, Constants.NOTIFICATION_CHANNEL_ID)
                .setSmallIcon(R.drawable.ic_notification_icon)
                .setColor(ContextCompat.getColor(context, R.color.colorPrimary))
                .setContentTitle(context.getString(R.string.notification))
                .setContentText(NOTIFICATION_CONTEXT_TEXT)
                .setPriority(NotificationCompat.PRIORITY_DEFAULT)
                .setStyle(NotificationCompat.BigTextStyle().bigText(NOTIFICATION_CONTEXT_TEXT))
                .setContentIntent(pendingIntent)
                .setAutoCancel(true)
    
            with(NotificationManagerCompat.from(context)) {
                notify(Constants.NOTIFICATION_NOTIFICATION_ID, builder.build())
            }
    
            setTheCottonPickinAlarm(context, calculateTheNextAlarmTime())
        }
    
        companion object {
            private val NOTIFICATION_CONTEXT_TEXT: String by lazy { "Lorem ipsum" }
        }
    }
    

    (where you would supply the calculateTheNextAlarmTime() implementation, or add that logic to setTheCottonPickinAlarm() itself)