1 /*
<lambda>null2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5  * except in compliance with the License. You may obtain a copy of the License at
6  *
7  *      http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the
10  * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11  * KIND, either express or implied. See the License for the specific language governing
12  * permissions and limitations under the License.
13  *
14  */
15 
16 @file:OptIn(ExperimentalCoroutinesApi::class)
17 
18 package com.android.systemui.statusbar.notification.icon.domain.interactor
19 
20 import com.android.systemui.dagger.qualifiers.Background
21 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
22 import com.android.systemui.statusbar.data.repository.NotificationListenerSettingsRepository
23 import com.android.systemui.statusbar.notification.data.repository.NotificationsKeyguardViewStateRepository
24 import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
25 import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationIconInteractor
26 import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
27 import com.android.wm.shell.bubbles.Bubbles
28 import java.util.Optional
29 import javax.inject.Inject
30 import kotlin.coroutines.CoroutineContext
31 import kotlin.jvm.optionals.getOrNull
32 import kotlinx.coroutines.ExperimentalCoroutinesApi
33 import kotlinx.coroutines.flow.Flow
34 import kotlinx.coroutines.flow.combine
35 import kotlinx.coroutines.flow.flatMapLatest
36 import kotlinx.coroutines.flow.flowOn
37 
38 /** Domain logic related to notification icons. */
39 class NotificationIconsInteractor
40 @Inject
41 constructor(
42     private val activeNotificationsInteractor: ActiveNotificationsInteractor,
43     private val bubbles: Optional<Bubbles>,
44     private val headsUpNotificationIconInteractor: HeadsUpNotificationIconInteractor,
45     private val keyguardViewStateRepository: NotificationsKeyguardViewStateRepository,
46 ) {
47     /** Returns a subset of all active notifications based on the supplied filtration parameters. */
48     fun filteredNotifSet(
49         forceShowHeadsUp: Boolean = false,
50         showAmbient: Boolean = true,
51         showLowPriority: Boolean = true,
52         showDismissed: Boolean = true,
53         showRepliedMessages: Boolean = true,
54         showPulsing: Boolean = true,
55     ): Flow<Set<ActiveNotificationModel>> {
56         return combine(
57             activeNotificationsInteractor.topLevelRepresentativeNotifications,
58             headsUpNotificationIconInteractor.isolatedNotification,
59             keyguardViewStateRepository.areNotificationsFullyHidden,
60         ) { notifications, isolatedNotifKey, notifsFullyHidden ->
61             notifications
62                 .asSequence()
63                 .filter { model: ActiveNotificationModel ->
64                     shouldShowNotificationIcon(
65                         model = model,
66                         forceShowHeadsUp = forceShowHeadsUp,
67                         showAmbient = showAmbient,
68                         showLowPriority = showLowPriority,
69                         showDismissed = showDismissed,
70                         showRepliedMessages = showRepliedMessages,
71                         showPulsing = showPulsing,
72                         isolatedNotifKey = isolatedNotifKey,
73                         notifsFullyHidden = notifsFullyHidden,
74                     )
75                 }
76                 .toSet()
77         }
78     }
79 
80     private fun shouldShowNotificationIcon(
81         model: ActiveNotificationModel,
82         forceShowHeadsUp: Boolean,
83         showAmbient: Boolean,
84         showLowPriority: Boolean,
85         showDismissed: Boolean,
86         showRepliedMessages: Boolean,
87         showPulsing: Boolean,
88         isolatedNotifKey: String?,
89         notifsFullyHidden: Boolean,
90     ): Boolean {
91         return when {
92             forceShowHeadsUp && model.key == isolatedNotifKey -> true
93             !showAmbient && model.isAmbient -> false
94             !showLowPriority && model.isSilent -> false
95             !showDismissed && model.isRowDismissed -> false
96             !showRepliedMessages && model.isLastMessageFromReply -> false
97             !showAmbient && model.isSuppressedFromStatusBar -> false
98             !showPulsing && model.isPulsing && !notifsFullyHidden -> false
99             bubbles.getOrNull()?.isBubbleExpanded(model.key) == true -> false
100             else -> true
101         }
102     }
103 }
104 
105 /** Domain logic related to notification icons shown on the always-on display. */
106 class AlwaysOnDisplayNotificationIconsInteractor
107 @Inject
108 constructor(
109     @Background bgContext: CoroutineContext,
110     deviceEntryInteractor: DeviceEntryInteractor,
111     iconsInteractor: NotificationIconsInteractor,
112 ) {
113     val aodNotifs: Flow<Set<ActiveNotificationModel>> =
114         deviceEntryInteractor.isBypassEnabled
isBypassEnablednull115             .flatMapLatest { isBypassEnabled ->
116                 iconsInteractor.filteredNotifSet(
117                     showAmbient = false,
118                     showDismissed = false,
119                     showRepliedMessages = false,
120                     showPulsing = !isBypassEnabled,
121                 )
122             }
123             .flowOn(bgContext)
124 }
125 
126 /** Domain logic related to notification icons shown in the status bar. */
127 class StatusBarNotificationIconsInteractor
128 @Inject
129 constructor(
130     @Background bgContext: CoroutineContext,
131     iconsInteractor: NotificationIconsInteractor,
132     settingsRepository: NotificationListenerSettingsRepository,
133 ) {
134     val statusBarNotifs: Flow<Set<ActiveNotificationModel>> =
135         settingsRepository.showSilentStatusIcons
showSilentIconsnull136             .flatMapLatest { showSilentIcons ->
137                 iconsInteractor.filteredNotifSet(
138                     forceShowHeadsUp = true,
139                     showAmbient = false,
140                     showLowPriority = showSilentIcons,
141                     showDismissed = false,
142                     showRepliedMessages = false,
143                 )
144             }
145             .flowOn(bgContext)
146 }
147