I'm trying to build module of reminder in my app to work as following, when user set alarm at exact time it fires a notification.
but, it fails for two cases:
1- when i set alarm time to next 2h and above
2- if i removed the app from task manger ( totally closed )
no notification is triggered but, when i open app for first time i receive all missing notifications at once.
I'm using alarm manger to set the alarm as following:
val manager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
val pendingIntent = PendingIntent.getBroadcast(context, requestCode, myIntent, 0)
when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> {
manager.setExactAndAllowWhileIdle(
AlarmManager.RTC_WAKEUP,
timeOnDayCalender.timeInMillis,
pendingIntent
)
}
else -> {
manager.setExact(
AlarmManager.RTC_WAKEUP,
timeOnDayCalender.timeInMillis,
pendingIntent
)
}
}
and receiving my alarm manger in broadcast receiver as following:
class AlarmBroadcast : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
val newIntent = Intent(context, AlarmService::class.java)
newIntent.type = intent?.type
newIntent.action = intent?.action
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context?.startForegroundService(newIntent)
} else {
context?.startService(newIntent)
}
}
}
and my AlarmService
class is:
class AlarmService : Service() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
// show foreground notification to let user there is action happening in background
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
showForegroundNotification()
else
startForeground(
1,
Notification()
)
handleAlarmRedirection(intent)
stopSelf(startId) // to destroy service after work is done & remove the fixed notification.
return START_STICKY
}
private fun showForegroundNotification() {
val channelId =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createNotificationChannel()
} else {
// If earlier version channel ID is not used
// https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context)
""
}
val notificationBuilder = NotificationCompat.Builder(this, channelId)
val notification = notificationBuilder.setOngoing(true)
.setSmallIcon(R.mipmap.ic_launcher)
.setPriority(NotificationCompat.PRIORITY_MIN)
.setContentTitle("title")
.setContentText("body")
.setCategory(Notification.CATEGORY_SERVICE)
.build()
startForeground(101, notification)
}
@RequiresApi(Build.VERSION_CODES.O)
private fun createNotificationChannel(): String {
val chan = NotificationChannel(
"my_service",
"My Background Service",
NotificationManager.IMPORTANCE_NONE
)
chan.lightColor = Color.BLUE
chan.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
val service = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
service.createNotificationChannel(chan)
return "my_service"
}
private fun handleAlarmRedirection(intent: Intent?) {
// logic for pushing the notification is here
}
override fun onDestroy() {
super.onDestroy()
stopForeground(true)
}
override fun onBind(intent: Intent?): IBinder? {
return null
}
}
I added the permission to manifest too:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
I hope if anyone have idea about this case.
if anyone have good tutorial or advice for best practice please let me know, I target android 31.
for those who are still stuck like me for this, it was because my phone was Xiaomi's battery optimization.
once allowed the app to work without battery optimization it worked normally as expected.
but, it's a bad user experience to ask users to do that, so, I'm looking for a better solution if I found I'll update the answer.