• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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