Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AlarmManager doesn't work on MIUI (and who knows where else)

My application gives user an option to schedule daily notifications on a specific time. I use AlarmManager to achieve this behavior.

On a click of a button I execute the following code:

val datetimeToAlarm = Calendar.getInstance(Locale.getDefault())
                /*datetimeToAlarm.set(HOUR_OF_DAY, 21)
                datetimeToAlarm.set(MINUTE, 0)
                datetimeToAlarm.set(SECOND, 0)
                datetimeToAlarm.set(MILLISECOND, 0)*/

                NotificationHelper.createNotificationChannel(
                    requireContext(),
                    NotificationManagerCompat.IMPORTANCE_DEFAULT,
                    false,
                    weather,
                    "Daily weather notifications for ${weather.cityName}"
                )

                NotificationHelper.scheduleNotification(
                    requireContext(),
                    datetimeToAlarm,
                    weather
                )

For testing purposes I do not set the exact time it must fire at just yet.

The two methods of NotificationHelper class that I use above:

fun createNotificationChannel(
            context: Context,
            importance: Int,
            showBadge: Boolean,
            data: Weather,
            description: String
        ) {

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {

                // Format: $placeId-$cityName
                val channelId = "${data.placeId}-${data.cityName}"
                val channel = NotificationChannel(channelId, "Weather for ${data.cityName}", importance)
                channel.description = description
                channel.setShowBadge(showBadge)

                val notificationManager = context.getSystemService(NotificationManager::class.java)
                notificationManager.createNotificationChannel(channel)
                Log.v("Notifications", "Created channel $channelId")
            }
        }

fun scheduleNotification(context: Context, timeOfNotification: Calendar, data: Weather) {
            val intent = Intent(context, AlarmReceiver::class.java)
            intent.putExtra(EXTRA_TITLE, "Weather for ${data.cityName}")
            intent.putExtra(EXTRA_TEXT, "Temperature: ${data.daily[0].temperature.min.roundToInt()}°/${data.daily[0].temperature.max.roundToInt()}°")
            intent.putExtra(EXTRA_ID, data.id)
            intent.putExtra(EXTRA_PLACE_ID, data.placeId)
            intent.putExtra(EXTRA_CITY_NAME, data.cityName)
            val pending =
                PendingIntent.getBroadcast(context, data.id, intent, PendingIntent.FLAG_UPDATE_CURRENT)
            // Schedule notification
            val manager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
            val repeatInterval: Long = 1000 * 60 * 15 // 15 minutes
            manager.setRepeating(AlarmManager.RTC_WAKEUP, timeOfNotification.timeInMillis, repeatInterval, pending)
            // start time is in AM/PM format
            Log.v("Notifications", "Scheduled notification id ${data.id} with start at " +
                    "${SimpleDateFormat("yyyy-MM-dd hh:mm:ss", Locale.getDefault()).format(timeOfNotification.timeInMillis)} and interval " +
                    SimpleDateFormat("MM-dd hh:mm:ss", Locale.getDefault()).format(repeatInterval))
        }

Again, for testing purposes I set repeating interval to 15 minutes just to see if it works.

In my BroadcastReciever's onReceive method I just call createNotification of my NotificationHelper class

    override fun onReceive(context: Context, intent: Intent) {

        // Deliver the notification.
        Log.v("Notifications", "Receiver received call")
        if (intent.extras == null) return
        val title = intent.getStringExtra(NotificationHelper.EXTRA_TITLE)!!
        val text = intent.getStringExtra(NotificationHelper.EXTRA_TEXT)!!
        val id = intent.getIntExtra(NotificationHelper.EXTRA_ID, -1)
        val placeId = intent.getStringExtra(NotificationHelper.EXTRA_PLACE_ID)
        val cityName = intent.getStringExtra(NotificationHelper.EXTRA_CITY_NAME)
        NotificationHelper.createNotification(
            context,
            title,
            text,
            "",
            "$placeId-$cityName",
            id
        )
    }
        fun createNotification(
            context: Context,
            title: String,
            message: String,
            bigText: String,
            channelId: String,
            id: Int,
            autoCancel: Boolean = true
        ) {

            Log.v("Notifications", "Fired notification creation with following parameters: $title, $message, $channelId, $id")
            val notificationBuilder = NotificationCompat.Builder(context, channelId).apply {
                setSmallIcon(R.drawable.ic_launcher_foreground)
                setContentTitle(title)
                setContentText(message)
                setStyle(NotificationCompat.BigTextStyle().bigText(bigText))
                priority = NotificationCompat.PRIORITY_DEFAULT
                setAutoCancel(autoCancel)

                val intent = Intent(context, MainActivity::class.java)
                intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
                val pendingIntent = PendingIntent.getActivity(context, 0, intent, 0)
                setContentIntent(pendingIntent)
            }
            val notificationManager = NotificationManagerCompat.from(context)
            notificationManager.notify(id, notificationBuilder.build())

        }

And of course, my receiver is registered in the manifest:

        <receiver
            android:name=".utils.notifications.AlarmReceiver"
            android:enabled="true"
            android:exported="false"/>

When I launch my app on an emulator API 30 and click the button, I get the following logs:

2021-03-06 20:13:11.615 11229-11229/com.calamity.weather V/Notifications: Created channel location_place-Mountain View
2021-03-06 20:13:11.626 11229-11229/com.calamity.weather V/Notifications: Scheduled notification id 1 with start at 2021-03-06 08:13:11 and interval 01-01 03:15:00
2021-03-06 20:13:16.624 11229-11229/com.calamity.weather V/Notifications: Receiver received call
2021-03-06 20:13:16.624 11229-11229/com.calamity.weather V/Notifications: Fired notification creation with following parameters: Weather for Mountain View, Temperature: 9°/15°, location_place-Mountain View, 1

My notification is shown right away, and if I go and close my app, it will be delivered again after 15 minutes.

However, if I launch it on a real device (Xiaomi Redmi Note 9, MIUI Global 12.0.4, API 29), on a click of a button my logs are:

2021-03-06 20:16:50.945 19673-19673/com.calamity.weather V/Notifications: Created channel ChIJiQHsW0m3j4ARm69rRkrUF3w-Mountain View
2021-03-06 20:16:50.951 19673-19673/com.calamity.weather V/Notifications: Scheduled notification id 2 with start at 2021-03-06 08:16:50 and interval 01-01 03:15:00

As you can see, alarm is not fired even if my app is still in the foreground, not to mention if I swipe it out of recent apps list. So the questions are:

  • Why doesn't it fire even with the app still running?
  • How do I make it behave how it's supposed to and deliver my notifications even with closed app?

I did my research and found out that Chinese ROMs aggressively restrict Services and Jobs, but it seems like AlarmManager should work regardless.

like image 490
Calamity Avatar asked Mar 06 '21 17:03

Calamity


1 Answers

Hı Calamity I encountered same problem yesterday. Also I am using Redmi Note 9. After searching all net, I found that setRepeating and setInexactRepeating methods doesnt work. if you change manager.setRepeating(AlarmManager.RTC_WAKEUP, timeOfNotification.timeInMillis, repeatInterval, pending) to manager.setExact(AlarmManager.RTC_WAKEUP, timeOfNotification.timeInMillis, pending) you will see that your codes work. to work setRepeating and setInexactRepeating methods, you need to disable Battery optimization for your app after that open your app you will see that those methods will work. Also here my asked question android BroadcastReceiver doesn't initiliaze

like image 155
Abdullah Avatar answered Oct 12 '22 23:10

Abdullah