1 /*
2  * Copyright (C) 2019 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.content.Context
20 import android.media.MediaMetadata
21 import android.provider.Settings
22 import com.android.keyguard.KeyguardUpdateMonitor
23 import com.android.systemui.dagger.SysUISingleton
24 import com.android.systemui.plugins.statusbar.StatusBarStateController
25 import com.android.systemui.statusbar.NotificationLockscreenUserManager
26 import com.android.systemui.statusbar.NotificationMediaManager
27 import com.android.systemui.statusbar.StatusBarState
28 import com.android.systemui.statusbar.notification.NotificationEntryManager
29 import com.android.systemui.statusbar.notification.collection.NotificationEntry
30 import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
31 import com.android.systemui.statusbar.phone.KeyguardBypassController
32 import com.android.systemui.tuner.TunerService
33 import javax.inject.Inject
34 
35 /**
36  * A class that automatically creates heads up for important notification when bypassing the
37  * lockscreen
38  */
39 @SysUISingleton
40 class BypassHeadsUpNotifier @Inject constructor(
41     private val context: Context,
42     private val bypassController: KeyguardBypassController,
43     private val statusBarStateController: StatusBarStateController,
44     private val headsUpManager: HeadsUpManagerPhone,
45     private val notificationLockscreenUserManager: NotificationLockscreenUserManager,
46     private val mediaManager: NotificationMediaManager,
47     private val entryManager: NotificationEntryManager,
48     tunerService: TunerService
49 ) : StatusBarStateController.StateListener, NotificationMediaManager.MediaListener {
50 
51     private var currentMediaEntry: NotificationEntry? = null
52     private var enabled = true
53 
54     var fullyAwake = false
55         set(value) {
56             field = value
57             if (value) {
58                 updateAutoHeadsUp(currentMediaEntry)
59             }
60         }
61 
62     init {
63         statusBarStateController.addCallback(this)
64         tunerService.addTunable(
65                 TunerService.Tunable { _, _ ->
66                     enabled = Settings.Secure.getIntForUser(
67                             context.contentResolver,
68                             Settings.Secure.SHOW_MEDIA_WHEN_BYPASSING,
69                             0 /* default */,
70                             KeyguardUpdateMonitor.getCurrentUser()) != 0
71                 }, Settings.Secure.SHOW_MEDIA_WHEN_BYPASSING)
72     }
73 
74     fun setUp() {
75         mediaManager.addCallback(this)
76     }
77 
78     override fun onPrimaryMetadataOrStateChanged(metadata: MediaMetadata?, state: Int) {
79         val previous = currentMediaEntry
80         var newEntry = entryManager
81                 .getActiveNotificationUnfiltered(mediaManager.mediaNotificationKey)
82         if (!NotificationMediaManager.isPlayingState(state)) {
83             newEntry = null
84         }
85         currentMediaEntry = newEntry
86         updateAutoHeadsUp(previous)
87         updateAutoHeadsUp(currentMediaEntry)
88     }
89 
90     private fun updateAutoHeadsUp(entry: NotificationEntry?) {
91         entry?.let {
92             val autoHeadsUp = it == currentMediaEntry && canAutoHeadsUp(it)
93             it.isAutoHeadsUp = autoHeadsUp
94             if (autoHeadsUp) {
95                 headsUpManager.showNotification(it)
96             }
97         }
98     }
99 
100     /**
101      * @return {@code true} if this entry be autoHeadsUpped right now.
102      */
103     private fun canAutoHeadsUp(entry: NotificationEntry): Boolean {
104         if (!isAutoHeadsUpAllowed()) {
105             return false
106         }
107         if (entry.isSensitive) {
108             // filter sensitive notifications
109             return false
110         }
111         if (!notificationLockscreenUserManager.shouldShowOnKeyguard(entry)) {
112             // filter notifications invisible on Keyguard
113             return false
114         }
115         if (entryManager.getActiveNotificationUnfiltered(entry.key) != null) {
116             // filter notifications not the active list currently
117             return false
118         }
119         return true
120     }
121 
122     override fun onStatePostChange() {
123         updateAutoHeadsUp(currentMediaEntry)
124     }
125 
126     /**
127      * @return {@code true} if autoHeadsUp is possible right now.
128      */
129     private fun isAutoHeadsUpAllowed(): Boolean {
130         if (!enabled) {
131             return false
132         }
133         if (!bypassController.bypassEnabled) {
134             return false
135         }
136         if (statusBarStateController.state != StatusBarState.KEYGUARD) {
137             return false
138         }
139         if (!fullyAwake) {
140             return false
141         }
142         return true
143     }
144 }
145