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.domain.interactor 18 19 import android.os.UserHandle 20 import android.provider.Settings 21 import android.util.IndentingPrintWriter 22 import com.android.systemui.dagger.SysUISingleton 23 import com.android.systemui.dagger.qualifiers.Background 24 import com.android.systemui.statusbar.notification.collection.NotificationEntry 25 import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository 26 import com.android.systemui.statusbar.notification.shared.NotificationMinimalism 27 import com.android.systemui.util.printSection 28 import com.android.systemui.util.settings.SecureSettings 29 import com.android.systemui.util.settings.SettingsProxyExt.observerFlow 30 import javax.inject.Inject 31 import kotlinx.coroutines.CoroutineDispatcher 32 import kotlinx.coroutines.flow.Flow 33 import kotlinx.coroutines.flow.StateFlow 34 import kotlinx.coroutines.flow.conflate 35 import kotlinx.coroutines.flow.distinctUntilChanged 36 import kotlinx.coroutines.flow.flowOn 37 import kotlinx.coroutines.flow.map 38 import kotlinx.coroutines.flow.onStart 39 40 /** Interactor for business logic associated with the notification stack. */ 41 @SysUISingleton 42 class SeenNotificationsInteractor 43 @Inject 44 constructor( 45 @Background private val bgDispatcher: CoroutineDispatcher, 46 private val notificationListRepository: ActiveNotificationListRepository, 47 private val secureSettings: SecureSettings, 48 ) { 49 /** Are any already-seen notifications currently filtered out of the shade? */ 50 val hasFilteredOutSeenNotifications: StateFlow<Boolean> = 51 notificationListRepository.hasFilteredOutSeenNotifications 52 53 /** Set whether already-seen notifications are currently filtered out of the shade. */ setHasFilteredOutSeenNotificationsnull54 fun setHasFilteredOutSeenNotifications(value: Boolean) { 55 notificationListRepository.hasFilteredOutSeenNotifications.value = value 56 } 57 58 /** Set the entry that is identified as the top ongoing notification. */ setTopOngoingNotificationnull59 fun setTopOngoingNotification(entry: NotificationEntry?) { 60 if (NotificationMinimalism.isUnexpectedlyInLegacyMode()) return 61 notificationListRepository.topOngoingNotificationKey.value = entry?.key 62 } 63 64 /** Determine if the given notification is the top ongoing notification. */ isTopOngoingNotificationnull65 fun isTopOngoingNotification(entry: NotificationEntry?): Boolean = 66 if (NotificationMinimalism.isUnexpectedlyInLegacyMode()) false 67 else 68 entry != null && notificationListRepository.topOngoingNotificationKey.value == entry.key 69 70 /** Set the entry that is identified as the top unseen notification. */ 71 fun setTopUnseenNotification(entry: NotificationEntry?) { 72 if (NotificationMinimalism.isUnexpectedlyInLegacyMode()) return 73 notificationListRepository.topUnseenNotificationKey.value = entry?.key 74 } 75 76 /** Determine if the given notification is the top unseen notification. */ isTopUnseenNotificationnull77 fun isTopUnseenNotification(entry: NotificationEntry?): Boolean = 78 if (NotificationMinimalism.isUnexpectedlyInLegacyMode()) false 79 else entry != null && notificationListRepository.topUnseenNotificationKey.value == entry.key 80 81 fun dump(pw: IndentingPrintWriter) = 82 with(pw) { 83 printSection("SeenNotificationsInteractor") { 84 print( 85 "hasFilteredOutSeenNotifications", 86 notificationListRepository.hasFilteredOutSeenNotifications.value, 87 ) 88 print( 89 "topOngoingNotificationKey", 90 notificationListRepository.topOngoingNotificationKey.value, 91 ) 92 print( 93 "topUnseenNotificationKey", 94 notificationListRepository.topUnseenNotificationKey.value, 95 ) 96 } 97 } 98 99 /** 100 * There are three states for LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS. 101 * 102 * 0: unset_off, default value for phones. 103 * 104 * 1: on, default value for tablets. 105 * 106 * 2: off. 107 */ isLockScreenShowOnlyUnseenNotificationsEnablednull108 fun isLockScreenShowOnlyUnseenNotificationsEnabled(): Flow<Boolean> = 109 secureSettings 110 // emit whenever the setting has changed 111 .observerFlow( 112 UserHandle.USER_ALL, 113 Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS, 114 ) 115 // perform a query immediately 116 .onStart { emit(Unit) } 117 // for each change, lookup the new value <lambda>null118 .map { 119 secureSettings.getIntForUser( 120 name = Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS, 121 default = 0, // 0 is unset_off, which should be treated as off 122 userHandle = UserHandle.USER_CURRENT, 123 ) == 1 124 } 125 // don't emit anything if nothing has changed 126 .distinctUntilChanged() 127 // perform lookups on the bg thread pool 128 .flowOn(bgDispatcher) 129 // only track the most recent emission, if events are happening faster than they can be 130 // consumed 131 .conflate() 132 isLockScreenNotificationMinimalismEnablednull133 fun isLockScreenNotificationMinimalismEnabled(): Flow<Boolean> = 134 secureSettings 135 // emit whenever the setting has changed 136 .observerFlow(UserHandle.USER_ALL, Settings.Secure.LOCK_SCREEN_NOTIFICATION_MINIMALISM) 137 // perform a query immediately 138 .onStart { emit(Unit) } 139 // for each change, lookup the new value <lambda>null140 .map { 141 secureSettings.getIntForUser( 142 name = Settings.Secure.LOCK_SCREEN_NOTIFICATION_MINIMALISM, 143 default = 1, 144 userHandle = UserHandle.USER_CURRENT, 145 ) == 1 146 } 147 // don't emit anything if nothing has changed 148 .distinctUntilChanged() 149 // perform lookups on the bg thread pool 150 .flowOn(bgDispatcher) 151 // only track the most recent emission, if events are happening faster than they can be 152 // consumed 153 .conflate() 154 } 155