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:
I did my research and found out that Chinese ROMs aggressively restrict Services and Jobs, but it seems like AlarmManager
should work regardless.
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