blob: 364a24be2c76e3c01f5adc785aa652ce25a38a61 [file] [log] [blame]
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.deskclock.alarms
import android.annotation.TargetApi
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.Service
import android.content.Context
import android.content.Intent
import android.content.res.Resources
import android.os.Build
import android.service.notification.StatusBarNotification
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.content.ContextCompat
import com.android.deskclock.AlarmClockFragment
import com.android.deskclock.AlarmUtils
import com.android.deskclock.DeskClock
import com.android.deskclock.LogUtils
import com.android.deskclock.provider.Alarm
import com.android.deskclock.provider.AlarmInstance
import com.android.deskclock.provider.ClockContract.InstancesColumns
import com.android.deskclock.R
import com.android.deskclock.Utils
import java.text.DateFormat
import java.text.SimpleDateFormat
import java.util.Locale
internal object AlarmNotifications {
const val EXTRA_NOTIFICATION_ID = "extra_notification_id"
/**
* Notification channel containing all low priority notifications.
*/
private const val ALARM_LOW_PRIORITY_NOTIFICATION_CHANNEL_ID = "alarmLowPriorityNotification"
/**
* Notification channel containing all high priority notifications.
*/
private const val ALARM_HIGH_PRIORITY_NOTIFICATION_CHANNEL_ID = "alarmHighPriorityNotification"
/**
* Notification channel containing all snooze notifications.
*/
private const val ALARM_SNOOZE_NOTIFICATION_CHANNEL_ID = "alarmSnoozeNotification"
/**
* Notification channel containing all missed notifications.
*/
private const val ALARM_MISSED_NOTIFICATION_CHANNEL_ID = "alarmMissedNotification"
/**
* Notification channel containing all alarm notifications.
*/
private const val ALARM_NOTIFICATION_CHANNEL_ID = "alarmNotification"
/**
* Formats times such that chronological order and lexicographical order agree.
*/
private val SORT_KEY_FORMAT: DateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US)
/**
* This value is coordinated with group ids from
* [com.android.deskclock.data.NotificationModel]
*/
private const val UPCOMING_GROUP_KEY = "1"
/**
* This value is coordinated with group ids from
* [com.android.deskclock.data.NotificationModel]
*/
private const val MISSED_GROUP_KEY = "4"
/**
* This value is coordinated with notification ids from
* [com.android.deskclock.data.NotificationModel]
*/
private const val ALARM_GROUP_NOTIFICATION_ID = Int.MAX_VALUE - 4
/**
* This value is coordinated with notification ids from
* [com.android.deskclock.data.NotificationModel]
*/
private const val ALARM_GROUP_MISSED_NOTIFICATION_ID = Int.MAX_VALUE - 5
/**
* This value is coordinated with notification ids from
* [com.android.deskclock.data.NotificationModel]
*/
private const val ALARM_FIRING_NOTIFICATION_ID = Int.MAX_VALUE - 7
@JvmStatic
@Synchronized
fun showLowPriorityNotification(
context: Context,
instance: AlarmInstance
) {
LogUtils.v("Displaying low priority notification for alarm instance: " + instance.mId)
val builder: NotificationCompat.Builder = NotificationCompat.Builder(
context, ALARM_LOW_PRIORITY_NOTIFICATION_CHANNEL_ID)
.setShowWhen(false)
.setContentTitle(context.getString(
R.string.alarm_alert_predismiss_title))
.setContentText(AlarmUtils.getAlarmText(
context, instance, true /* includeLabel */))
.setColor(ContextCompat.getColor(context, R.color.default_background))
.setSmallIcon(R.drawable.stat_notify_alarm)
.setAutoCancel(false)
.setSortKey(createSortKey(instance))
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setCategory(NotificationCompat.CATEGORY_EVENT)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setLocalOnly(true)
if (Utils.isNOrLater()) {
builder.setGroup(UPCOMING_GROUP_KEY)
}
// Setup up hide notification
val hideIntent: Intent = AlarmStateManager.createStateChangeIntent(context,
AlarmStateManager.ALARM_DELETE_TAG, instance,
InstancesColumns.HIDE_NOTIFICATION_STATE)
val id = instance.hashCode()
builder.setDeleteIntent(PendingIntent.getService(context, id,
hideIntent, PendingIntent.FLAG_UPDATE_CURRENT))
// Setup up dismiss action
val dismissIntent: Intent = AlarmStateManager.createStateChangeIntent(context,
AlarmStateManager.ALARM_DISMISS_TAG, instance, InstancesColumns.PREDISMISSED_STATE)
builder.addAction(R.drawable.ic_alarm_off_24dp,
context.getString(R.string.alarm_alert_dismiss_text),
PendingIntent.getService(context, id,
dismissIntent, PendingIntent.FLAG_UPDATE_CURRENT))
// Setup content action if instance is owned by alarm
val viewAlarmIntent: Intent = createViewAlarmIntent(context, instance)
builder.setContentIntent(PendingIntent.getActivity(context, id,
viewAlarmIntent, PendingIntent.FLAG_UPDATE_CURRENT))
val nm: NotificationManagerCompat = NotificationManagerCompat.from(context)
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
val channel = NotificationChannel(
ALARM_LOW_PRIORITY_NOTIFICATION_CHANNEL_ID,
context.getString(R.string.default_label),
NotificationManagerCompat.IMPORTANCE_DEFAULT)
nm.createNotificationChannel(channel)
}
val notification: Notification = builder.build()
nm.notify(id, notification)
updateUpcomingAlarmGroupNotification(context, -1, notification)
}
@JvmStatic
@Synchronized
fun showHighPriorityNotification(
context: Context,
instance: AlarmInstance
) {
LogUtils.v("Displaying high priority notification for alarm instance: " + instance.mId)
val builder: NotificationCompat.Builder = NotificationCompat.Builder(
context, ALARM_HIGH_PRIORITY_NOTIFICATION_CHANNEL_ID)
.setShowWhen(false)
.setContentTitle(context.getString(
R.string.alarm_alert_predismiss_title))
.setContentText(AlarmUtils.getAlarmText(
context, instance, true /* includeLabel */))
.setColor(ContextCompat.getColor(context, R.color.default_background))
.setSmallIcon(R.drawable.stat_notify_alarm)
.setAutoCancel(false)
.setSortKey(createSortKey(instance))
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setCategory(NotificationCompat.CATEGORY_EVENT)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setLocalOnly(true)
if (Utils.isNOrLater()) {
builder.setGroup(UPCOMING_GROUP_KEY)
}
// Setup up dismiss action
val dismissIntent: Intent = AlarmStateManager.createStateChangeIntent(context,
AlarmStateManager.ALARM_DISMISS_TAG, instance, InstancesColumns.PREDISMISSED_STATE)
val id = instance.hashCode()
builder.addAction(R.drawable.ic_alarm_off_24dp,
context.getString(R.string.alarm_alert_dismiss_text),
PendingIntent.getService(context, id,
dismissIntent, PendingIntent.FLAG_UPDATE_CURRENT))
// Setup content action if instance is owned by alarm
val viewAlarmIntent: Intent = createViewAlarmIntent(context, instance)
builder.setContentIntent(PendingIntent.getActivity(context, id,
viewAlarmIntent, PendingIntent.FLAG_UPDATE_CURRENT))
val nm: NotificationManagerCompat = NotificationManagerCompat.from(context)
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
val channel = NotificationChannel(
ALARM_HIGH_PRIORITY_NOTIFICATION_CHANNEL_ID,
context.getString(R.string.default_label),
NotificationManagerCompat.IMPORTANCE_DEFAULT)
nm.createNotificationChannel(channel)
}
val notification: Notification = builder.build()
nm.notify(id, notification)
updateUpcomingAlarmGroupNotification(context, -1, notification)
}
@TargetApi(Build.VERSION_CODES.N)
private fun isGroupSummary(n: Notification): Boolean {
return n.flags and Notification.FLAG_GROUP_SUMMARY == Notification.FLAG_GROUP_SUMMARY
}
/**
* Method which returns the first active notification for a given group. If a notification was
* just posted, provide it to make sure it is included as a potential result. If a notification
* was just canceled, provide the id so that it is not included as a potential result. These
* extra parameters are needed due to a race condition which exists in
* [NotificationManager.getActiveNotifications].
*
* @param context Context from which to grab the NotificationManager
* @param group The group key to query for notifications
* @param canceledNotificationId The id of the just-canceled notification (-1 if none)
* @param postedNotification The notification that was just posted
* @return The first active notification for the group
*/
@TargetApi(Build.VERSION_CODES.N)
private fun getFirstActiveNotification(
context: Context,
group: String,
canceledNotificationId: Int,
postedNotification: Notification?
): Notification? {
val nm: NotificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val notifications: Array<StatusBarNotification> = nm.getActiveNotifications()
var firstActiveNotification: Notification? = postedNotification
for (statusBarNotification in notifications) {
val n: Notification = statusBarNotification.getNotification()
if (!isGroupSummary(n) && group == n.getGroup() &&
statusBarNotification.getId() != canceledNotificationId) {
if (firstActiveNotification == null ||
n.getSortKey().compareTo(firstActiveNotification.getSortKey()) < 0) {
firstActiveNotification = n
}
}
}
return firstActiveNotification
}
@TargetApi(Build.VERSION_CODES.N)
private fun getActiveGroupSummaryNotification(context: Context, group: String): Notification? {
val nm: NotificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val notifications: Array<StatusBarNotification> = nm.getActiveNotifications()
for (statusBarNotification in notifications) {
val n: Notification = statusBarNotification.getNotification()
if (isGroupSummary(n) && group == n.getGroup()) {
return n
}
}
return null
}
private fun updateUpcomingAlarmGroupNotification(
context: Context,
canceledNotificationId: Int,
postedNotification: Notification?
) {
if (!Utils.isNOrLater()) {
return
}
val nm: NotificationManagerCompat = NotificationManagerCompat.from(context)
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
val channel = NotificationChannel(
ALARM_NOTIFICATION_CHANNEL_ID,
context.getString(R.string.default_label),
NotificationManagerCompat.IMPORTANCE_DEFAULT)
nm.createNotificationChannel(channel)
}
val firstUpcoming: Notification? = getFirstActiveNotification(context, UPCOMING_GROUP_KEY,
canceledNotificationId, postedNotification)
if (firstUpcoming == null) {
nm.cancel(ALARM_GROUP_NOTIFICATION_ID)
return
}
var summary: Notification? = getActiveGroupSummaryNotification(context, UPCOMING_GROUP_KEY)
if (summary == null ||
summary.contentIntent != firstUpcoming.contentIntent) {
summary = NotificationCompat.Builder(context, ALARM_NOTIFICATION_CHANNEL_ID)
.setShowWhen(false)
.setContentIntent(firstUpcoming.contentIntent)
.setColor(ContextCompat.getColor(context, R.color.default_background))
.setSmallIcon(R.drawable.stat_notify_alarm)
.setGroup(UPCOMING_GROUP_KEY)
.setGroupSummary(true)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setCategory(NotificationCompat.CATEGORY_EVENT)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setLocalOnly(true)
.build()
nm.notify(ALARM_GROUP_NOTIFICATION_ID, summary)
}
}
private fun updateMissedAlarmGroupNotification(
context: Context,
canceledNotificationId: Int,
postedNotification: Notification?
) {
if (!Utils.isNOrLater()) {
return
}
val nm: NotificationManagerCompat = NotificationManagerCompat.from(context)
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
val channel = NotificationChannel(
ALARM_NOTIFICATION_CHANNEL_ID,
context.getString(R.string.default_label),
NotificationManagerCompat.IMPORTANCE_DEFAULT)
nm.createNotificationChannel(channel)
}
val firstMissed: Notification? = getFirstActiveNotification(context, MISSED_GROUP_KEY,
canceledNotificationId, postedNotification)
if (firstMissed == null) {
nm.cancel(ALARM_GROUP_MISSED_NOTIFICATION_ID)
return
}
var summary: Notification? = getActiveGroupSummaryNotification(context, MISSED_GROUP_KEY)
if (summary == null ||
summary.contentIntent != firstMissed.contentIntent) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
val channel = NotificationChannel(
ALARM_MISSED_NOTIFICATION_CHANNEL_ID,
context.getString(R.string.default_label),
NotificationManagerCompat.IMPORTANCE_DEFAULT)
nm.createNotificationChannel(channel)
}
summary = NotificationCompat.Builder(context, ALARM_NOTIFICATION_CHANNEL_ID)
.setShowWhen(false)
.setContentIntent(firstMissed.contentIntent)
.setColor(ContextCompat.getColor(context, R.color.default_background))
.setSmallIcon(R.drawable.stat_notify_alarm)
.setGroup(MISSED_GROUP_KEY)
.setGroupSummary(true)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setCategory(NotificationCompat.CATEGORY_EVENT)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setLocalOnly(true)
.build()
nm.notify(ALARM_GROUP_MISSED_NOTIFICATION_ID, summary)
}
}
@JvmStatic
@Synchronized
fun showSnoozeNotification(
context: Context,
instance: AlarmInstance
) {
LogUtils.v("Displaying snoozed notification for alarm instance: " + instance.mId)
val builder: NotificationCompat.Builder = NotificationCompat.Builder(
context, ALARM_SNOOZE_NOTIFICATION_CHANNEL_ID)
.setShowWhen(false)
.setContentTitle(instance.getLabelOrDefault(context))
.setContentText(context.getString(R.string.alarm_alert_snooze_until,
AlarmUtils.getFormattedTime(context, instance.alarmTime)))
.setColor(ContextCompat.getColor(context, R.color.default_background))
.setSmallIcon(R.drawable.stat_notify_alarm)
.setAutoCancel(false)
.setSortKey(createSortKey(instance))
.setPriority(NotificationCompat.PRIORITY_MAX)
.setCategory(NotificationCompat.CATEGORY_EVENT)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setLocalOnly(true)
if (Utils.isNOrLater()) {
builder.setGroup(UPCOMING_GROUP_KEY)
}
// Setup up dismiss action
val dismissIntent: Intent = AlarmStateManager.createStateChangeIntent(context,
AlarmStateManager.ALARM_DISMISS_TAG, instance, InstancesColumns.DISMISSED_STATE)
val id = instance.hashCode()
builder.addAction(R.drawable.ic_alarm_off_24dp,
context.getString(R.string.alarm_alert_dismiss_text),
PendingIntent.getService(context, id,
dismissIntent, PendingIntent.FLAG_UPDATE_CURRENT))
// Setup content action if instance is owned by alarm
val viewAlarmIntent: Intent = createViewAlarmIntent(context, instance)
builder.setContentIntent(PendingIntent.getActivity(context, id,
viewAlarmIntent, PendingIntent.FLAG_UPDATE_CURRENT))
val nm: NotificationManagerCompat = NotificationManagerCompat.from(context)
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
val channel = NotificationChannel(
ALARM_SNOOZE_NOTIFICATION_CHANNEL_ID,
context.getString(R.string.default_label),
NotificationManagerCompat.IMPORTANCE_DEFAULT)
nm.createNotificationChannel(channel)
}
val notification: Notification = builder.build()
nm.notify(id, notification)
updateUpcomingAlarmGroupNotification(context, -1, notification)
}
@JvmStatic
@Synchronized
fun showMissedNotification(
context: Context,
instance: AlarmInstance
) {
LogUtils.v("Displaying missed notification for alarm instance: " + instance.mId)
val label = instance.mLabel
val alarmTime: String = AlarmUtils.getFormattedTime(context, instance.alarmTime)
val builder: NotificationCompat.Builder = NotificationCompat.Builder(
context, ALARM_MISSED_NOTIFICATION_CHANNEL_ID)
.setShowWhen(false)
.setContentTitle(context.getString(R.string.alarm_missed_title))
.setContentText(if (instance.mLabel!!.isEmpty()) {
alarmTime
} else {
context.getString(R.string.alarm_missed_text, alarmTime, label)
})
.setColor(ContextCompat.getColor(context, R.color.default_background))
.setSortKey(createSortKey(instance))
.setSmallIcon(R.drawable.stat_notify_alarm)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setCategory(NotificationCompat.CATEGORY_EVENT)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setLocalOnly(true)
if (Utils.isNOrLater()) {
builder.setGroup(MISSED_GROUP_KEY)
}
val id = instance.hashCode()
// Setup dismiss intent
val dismissIntent: Intent = AlarmStateManager.createStateChangeIntent(context,
AlarmStateManager.ALARM_DISMISS_TAG, instance, InstancesColumns.DISMISSED_STATE)
builder.setDeleteIntent(PendingIntent.getService(context, id,
dismissIntent, PendingIntent.FLAG_UPDATE_CURRENT))
// Setup content intent
val showAndDismiss: Intent = AlarmInstance.createIntent(context,
AlarmStateManager::class.java, instance.mId)
showAndDismiss.putExtra(EXTRA_NOTIFICATION_ID, id)
showAndDismiss.setAction(AlarmStateManager.SHOW_AND_DISMISS_ALARM_ACTION)
builder.setContentIntent(PendingIntent.getBroadcast(context, id,
showAndDismiss, PendingIntent.FLAG_UPDATE_CURRENT))
val nm: NotificationManagerCompat = NotificationManagerCompat.from(context)
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
val channel = NotificationChannel(
ALARM_MISSED_NOTIFICATION_CHANNEL_ID,
context.getString(R.string.default_label),
NotificationManagerCompat.IMPORTANCE_DEFAULT)
nm.createNotificationChannel(channel)
}
val notification: Notification = builder.build()
nm.notify(id, notification)
updateMissedAlarmGroupNotification(context, -1, notification)
}
@Synchronized
fun showAlarmNotification(service: Service, instance: AlarmInstance) {
LogUtils.v("Displaying alarm notification for alarm instance: " + instance.mId)
val resources: Resources = service.getResources()
val notification: NotificationCompat.Builder = NotificationCompat.Builder(
service, ALARM_NOTIFICATION_CHANNEL_ID)
.setContentTitle(instance.getLabelOrDefault(service))
.setContentText(AlarmUtils.getFormattedTime(
service, instance.alarmTime))
.setColor(ContextCompat.getColor(service, R.color.default_background))
.setSmallIcon(R.drawable.stat_notify_alarm)
.setOngoing(true)
.setAutoCancel(false)
.setDefaults(NotificationCompat.DEFAULT_LIGHTS)
.setWhen(0)
.setCategory(NotificationCompat.CATEGORY_ALARM)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setLocalOnly(true)
// Setup Snooze Action
val snoozeIntent: Intent = AlarmStateManager.createStateChangeIntent(service,
AlarmStateManager.ALARM_SNOOZE_TAG, instance, InstancesColumns.SNOOZE_STATE)
snoozeIntent.putExtra(AlarmStateManager.FROM_NOTIFICATION_EXTRA, true)
val snoozePendingIntent: PendingIntent = PendingIntent.getService(service,
ALARM_FIRING_NOTIFICATION_ID, snoozeIntent, PendingIntent.FLAG_UPDATE_CURRENT)
notification.addAction(R.drawable.ic_snooze_24dp,
resources.getString(R.string.alarm_alert_snooze_text), snoozePendingIntent)
// Setup Dismiss Action
val dismissIntent: Intent = AlarmStateManager.createStateChangeIntent(service,
AlarmStateManager.ALARM_DISMISS_TAG, instance, InstancesColumns.DISMISSED_STATE)
dismissIntent.putExtra(AlarmStateManager.FROM_NOTIFICATION_EXTRA, true)
val dismissPendingIntent: PendingIntent = PendingIntent.getService(service,
ALARM_FIRING_NOTIFICATION_ID, dismissIntent, PendingIntent.FLAG_UPDATE_CURRENT)
notification.addAction(R.drawable.ic_alarm_off_24dp,
resources.getString(R.string.alarm_alert_dismiss_text),
dismissPendingIntent)
// Setup Content Action
val contentIntent: Intent = AlarmInstance.createIntent(service, AlarmActivity::class.java,
instance.mId)
notification.setContentIntent(PendingIntent.getActivity(service,
ALARM_FIRING_NOTIFICATION_ID, contentIntent, PendingIntent.FLAG_UPDATE_CURRENT))
// Setup fullscreen intent
val fullScreenIntent: Intent =
AlarmInstance.createIntent(service, AlarmActivity::class.java, instance.mId)
// set action, so we can be different then content pending intent
fullScreenIntent.setAction("fullscreen_activity")
fullScreenIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or
Intent.FLAG_ACTIVITY_NO_USER_ACTION)
notification.setFullScreenIntent(PendingIntent.getActivity(service,
ALARM_FIRING_NOTIFICATION_ID, fullScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT),
true)
notification.setPriority(NotificationCompat.PRIORITY_MAX)
clearNotification(service, instance)
service.startForeground(ALARM_FIRING_NOTIFICATION_ID, notification.build())
}
@JvmStatic
@Synchronized
fun clearNotification(context: Context, instance: AlarmInstance) {
LogUtils.v("Clearing notifications for alarm instance: " + instance.mId)
val nm: NotificationManagerCompat = NotificationManagerCompat.from(context)
val id = instance.hashCode()
nm.cancel(id)
updateUpcomingAlarmGroupNotification(context, id, null)
updateMissedAlarmGroupNotification(context, id, null)
}
/**
* Updates the notification for an existing alarm. Use if the label has changed.
*/
@JvmStatic
fun updateNotification(context: Context, instance: AlarmInstance) {
when (instance.mAlarmState) {
InstancesColumns.LOW_NOTIFICATION_STATE -> {
showLowPriorityNotification(context, instance)
}
InstancesColumns.HIGH_NOTIFICATION_STATE -> {
showHighPriorityNotification(context, instance)
}
InstancesColumns.SNOOZE_STATE -> showSnoozeNotification(context, instance)
InstancesColumns.MISSED_STATE -> showMissedNotification(context, instance)
else -> LogUtils.d("No notification to update")
}
}
@JvmStatic
fun createViewAlarmIntent(context: Context?, instance: AlarmInstance): Intent {
val alarmId = instance.mAlarmId ?: Alarm.INVALID_ID
return Alarm.createIntent(context, DeskClock::class.java, alarmId)
.putExtra(AlarmClockFragment.SCROLL_TO_ALARM_INTENT_EXTRA, alarmId)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
/**
* Alarm notifications are sorted chronologically. Missed alarms are sorted chronologically
* **after** all upcoming/snoozed alarms by including the "MISSED" prefix on the
* sort key.
*
* @param instance the alarm instance for which the notification is generated
* @return the sort key that specifies the order of this alarm notification
*/
private fun createSortKey(instance: AlarmInstance): String {
val timeKey = SORT_KEY_FORMAT.format(instance.alarmTime.time)
val missedAlarm = instance.mAlarmState == InstancesColumns.MISSED_STATE
return if (missedAlarm) "MISSED $timeKey" else timeKey
}
}