0

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.

Moustafa EL-Saghier
  • 1,721
  • 1
  • 13
  • 43
  • 1
    setExact will not trigger in doze mode try to use [setExactAllowWhileIdle()](https://developer.android.com/reference/android/app/AlarmManager#setExactAndAllowWhileIdle(int,%20long,%20android.app.PendingIntent)) – Bhavin Sep 23 '21 at 09:28
  • okay, I'll try this but, is that related to broadcast get called once app opened? – Moustafa EL-Saghier Sep 23 '21 at 09:32
  • no! when we require to trigger alarm while device is in doze/idle mode, we can use setExactAllowWhileIdle() to trigger alarm at exact time. while setExact will differ time to next maintenance window. – Bhavin Sep 23 '21 at 09:36
  • okay, i got it, i'll try it, ty – Moustafa EL-Saghier Sep 23 '21 at 09:40
  • still the same issue happening, if i put alarm with long time for example 30 min ahead it's not triggered and have to open the app to trigger all missing broadcast recievers. – Moustafa EL-Saghier Sep 23 '21 at 14:44
  • can you update the code for what changes have you made with intent you're passing with getBroadcast() method? and also instead of using 0 in getBroadcast() method try to put PendingIntent.FLAG_UPDATE_CURRENT. – Bhavin Sep 23 '21 at 17:00
  • can you tell me what happen when i put 0 value? i found all tutorials doing that. – Moustafa EL-Saghier Sep 23 '21 at 17:26
  • can it be related to battery optimization from different manufacture like xiaomi? – Moustafa EL-Saghier Sep 23 '21 at 17:27
  • as far as i have tested in xiaomi devices alarm are being perfectly triggered if you want to check your device alarms you should use **adb shell dumpsys alarm** command in your terminal so you can find out if alarm exists or not. [how to read alarm](https://stackoverflow.com/a/31600886/10429259) – Bhavin Sep 23 '21 at 18:42
  • it's exist but, not triggered I've to open the application to get them all triggered again. i've removed app from battery optimization and it seems working now, it's in testing lvl. – Moustafa EL-Saghier Sep 23 '21 at 20:16
  • sorry but i don't know about when you put 0 value in getBroadcast() cause i never tried that. but update flag will update the PendingIntent with new Intent which you're providing and if older one doesn't exists it will create new one. and also for the battery optimization rather than trying to opt-out from it try something else or it will create bad user experience. and for the third question for different manufacture's optimization i am not sure of it but it can be the case. – Bhavin Sep 25 '21 at 06:40
  • @Bhavin thanks too much bro, I'll use the flag you mentioned, regarding my issue it's because of battery optimization and it's too bad user experience, i don't know even how to tell users to open settings to remove it from battery optimization, – Moustafa EL-Saghier Sep 25 '21 at 11:32
  • @Bhavin from a tutorial I found that 0 indicates that the system will use its default way to handle the creation of PendingIntent. – Moustafa EL-Saghier Sep 25 '21 at 12:43
  • 1
    okay! thanks i don't know about that. thanks for updating me about that. – Bhavin Sep 25 '21 at 12:59
  • it's okay bro, we born to learn ;) – Moustafa EL-Saghier Sep 25 '21 at 13:06

1 Answers1

0

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.

Moustafa EL-Saghier
  • 1,721
  • 1
  • 13
  • 43