1 /* <lambda>null2 * 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.scene.domain.interactor 18 19 import com.android.compose.animation.scene.ObservableTransitionState 20 import com.android.systemui.CoreStartable 21 import com.android.systemui.dagger.SysUISingleton 22 import com.android.systemui.dagger.qualifiers.Application 23 import com.android.systemui.keyguard.data.repository.KeyguardRepository 24 import com.android.systemui.keyguard.shared.model.StatusBarState 25 import com.android.systemui.power.domain.interactor.PowerInteractor 26 import com.android.systemui.scene.data.repository.WindowRootViewVisibilityRepository 27 import com.android.systemui.scene.shared.flag.SceneContainerFlag 28 import com.android.systemui.scene.shared.model.Scenes 29 import com.android.systemui.statusbar.NotificationPresenter 30 import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor 31 import com.android.systemui.statusbar.notification.init.NotificationsController 32 import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor 33 import com.android.systemui.statusbar.policy.HeadsUpManager 34 import javax.inject.Inject 35 import javax.inject.Provider 36 import kotlinx.coroutines.CoroutineScope 37 import kotlinx.coroutines.flow.SharingStarted 38 import kotlinx.coroutines.flow.StateFlow 39 import kotlinx.coroutines.flow.combine 40 import kotlinx.coroutines.flow.distinctUntilChanged 41 import kotlinx.coroutines.flow.map 42 import kotlinx.coroutines.flow.stateIn 43 import kotlinx.coroutines.launch 44 45 /** Business logic about the visibility of various parts of the window root view. */ 46 @SysUISingleton 47 class WindowRootViewVisibilityInteractor 48 @Inject 49 constructor( 50 @Application private val scope: CoroutineScope, 51 private val windowRootViewVisibilityRepository: WindowRootViewVisibilityRepository, 52 private val keyguardRepository: KeyguardRepository, 53 private val headsUpManager: HeadsUpManager, 54 powerInteractor: PowerInteractor, 55 private val activeNotificationsInteractor: ActiveNotificationsInteractor, 56 sceneInteractorProvider: Provider<SceneInteractor>, 57 ) : CoreStartable { 58 59 private var notificationPresenter: NotificationPresenter? = null 60 private var notificationsController: NotificationsController? = null 61 62 private val isNotifPresenterFullyCollapsed: Boolean 63 get() = notificationPresenter?.isPresenterFullyCollapsed ?: true 64 65 /** 66 * True if lockscreen (including AOD) or the shade is visible and false otherwise. Notably, 67 * false if the bouncer is visible. 68 */ 69 val isLockscreenOrShadeVisible: StateFlow<Boolean> = 70 if (!SceneContainerFlag.isEnabled) { 71 windowRootViewVisibilityRepository.isLockscreenOrShadeVisible 72 } else { 73 sceneInteractorProvider 74 .get() 75 .transitionState 76 .map { state -> 77 when (state) { 78 is ObservableTransitionState.Idle -> 79 state.currentScene == Scenes.Shade || 80 state.currentScene == Scenes.NotificationsShade || 81 state.currentScene == Scenes.QuickSettingsShade || 82 state.currentScene == Scenes.Lockscreen 83 is ObservableTransitionState.Transition -> 84 state.toScene == Scenes.Shade || 85 state.toScene == Scenes.NotificationsShade || 86 state.toScene == Scenes.QuickSettingsShade || 87 state.toScene == Scenes.Lockscreen || 88 state.fromScene == Scenes.Shade || 89 state.fromScene == Scenes.NotificationsShade || 90 state.fromScene == Scenes.QuickSettingsShade || 91 state.fromScene == Scenes.Lockscreen 92 } 93 } 94 .distinctUntilChanged() 95 .stateIn(scope, SharingStarted.Eagerly, false) 96 } 97 98 /** 99 * True if lockscreen (including AOD) or the shade is visible **and** the user is currently 100 * interacting with the device, false otherwise. Notably, false if the bouncer is visible and 101 * false if the device is asleep. 102 */ 103 val isLockscreenOrShadeVisibleAndInteractive: StateFlow<Boolean> = 104 combine( 105 isLockscreenOrShadeVisible, 106 powerInteractor.isAwake, 107 ) { isKeyguardAodOrShadeVisible, isAwake -> 108 isKeyguardAodOrShadeVisible && isAwake 109 } 110 .stateIn(scope, SharingStarted.Eagerly, initialValue = false) 111 112 /** 113 * Sets classes that aren't easily injectable on this class. 114 * 115 * TODO(b/277762009): Inject these directly instead. 116 */ 117 fun setUp( 118 presenter: NotificationPresenter?, 119 notificationsController: NotificationsController?, 120 ) { 121 this.notificationPresenter = presenter 122 this.notificationsController = notificationsController 123 } 124 125 override fun start() { 126 scope.launch { 127 isLockscreenOrShadeVisibleAndInteractive.collect { interactive -> 128 if (interactive) { 129 windowRootViewVisibilityRepository.onLockscreenOrShadeInteractive( 130 getShouldClearNotificationEffects(keyguardRepository.statusBarState.value), 131 getNotificationLoad(), 132 ) 133 } else { 134 windowRootViewVisibilityRepository.onLockscreenOrShadeNotInteractive() 135 } 136 } 137 } 138 } 139 140 fun setIsLockscreenOrShadeVisible(visible: Boolean) { 141 windowRootViewVisibilityRepository.setIsLockscreenOrShadeVisible(visible) 142 } 143 144 private fun getShouldClearNotificationEffects(statusBarState: StatusBarState): Boolean { 145 return !isNotifPresenterFullyCollapsed && 146 (statusBarState == StatusBarState.SHADE || 147 statusBarState == StatusBarState.SHADE_LOCKED) 148 } 149 150 private fun getNotificationLoad(): Int { 151 return if (headsUpManager.hasPinnedHeadsUp() && isNotifPresenterFullyCollapsed) { 152 1 153 } else { 154 getActiveNotificationsCount() 155 } 156 } 157 158 private fun getActiveNotificationsCount(): Int { 159 return if (NotificationsLiveDataStoreRefactor.isEnabled) { 160 activeNotificationsInteractor.allNotificationsCountValue 161 } else { 162 notificationsController?.getActiveNotificationsCount() ?: 0 163 } 164 } 165 } 166