1 /*
2  * Copyright (C) 2023 The Android Open Source 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.systemui.statusbar.notification.interruption
18 
19 import android.app.NotificationManager.IMPORTANCE_HIGH
20 import android.os.PowerManager
21 import com.android.internal.logging.UiEventLogger.UiEventEnum
22 import com.android.systemui.plugins.statusbar.StatusBarStateController
23 import com.android.systemui.statusbar.StatusBarState.KEYGUARD
24 import com.android.systemui.statusbar.notification.collection.NotificationEntry
25 import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.FSI_DEVICE_DREAMING
26 import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.FSI_DEVICE_NOT_INTERACTIVE
27 import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.FSI_DEVICE_NOT_PROVISIONED
28 import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.FSI_KEYGUARD_OCCLUDED
29 import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.FSI_KEYGUARD_SHOWING
30 import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.FSI_LOCKED_SHADE
31 import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.FSI_USER_SETUP_INCOMPLETE
32 import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_EXPECTED_TO_HUN
33 import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_NOT_IMPORTANT_ENOUGH
34 import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_NO_FULL_SCREEN_INTENT
35 import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_NO_HUN_OR_KEYGUARD
36 import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_PACKAGE_SUSPENDED
37 import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_SHOW_STICKY_HUN
38 import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_SUPPRESSED_BY_DND
39 import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_SUPPRESSED_ONLY_BY_DND
40 import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_SUPPRESSIVE_BUBBLE_METADATA
41 import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR
42 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.FSI_SUPPRESSED_NO_HUN_OR_KEYGUARD
43 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.FSI_SUPPRESSED_SUPPRESSIVE_BUBBLE_METADATA
44 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.FSI_SUPPRESSED_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR
45 import com.android.systemui.statusbar.notification.interruption.VisualInterruptionSuppressor.EventLogData
46 import com.android.systemui.statusbar.policy.DeviceProvisionedController
47 import com.android.systemui.statusbar.policy.KeyguardStateController
48 
49 class FullScreenIntentDecisionProvider(
50     private val deviceProvisionedController: DeviceProvisionedController,
51     private val keyguardStateController: KeyguardStateController,
52     private val powerManager: PowerManager,
53     private val statusBarStateController: StatusBarStateController
54 ) {
55     interface Decision {
56         val shouldFsi: Boolean
57         val wouldFsiWithoutDnd: Boolean
58         val logReason: String
59         val shouldLog: Boolean
60         val isWarning: Boolean
61         val uiEventId: UiEventEnum?
62         val eventLogData: EventLogData?
63     }
64 
65     private enum class DecisionImpl(
66         override val shouldFsi: Boolean,
67         override val logReason: String,
68         override val wouldFsiWithoutDnd: Boolean = shouldFsi,
69         val supersedesDnd: Boolean = false,
70         override val shouldLog: Boolean = true,
71         override val isWarning: Boolean = false,
72         override val uiEventId: UiEventEnum? = null,
73         override val eventLogData: EventLogData? = null
74     ) : Decision {
75         NO_FSI_NO_FULL_SCREEN_INTENT(
76             false,
77             "no full-screen intent",
78             supersedesDnd = true,
79             shouldLog = false
80         ),
81         NO_FSI_SHOW_STICKY_HUN(false, "full-screen intents are disabled", supersedesDnd = true),
82         NO_FSI_NOT_IMPORTANT_ENOUGH(false, "not important enough"),
83         NO_FSI_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR(
84             false,
85             "suppressive group alert behavior",
86             isWarning = true,
87             uiEventId = FSI_SUPPRESSED_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR,
88             eventLogData = EventLogData("231322873", "groupAlertBehavior")
89         ),
90         NO_FSI_SUPPRESSIVE_BUBBLE_METADATA(
91             false,
92             "suppressive bubble metadata",
93             isWarning = true,
94             uiEventId = FSI_SUPPRESSED_SUPPRESSIVE_BUBBLE_METADATA,
95             eventLogData = EventLogData("274759612", "bubbleMetadata")
96         ),
97         NO_FSI_PACKAGE_SUSPENDED(false, "package suspended"),
98         FSI_DEVICE_NOT_INTERACTIVE(true, "device is not interactive"),
99         FSI_DEVICE_DREAMING(true, "device is dreaming"),
100         FSI_KEYGUARD_SHOWING(true, "keyguard is showing"),
101         NO_FSI_EXPECTED_TO_HUN(false, "expected to heads-up instead"),
102         FSI_KEYGUARD_OCCLUDED(true, "keyguard is occluded"),
103         FSI_LOCKED_SHADE(true, "locked shade"),
104         FSI_DEVICE_NOT_PROVISIONED(true, "device not provisioned"),
105         FSI_USER_SETUP_INCOMPLETE(true, "user setup incomplete"),
106         NO_FSI_NO_HUN_OR_KEYGUARD(
107             false,
108             "no HUN or keyguard",
109             isWarning = true,
110             uiEventId = FSI_SUPPRESSED_NO_HUN_OR_KEYGUARD,
111             eventLogData = EventLogData("231322873", "no hun or keyguard")
112         ),
113         NO_FSI_SUPPRESSED_BY_DND(false, "suppressed by DND", wouldFsiWithoutDnd = false),
114         NO_FSI_SUPPRESSED_ONLY_BY_DND(false, "suppressed only by DND", wouldFsiWithoutDnd = true)
115     }
116 
makeFullScreenIntentDecisionnull117     fun makeFullScreenIntentDecision(entry: NotificationEntry, couldHeadsUp: Boolean): Decision {
118         val reasonWithoutDnd = makeDecisionWithoutDnd(entry, couldHeadsUp)
119 
120         val suppressedWithoutDnd = !reasonWithoutDnd.shouldFsi
121         val suppressedByDnd = entry.shouldSuppressFullScreenIntent()
122 
123         val reasonWithDnd =
124             when {
125                 reasonWithoutDnd.supersedesDnd -> reasonWithoutDnd
126                 suppressedByDnd && !suppressedWithoutDnd -> NO_FSI_SUPPRESSED_ONLY_BY_DND
127                 suppressedByDnd -> NO_FSI_SUPPRESSED_BY_DND
128                 else -> reasonWithoutDnd
129             }
130 
131         return reasonWithDnd
132     }
133 
makeDecisionWithoutDndnull134     private fun makeDecisionWithoutDnd(
135         entry: NotificationEntry,
136         couldHeadsUp: Boolean
137     ): DecisionImpl {
138         val sbn = entry.sbn
139         val notification = sbn.notification!!
140 
141         if (notification.fullScreenIntent == null) {
142             return if (entry.isStickyAndNotDemoted) {
143                 NO_FSI_SHOW_STICKY_HUN
144             } else {
145                 NO_FSI_NO_FULL_SCREEN_INTENT
146             }
147         }
148 
149         if (entry.importance < IMPORTANCE_HIGH) {
150             return NO_FSI_NOT_IMPORTANT_ENOUGH
151         }
152 
153         if (sbn.isGroup && notification.suppressAlertingDueToGrouping()) {
154             return NO_FSI_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR
155         }
156 
157         val bubbleMetadata = notification.bubbleMetadata
158         if (bubbleMetadata != null && bubbleMetadata.isNotificationSuppressed) {
159             return NO_FSI_SUPPRESSIVE_BUBBLE_METADATA
160         }
161 
162         if (entry.ranking.isSuspended) {
163             return NO_FSI_PACKAGE_SUSPENDED
164         }
165 
166         if (!powerManager.isInteractive) {
167             return FSI_DEVICE_NOT_INTERACTIVE
168         }
169 
170         if (statusBarStateController.isDreaming) {
171             return FSI_DEVICE_DREAMING
172         }
173 
174         if (statusBarStateController.state == KEYGUARD) {
175             return FSI_KEYGUARD_SHOWING
176         }
177 
178         if (couldHeadsUp) {
179             return NO_FSI_EXPECTED_TO_HUN
180         }
181 
182         if (keyguardStateController.isShowing) {
183             return if (keyguardStateController.isOccluded) {
184                 FSI_KEYGUARD_OCCLUDED
185             } else {
186                 FSI_LOCKED_SHADE
187             }
188         }
189 
190         if (!deviceProvisionedController.isDeviceProvisioned) {
191             return FSI_DEVICE_NOT_PROVISIONED
192         }
193 
194         if (!deviceProvisionedController.isCurrentUserSetup) {
195             return FSI_USER_SETUP_INCOMPLETE
196         }
197 
198         return NO_FSI_NO_HUN_OR_KEYGUARD
199     }
200 }
201