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