I am developing an android-application which main usage is displaying notifications at a set time (some specific calendar app). One of the main complaints is that users do not (always) receive notifications, and I am at wits end.
We have internally tested the code below against Android 4.4, 6, 7.0, 7.1, 8.0, 8.1 on emulators and used about 10 real devices (6 to 8.1), and all devices received their notifications on time. Even across reboots, the notifications were all received on time.
One of the things we have ran into was the SecurityException on Samsung devices (>500 registered alarms), which we had previously triggered due to unproper cancelling. It looks like that no longer is an issue.
So, what could be the cause for these missing notifications? Is it a device specific setting, is it a simple bug? Or are there other factors at play here?
This is the code we are using:
private void cancelAlarm(String notificationId, Class<? extends AbstractReceiver> receiverClass)
throws BroadcastException {
/*
* Create an intent that looks similar, to the one that was registered using add. Making sure the notification id in the action is the same. Now we can search for
* such an intent using the 'getService' method and cancel it.
*/
final Intent intent = new Intent(this.context, receiverClass);
intent.setAction(notificationId);
final PendingIntent pi = PendingIntent.getBroadcast(this.context, 0, intent, 0);
final AlarmManager am = getAlarmManager();
try {
am.cancel(pi);
} catch (Exception e) {
Log.e(this.getClass().getSimpleName(), e.getMessage());
throw new BroadcastException(e.getMessage());
}
}
private void addOrUpdateAlarm(...){
try {
cancelAlarm(notificationId, OurReceiver.class);
} catch (BroadcastException e) {
Log.e(AlarmHelper.class.getSimpleName(), "addOrUpdateAlarm: Can't cancel current alarm before reinserting.", e);
}
Intent intent = new Intent(this.context, receiverClass);
intent.setAction(notificationId);
// some intent.setExtra() calls.
PendingIntent sender = PendingIntent.getBroadcast(this.context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
/* Get the AlarmManager service */
final AlarmManager am = getAlarmManager();
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
am.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), sender);
}else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
am.setExact(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), sender);
}else{
am.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), sender);
}
}
and then in OurReceiver we create a notificationchannel:
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationManager mNotificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
int importance = NotificationManager.IMPORTANCE_HIGH;
NotificationChannel mChannel = new NotificationChannel(id, name, importance);
// Configure the notification channel.
mChannel.setDescription(description);
mChannel.enableLights(true);
// Sets the notification light color for notifications posted to this
// channel, if the device supports this feature.
mChannel.setLightColor(Color.RED);
mChannel.enableVibration(true);
mChannel.setVibrationPattern(new long[]{100, 200, 300, 400, 500, 400, 300, 200, 400});
mNotificationManager.createNotificationChannel(mChannel);
}
and finally send a notification:
PendingIntent pIntent = PendingIntent.getActivity(context, (int) System.currentTimeMillis(), intent, 0);
Notification n = new NotificationCompat.Builder(context, channel)
.setContentTitle(notificationTitle)
.setContentText(notificationSubText)
.setSmallIcon(R.drawable.logo)
.setContentIntent(pIntent)
.setDefaults(Notification.DEFAULT_SOUND|Notification.DEFAULT_VIBRATE)
.setAutoCancel(true).build();
NotificationManager notificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0, n);