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 package com.android.systemui.statusbar.notification.icon.domain.interactor 17 18 import com.android.systemui.dagger.qualifiers.Background 19 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor 20 import com.android.systemui.statusbar.data.repository.NotificationListenerSettingsRepository 21 import com.android.systemui.statusbar.notification.data.repository.NotificationsKeyguardViewStateRepository 22 import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor 23 import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationIconInteractor 24 import com.android.systemui.statusbar.notification.promoted.domain.interactor.AODPromotedNotificationInteractor 25 import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel 26 import com.android.wm.shell.bubbles.Bubbles 27 import java.util.Optional 28 import javax.inject.Inject 29 import kotlin.coroutines.CoroutineContext 30 import kotlin.jvm.optionals.getOrNull 31 import kotlinx.coroutines.flow.Flow 32 import kotlinx.coroutines.flow.combine 33 import kotlinx.coroutines.flow.flatMapLatest 34 import kotlinx.coroutines.flow.flowOf 35 import kotlinx.coroutines.flow.flowOn 36 37 /** Domain logic related to notification icons. */ 38 class NotificationIconsInteractor 39 @Inject 40 constructor( 41 private val activeNotificationsInteractor: ActiveNotificationsInteractor, 42 private val bubbles: Optional<Bubbles>, 43 private val headsUpNotificationIconInteractor: HeadsUpNotificationIconInteractor, 44 private val aodPromotedNotificationInteractor: AODPromotedNotificationInteractor, 45 private val keyguardViewStateRepository: NotificationsKeyguardViewStateRepository, 46 ) { 47 private val aodPromotedKeyToHide: Flow<String?> = 48 combine( 49 aodPromotedNotificationInteractor.content, 50 aodPromotedNotificationInteractor.isPresent, 51 ) { content, isPresent -> 52 when { 53 !isPresent -> null 54 content == null -> null 55 else -> content.identity.key 56 } 57 } 58 59 /** Returns a subset of all active notifications based on the supplied filtration parameters. */ 60 fun filteredNotifSet( 61 forceShowHeadsUp: Boolean = false, 62 showAmbient: Boolean = true, 63 showLowPriority: Boolean = true, 64 showDismissed: Boolean = true, 65 showRepliedMessages: Boolean = true, 66 showPulsing: Boolean = true, 67 showAodPromoted: Boolean = true, 68 ): Flow<Set<ActiveNotificationModel>> { 69 return combine( 70 activeNotificationsInteractor.topLevelRepresentativeNotifications, 71 headsUpNotificationIconInteractor.isolatedNotification, 72 if (showAodPromoted) flowOf(null) else aodPromotedKeyToHide, 73 keyguardViewStateRepository.areNotificationsFullyHidden, 74 ) { notifications, isolatedNotifKey, aodPromotedKeyToHide, notifsFullyHidden -> 75 notifications 76 .asSequence() 77 .filter { model: ActiveNotificationModel -> 78 shouldShowNotificationIcon( 79 model = model, 80 forceShowHeadsUp = forceShowHeadsUp, 81 showAmbient = showAmbient, 82 showLowPriority = showLowPriority, 83 showDismissed = showDismissed, 84 showRepliedMessages = showRepliedMessages, 85 showPulsing = showPulsing, 86 isolatedNotifKey = isolatedNotifKey, 87 aodPromotedKeyToHide = aodPromotedKeyToHide, 88 notifsFullyHidden = notifsFullyHidden, 89 ) 90 } 91 .toSet() 92 } 93 } 94 95 private fun shouldShowNotificationIcon( 96 model: ActiveNotificationModel, 97 forceShowHeadsUp: Boolean, 98 showAmbient: Boolean, 99 showLowPriority: Boolean, 100 showDismissed: Boolean, 101 showRepliedMessages: Boolean, 102 showPulsing: Boolean, 103 isolatedNotifKey: String?, 104 aodPromotedKeyToHide: String?, 105 notifsFullyHidden: Boolean, 106 ): Boolean { 107 return when { 108 forceShowHeadsUp && model.key == isolatedNotifKey -> true 109 !showAmbient && model.isAmbient -> false 110 !showLowPriority && model.isSilent -> false 111 !showDismissed && model.isRowDismissed -> false 112 !showRepliedMessages && model.isLastMessageFromReply -> false 113 !showAmbient && model.isSuppressedFromStatusBar -> false 114 !showPulsing && model.isPulsing && !notifsFullyHidden -> false 115 model.key == aodPromotedKeyToHide -> false 116 bubbles.getOrNull()?.isBubbleExpanded(model.key) == true -> false 117 else -> true 118 } 119 } 120 } 121 122 /** Domain logic related to notification icons shown on the always-on display. */ 123 class AlwaysOnDisplayNotificationIconsInteractor 124 @Inject 125 constructor( 126 @Background bgContext: CoroutineContext, 127 deviceEntryInteractor: DeviceEntryInteractor, 128 iconsInteractor: NotificationIconsInteractor, 129 ) { 130 val aodNotifs: Flow<Set<ActiveNotificationModel>> = 131 deviceEntryInteractor.isBypassEnabled isBypassEnablednull132 .flatMapLatest { isBypassEnabled -> 133 iconsInteractor.filteredNotifSet( 134 showAmbient = false, 135 showDismissed = false, 136 showRepliedMessages = false, 137 showPulsing = !isBypassEnabled, 138 showAodPromoted = false, 139 ) 140 } 141 .flowOn(bgContext) 142 } 143 144 /** Domain logic related to notification icons shown in the status bar. */ 145 class StatusBarNotificationIconsInteractor 146 @Inject 147 constructor( 148 @Background bgContext: CoroutineContext, 149 iconsInteractor: NotificationIconsInteractor, 150 settingsRepository: NotificationListenerSettingsRepository, 151 ) { 152 val statusBarNotifs: Flow<Set<ActiveNotificationModel>> = 153 settingsRepository.showSilentStatusIcons showSilentIconsnull154 .flatMapLatest { showSilentIcons -> 155 iconsInteractor.filteredNotifSet( 156 forceShowHeadsUp = true, 157 showAmbient = false, 158 showLowPriority = showSilentIcons, 159 showDismissed = false, 160 showRepliedMessages = false, 161 ) 162 } 163 .flowOn(bgContext) 164 } 165