Oguz Uncu
Oguz Uncu

Reputation: 113

Missing notifications using AlarmManager

I am facing a problem when scheduling multiple (up to 14) notifications using the AlarmManager. The problem is that the notifications are randomly missing (especially when the debug cable isn’t plugged). For example the first three notifications are triggered and after these notifications none of the scheduled notifications get triggered anymore, until you reschedule the notifications. It seems it’s because of the amount of notifications, but I am not 100% sure.

Below you can find my functions for scheduling notifications (alarm and bedtime notifications);

private fun createNotificationChannel(name: String, descriptionText: String, id: String, setSound:Boolean)
  {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
    {
      val importance = NotificationManager.IMPORTANCE_DEFAULT
      val channel = NotificationChannel(id, name, importance).apply {
        description = descriptionText
      }
      val audioAttributes = AudioAttributes.Builder()
        .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
        .setUsage(AudioAttributes.USAGE_NOTIFICATION)
        .build()
      if (setSound)
      {
        channel.setSound(Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + m_context.packageName + "/" + m_ringtone), audioAttributes)
      }
      (m_context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager).createNotificationChannel(channel)
    }
  }

  private fun createBedTimeNotificationChannel()
  {
    createNotificationChannel(BEDTIME_NOTIFICATION_CHANNEL_NAME, BEDTIME_NOTIFICATION_CHANNEL_DESCRIPTION, BEDTIME_NOTIFICATION_CHANNEL, false)
  }

  private fun createWakeUpNotificationChannel()
  {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
    {
      for (channel in (m_context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager).notificationChannels)
      {
        if (channel.id.contains(WAKE_UP_ALARM_NOTIFICATION_CHANNEL))
        {
          (m_context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager).deleteNotificationChannel(channel.id)
        }
      }
      m_randomNumber = Random().nextLong()
      createNotificationChannel(WAKE_UP_NOTIFICATION_CHANNEL_NAME, WAKE_UP_NOTIFICATION_CHANNEL_DESCRIPTION, WAKE_UP_ALARM_NOTIFICATION_CHANNEL + m_randomNumber, true)
    }
  }

  fun configureAlarmNotification(ringtone: Int, alarmTime: Int, snooze: Boolean, days: BooleanArray)
  {
    m_ringtone = ringtone
    createWakeUpNotificationChannel()
    for ((index, alarmOn) in days.withIndex())
    {
      if (alarmOn)
      {
        val builder = getWakeUpAlarmBuilder()

        val intent = Intent(m_context, CMainActivity::class.java)
        val activity = PendingIntent.getActivity(m_context, WAKE_UP_NOTIFICATION_ID_START + index + 1, intent, PendingIntent.FLAG_UPDATE_CURRENT)
        builder.setContentIntent(activity)

        if (snooze)
        {
          val snoozeIntent = Intent(m_context, CSleepPlannerService::class.java).apply {
            putExtra(ACTION, ACTION_SNOOZE)
            putExtra(NOTIFICATION_ID, WAKE_UP_NOTIFICATION_ID_START + index + 1)
            putExtra(NOTIFICATION, builder.build())
          }

          val snoozePendingIntent: PendingIntent = PendingIntent.getBroadcast(m_context, SNOOZE_PRESSED_NOTIFICATION_ID + index + 1, snoozeIntent, 0)
          builder.addAction(R.drawable.alarm_bel_icon, m_context.getString(R.string.snooze), snoozePendingIntent)
        }

        val notification = builder.build()
        val calendar = Calendar.getInstance()
        val currentTimeInMillis = calendar.timeInMillis

        calendar[Calendar.DAY_OF_WEEK] = convertWeekday(index)
        calendar[Calendar.HOUR_OF_DAY] = alarmTime / 60
        calendar[Calendar.MINUTE] = alarmTime % 60
        calendar[Calendar.SECOND] = 0

        if (calendar.timeInMillis <= currentTimeInMillis)
        {
          calendar.timeInMillis += NUMBER_OF_MILLIS_IN_WEEK
        }

        val pendingIntent = getPendingIntent(calendar.timeInMillis, WAKE_UP_NOTIFICATION_ID_START + index + 1, notification, ACTION_NOTIFICATION, PendingIntent.FLAG_UPDATE_CURRENT)
        (m_context.getSystemService(ALARM_SERVICE) as AlarmManager).setExact(AlarmManager.RTC_WAKEUP, calendar.timeInMillis, pendingIntent)
      }
    }

    val calendar = Calendar.getInstance()
    m_alarmWithSoundEnabledWithinADay.postValue((m_preferences.isAlarmWithSoundEnabled() && isAlarmEnabled(calendar[Calendar.HOUR_OF_DAY] * 60 + calendar[Calendar.MINUTE], m_preferences.getAlarmTime(), calendar[Calendar.DAY_OF_WEEK])))
  }

  fun configureBedTimeNotification(bedTime: Int, bedTimeSetting: Int, days: BooleanArray)
  {
    for ((index, alarmOn) in days.withIndex())
    {
      if (alarmOn)
      {
          val builder = NotificationCompat.Builder(m_context, BEDTIME_NOTIFICATION_CHANNEL).apply {
            setContentTitle(m_context.getString(R.string.bedtime_notification_tile))
            when (BedTimeNotificationSetting_e.fromInt(bedTimeSetting))
            {
              BedTimeNotificationSetting_e.AT_BED_TIME -> setContentText(m_context.getString(R.string.bedtime_notification_at_bedtime))
              BedTimeNotificationSetting_e.TEN_MINUTES_BEFORE -> setContentText(m_context.getString(
                  R.string.bedtime_notification_description_ten
                ))
              BedTimeNotificationSetting_e.THIRTY_MINUTES_BEFORE -> setContentText(m_context.getString(R.string.bedtime_notification_description_thirty))
              BedTimeNotificationSetting_e.FORTYFIVE_MINUTES_BEFORE -> setContentText(m_context.getString(R.string.bedtime_notification_description_fortyfive))
              BedTimeNotificationSetting_e.SIXTY_MINUTES_BEFORE -> setContentText(m_context.getString(R.string.bedtime_notification_description_sixty))
            }
            setAutoCancel(true)
            setSmallIcon(R.drawable.alarm_icon)
            priority = NotificationCompat.PRIORITY_HIGH
          }

        val intent = Intent(m_context, CMainActivity::class.java)
        val activity = PendingIntent.getActivity(m_context, BEDTIME_NOTIFICATION_ID_START + index + 1, intent, PendingIntent.FLAG_UPDATE_CURRENT)
        builder.setContentIntent(activity)
        val notification = builder.build()

        val calendar = Calendar.getInstance()
        val currentTimeInMillis = calendar.timeInMillis

        calendar[Calendar.DAY_OF_WEEK] = convertWeekday(index)
        calendar[Calendar.HOUR_OF_DAY] = bedTime / 60
        calendar[Calendar.MINUTE] = bedTime % 60
        calendar[Calendar.SECOND] = 0

        var newBedTime = calendar.timeInMillis - (bedTimeSetting * 60 * 1000)

        if (newBedTime <= currentTimeInMillis)
        {
          newBedTime += NUMBER_OF_MILLIS_IN_WEEK
        }

        val pendingIntent = getPendingIntent(newBedTime, BEDTIME_NOTIFICATION_ID_START + index + 1, notification, ACTION_NOTIFICATION, PendingIntent.FLAG_UPDATE_CURRENT)
        (m_context.getSystemService(ALARM_SERVICE) as AlarmManager).setExact(AlarmManager.RTC_WAKEUP, newBedTime, pendingIntent)
      }
    }
  }

  private fun getPendingIntent(calendarTime: Long = 0, notificationId: Int, notification: Notification? = null, action: String, flag: Int) : PendingIntent?
  {
    val notificationIntent = Intent(m_context, this::class.java)
    notificationIntent.putExtra(ACTION, action)
    notificationIntent.putExtra(NOTIFICATION_ID, notificationId)
    notificationIntent.putExtra(NOTIFICATION, notification)
    notificationIntent.putExtra(ALARM_TIME, calendarTime)
    return PendingIntent.getBroadcast(m_context, notificationId, notificationIntent, flag)
  }

What could be the cause of this?

Upvotes: 1

Views: 83

Answers (2)

Oguz Uncu
Oguz Uncu

Reputation: 113

I fixed the issue for setting the wakeup notifications by using the function setAlarmClock from AlarmManager.

Upvotes: 0

Basile Perrenoud
Basile Perrenoud

Reputation: 4112

It is difficult to know exactly what could be wrong. Depending on the version of the system, Android might decide to wait / delay / group notifications. The amount of battery left also impact the decision to show a notif.

I see that you use setPriority on the notification builder with a HIGH value. This should work for android < api 26. For newer versions, it is the setImportance in the notification channel that will be taken in account

see https://developer.android.com/reference/androidx/core/app/NotificationCompat.Builder?hl=en#setPriority(int)

  1. In the creation of the channel, try using High importance

val importance = NotificationManager.IMPORTANCE_HIGH // edit your code like this in createNotificationChannel, you currently use DEFAULT

  1. make sure that there is no option like battery saver or whatever in the phone you use to test.

Upvotes: 1

Related Questions