I want to set notification for every day, at user-selected hours and minutes, but the issue is that some times it gets notification on time and some time it delays with 10, 20, 30, 40 seconds delay, and also check that today time is passed or not; if it is, I don't want to show notification
Function to set Notification
private fun scheduleNotification(context: Context, hour: Int, minute: Int) {
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
val intent = Intent(context, AlarmReceiver::class.java)
val pendingIntent = PendingIntent.getBroadcast(
context,
0,
intent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
val calendar = Calendar.getInstance().apply {
timeInMillis = System.currentTimeMillis()
set(Calendar.HOUR_OF_DAY, hour)
set(Calendar.MINUTE, minute)
set(Calendar.SECOND, 0)
set(Calendar.MILLISECOND, 0)
if (before(Calendar.getInstance())) {
add(Calendar.DATE, 1)
}
}
alarmManager.cancel(pendingIntent)
alarmManager.setExact(
AlarmManager.RTC_WAKEUP,
calendar.timeInMillis,
pendingIntent
)
alarmManager.setRepeating(
AlarmManager.RTC_WAKEUP,
calendar.timeInMillis,
AlarmManager.INTERVAL_DAY,
pendingIntent
)
}
private fun createNotificationChannel(context: Context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val name = "Workout Reminder Channel"
val descriptionText = "Channel for workout reminders"
val importance = NotificationManager.IMPORTANCE_HIGH
val channel = NotificationChannel(CHANNEL_ID, name, importance).apply {
description = descriptionText
}
val notificationManager: NotificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(channel)
}
}
companion object {
const val CHANNEL_ID = "workout_reminder_channel"
}
BroadcastReceiver
class AlarmReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val today = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
LocalDate.now()
} else {
TODO("VERSION.SDK_INT < O")
}
val mainIntent = Intent(context, MainActivity::class.java)
mainIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
val pendingIntent = PendingIntent.getActivity(
context,
0,
mainIntent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val notification = NotificationCompat.Builder(context, ProfileViewModel.CHANNEL_ID)
.setSmallIcon(R.drawable.img_fitness1)
.setContentTitle("Workout Reminder")
.setContentText("It's time for your workout! Time : ${formatDate(today)}")
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setContentIntent(pendingIntent)
.setAutoCancel(true)
.build()
notificationManager.notify(NOTIFICATION_ID, notification)
}
companion object {
const val NOTIFICATION_ID = 1
}
}
Please have a look at the official documentation about scheduling alarms.
In Android, you have two types of alarms:
If we inspect the documentation of setRepeated
, we find the following piece of information:
Note: as of API 19, all repeating alarms are inexact. If your application needs precise delivery times then it must use one-time exact alarms, rescheduling each time as described above. Legacy applications whose targetSdkVersion is earlier than API 19 will continue to have all of their alarms, including repeating alarms, treated as exact.
So depending on which device APIs you want to support, you will need to use a two-layered approach:
setRepeated
, and it will be exact.setExact()
(respects battery saving mode), setExactAndAllowWhileIdle()
(ignores battery saving mode), or setAlarmClock()
(highest priority).In your case, if the notification should appear at a user-sepcified time, you might want to use setAlarmClock
. You then however need to schedule each next alarm in your code.
See also this stackoverflow question for reference.