• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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.collection.inflation
18 
19 import android.content.Context
20 import android.database.ContentObserver
21 import android.os.Handler
22 import android.os.HandlerExecutor
23 import android.os.UserHandle
24 import android.provider.Settings.Secure.SHOW_NOTIFICATION_SNOOZE
25 import com.android.server.notification.Flags.screenshareNotificationHiding
26 import com.android.systemui.dagger.SysUISingleton
27 import com.android.systemui.dagger.qualifiers.Main
28 import com.android.systemui.settings.UserTracker
29 import com.android.systemui.statusbar.NotificationLockscreenUserManager
30 import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_PUBLIC
31 import com.android.systemui.statusbar.notification.collection.GroupEntry
32 import com.android.systemui.statusbar.notification.collection.NotificationEntry
33 import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider
34 import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager
35 import com.android.systemui.statusbar.policy.SensitiveNotificationProtectionController
36 import com.android.systemui.util.ListenerSet
37 import com.android.systemui.util.settings.SecureSettings
38 import javax.inject.Inject
39 
40 /**
41  * A class which provides an adjustment object to the preparation coordinator which is uses to
42  * ensure that notifications are reinflated when ranking-derived information changes.
43  */
44 @SysUISingleton
45 class NotifUiAdjustmentProvider
46 @Inject
47 constructor(
48     @Main private val handler: Handler,
49     private val secureSettings: SecureSettings,
50     private val lockscreenUserManager: NotificationLockscreenUserManager,
51     private val sensitiveNotifProtectionController: SensitiveNotificationProtectionController,
52     private val sectionStyleProvider: SectionStyleProvider,
53     private val userTracker: UserTracker,
54     private val groupMembershipManager: GroupMembershipManager,
55 ) {
56     private val dirtyListeners = ListenerSet<Runnable>()
57     private var isSnoozeSettingsEnabled = false
58 
59     /** Update the snooze enabled value on user switch */
60     private val userTrackerCallback =
61         object : UserTracker.Callback {
onUserChangednull62             override fun onUserChanged(newUser: Int, userContext: Context) {
63                 updateSnoozeEnabled()
64             }
65         }
66 
67     init {
68         userTracker.addCallback(userTrackerCallback, HandlerExecutor(handler))
69     }
70 
addDirtyListenernull71     fun addDirtyListener(listener: Runnable) {
72         if (dirtyListeners.isEmpty()) {
73             lockscreenUserManager.addNotificationStateChangedListener(notifStateChangedListener)
74             if (screenshareNotificationHiding()) {
75                 sensitiveNotifProtectionController.registerSensitiveStateListener(
76                     onSensitiveStateChangedListener
77                 )
78             }
79             updateSnoozeEnabled()
80             secureSettings.registerContentObserverForUserSync(
81                 SHOW_NOTIFICATION_SNOOZE,
82                 settingsObserver,
83                 UserHandle.USER_ALL,
84             )
85         }
86         dirtyListeners.addIfAbsent(listener)
87     }
88 
removeDirtyListenernull89     fun removeDirtyListener(listener: Runnable) {
90         dirtyListeners.remove(listener)
91         if (dirtyListeners.isEmpty()) {
92             lockscreenUserManager.removeNotificationStateChangedListener(notifStateChangedListener)
93             if (screenshareNotificationHiding()) {
94                 sensitiveNotifProtectionController.unregisterSensitiveStateListener(
95                     onSensitiveStateChangedListener
96                 )
97             }
98             secureSettings.unregisterContentObserverSync(settingsObserver)
99         }
100     }
101 
102     private val notifStateChangedListener =
<lambda>null103         NotificationLockscreenUserManager.NotificationStateChangedListener {
104             dirtyListeners.forEach(Runnable::run)
105         }
106 
<lambda>null107     private val onSensitiveStateChangedListener = Runnable { dirtyListeners.forEach(Runnable::run) }
108 
109     private val settingsObserver =
110         object : ContentObserver(handler) {
onChangenull111             override fun onChange(selfChange: Boolean) {
112                 updateSnoozeEnabled()
113                 dirtyListeners.forEach(Runnable::run)
114             }
115         }
116 
updateSnoozeEnablednull117     private fun updateSnoozeEnabled() {
118         isSnoozeSettingsEnabled =
119             secureSettings.getIntForUser(SHOW_NOTIFICATION_SNOOZE, 0, UserHandle.USER_CURRENT) == 1
120     }
121 
isEntryMinimizednull122     private fun isEntryMinimized(entry: NotificationEntry): Boolean {
123         val section = entry.section ?: error("Entry must have a section to determine if minimized")
124         val parent = entry.parent ?: error("Entry must have a parent to determine if minimized")
125         val isMinimizedSection = sectionStyleProvider.isMinimizedSection(section)
126         val isTopLevelEntry = parent == GroupEntry.ROOT_ENTRY
127         val isGroupSummary = (parent as? GroupEntry)?.summary == entry
128         return isMinimizedSection && (isTopLevelEntry || isGroupSummary)
129     }
130 
131     /**
132      * Returns a adjustment object for the given entry. This can be compared to a previous instance
133      * from the same notification using [NotifUiAdjustment.needReinflate] to determine if it should
134      * be reinflated.
135      */
calculateAdjustmentnull136     fun calculateAdjustment(entry: NotificationEntry) =
137         NotifUiAdjustment(
138             key = entry.key,
139             smartActions = entry.ranking.smartActions,
140             smartReplies = entry.ranking.smartReplies,
141             isConversation = entry.ranking.isConversation,
142             isSnoozeEnabled = isSnoozeSettingsEnabled && !entry.isCanceled,
143             isMinimized = isEntryMinimized(entry),
144             redactionType =
145                 if (
146                     screenshareNotificationHiding() &&
147                         sensitiveNotifProtectionController.shouldProtectNotification(entry)
148                 ) {
149                     REDACTION_TYPE_PUBLIC
150                 } else {
151                     lockscreenUserManager.getRedactionType(entry)
152                 },
153             isChildInGroup = entry.hasEverBeenGroupChild(),
154             isGroupSummary = entry.hasEverBeenGroupSummary(),
155             summarization = entry.ranking.summarization
156         )
157 }
158