1 /*
2  * Copyright (C) 2020 The LineageOS Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.deskclock
18 
19 import android.app.NotificationChannel
20 import android.content.Context
21 import android.util.ArraySet
22 import android.util.Log
23 import androidx.core.app.NotificationManagerCompat
24 import androidx.core.app.NotificationManagerCompat.IMPORTANCE_HIGH
25 import androidx.core.app.NotificationManagerCompat.IMPORTANCE_LOW
26 
27 object NotificationUtils {
28     private val TAG = NotificationUtils::class.java.simpleName
29 
30     /**
31      * Notification channel containing all missed alarm notifications.
32      */
33     const val ALARM_MISSED_NOTIFICATION_CHANNEL_ID = "alarmMissedNotification"
34 
35     /**
36      * Notification channel containing all upcoming alarm notifications.
37      */
38     const val ALARM_UPCOMING_NOTIFICATION_CHANNEL_ID = "alarmUpcomingNotification"
39 
40     /**
41      * Notification channel containing all snooze notifications.
42      */
43     const val ALARM_SNOOZE_NOTIFICATION_CHANNEL_ID = "alarmSnoozingNotification"
44 
45     /**
46      * Notification channel containing all firing alarm and timer notifications.
47      */
48     const val FIRING_NOTIFICATION_CHANNEL_ID = "firingAlarmsAndTimersNotification"
49 
50     /**
51      * Notification channel containing all TimerModel notifications.
52      */
53     const val TIMER_MODEL_NOTIFICATION_CHANNEL_ID = "timerNotification"
54 
55     /**
56      * Notification channel containing all stopwatch notifications.
57      */
58     const val STOPWATCH_NOTIFICATION_CHANNEL_ID = "stopwatchNotification"
59 
60     /**
61      * Values used to bitmask certain channel defaults
62      */
63     private const val PLAY_SOUND = 0x01
64     private const val ENABLE_LIGHTS = 0x02
65     private const val ENABLE_VIBRATION = 0x04
66 
67     private val CHANNEL_PROPS: MutableMap<String, IntArray> = HashMap()
68 
69     init {
70         CHANNEL_PROPS[ALARM_MISSED_NOTIFICATION_CHANNEL_ID] = intArrayOf(
71                 R.string.alarm_missed_channel,
72                 IMPORTANCE_HIGH
73         )
74         CHANNEL_PROPS[ALARM_SNOOZE_NOTIFICATION_CHANNEL_ID] = intArrayOf(
75                 R.string.alarm_snooze_channel,
76                 IMPORTANCE_LOW
77         )
78         CHANNEL_PROPS[ALARM_UPCOMING_NOTIFICATION_CHANNEL_ID] = intArrayOf(
79                 R.string.alarm_upcoming_channel,
80                 IMPORTANCE_LOW
81         )
82         CHANNEL_PROPS[FIRING_NOTIFICATION_CHANNEL_ID] = intArrayOf(
83                 R.string.firing_alarms_timers_channel,
84                 IMPORTANCE_HIGH,
85                 ENABLE_LIGHTS
86         )
87         CHANNEL_PROPS[STOPWATCH_NOTIFICATION_CHANNEL_ID] = intArrayOf(
88                 R.string.stopwatch_channel,
89                 IMPORTANCE_LOW
90         )
91         CHANNEL_PROPS[TIMER_MODEL_NOTIFICATION_CHANNEL_ID] = intArrayOf(
92                 R.string.timer_channel,
93                 IMPORTANCE_LOW
94         )
95     }
96 
97     @JvmStatic
createChannelnull98     fun createChannel(context: Context, id: String) {
99         if (!Utils.isOOrLater) {
100             return
101         }
102 
103         if (!CHANNEL_PROPS.containsKey(id)) {
104             Log.e(TAG, "Invalid channel requested: $id")
105             return
106         }
107 
108         val properties = CHANNEL_PROPS[id]!!
109         val nameId = properties[0]
110         val importance = properties[1]
111         val channel = NotificationChannel(id, context.getString(nameId), importance)
112         if (properties.size >= 3) {
113             val bits = properties[2]
114             channel.enableLights(bits and ENABLE_LIGHTS != 0)
115             channel.enableVibration(bits and ENABLE_VIBRATION != 0)
116             if (bits and PLAY_SOUND == 0) {
117                 channel.setSound(null, null)
118             }
119         }
120         val nm: NotificationManagerCompat = NotificationManagerCompat.from(context)
121         nm.createNotificationChannel(channel)
122     }
123 
deleteChannelnull124     private fun deleteChannel(nm: NotificationManagerCompat, channelId: String) {
125         val channel: NotificationChannel? = nm.getNotificationChannel(channelId)
126         if (channel != null) {
127             nm.deleteNotificationChannel(channelId)
128         }
129     }
130 
getAllExistingChannelIdsnull131     private fun getAllExistingChannelIds(nm: NotificationManagerCompat): Set<String> {
132         val result: MutableSet<String> = ArraySet()
133         for (channel in nm.getNotificationChannels()) {
134             result.add(channel.id)
135         }
136         return result
137     }
138 
139     @JvmStatic
updateNotificationChannelsnull140     fun updateNotificationChannels(context: Context) {
141         if (!Utils.isOOrLater) {
142             return
143         }
144 
145         val nm: NotificationManagerCompat = NotificationManagerCompat.from(context)
146 
147         // These channels got a new behavior so we need to recreate them with new ids
148         deleteChannel(nm, "alarmLowPriorityNotification")
149         deleteChannel(nm, "alarmHighPriorityNotification")
150         deleteChannel(nm, "StopwatchNotification")
151         deleteChannel(nm, "alarmNotification")
152         deleteChannel(nm, "TimerModelNotification")
153         deleteChannel(nm, "alarmSnoozeNotification")
154 
155         // We recreate all existing channels so any language change or our name changes propagate
156         // to the actual channels
157         val existingChannelIds = getAllExistingChannelIds(nm)
158         for (id in existingChannelIds) {
159             createChannel(context, id)
160         }
161     }
162 }