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