1 /* <lambda>null2 * Copyright (C) 2024 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.compose.animation.scene.SceneKey 21 import com.android.systemui.dagger.SysUISingleton 22 import com.android.systemui.dagger.qualifiers.Application 23 import com.android.systemui.keyguard.domain.interactor.KeyguardOcclusionInteractor 24 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor 25 import com.android.systemui.keyguard.shared.model.KeyguardState 26 import com.android.systemui.scene.shared.model.Scenes 27 import javax.inject.Inject 28 import kotlinx.coroutines.CoroutineScope 29 import kotlinx.coroutines.flow.SharingStarted 30 import kotlinx.coroutines.flow.StateFlow 31 import kotlinx.coroutines.flow.combine 32 import kotlinx.coroutines.flow.map 33 import kotlinx.coroutines.flow.onStart 34 import kotlinx.coroutines.flow.stateIn 35 36 /** Encapsulates logic regarding the occlusion state of the scene container. */ 37 @SysUISingleton 38 class SceneContainerOcclusionInteractor 39 @Inject 40 constructor( 41 @Application applicationScope: CoroutineScope, 42 keyguardOcclusionInteractor: KeyguardOcclusionInteractor, 43 sceneInteractor: SceneInteractor, 44 keyguardTransitionInteractor: KeyguardTransitionInteractor, 45 ) { 46 /** Whether a show-when-locked activity is at the top of the current activity stack. */ 47 private val isOccludingActivityShown: StateFlow<Boolean> = 48 keyguardOcclusionInteractor.isShowWhenLockedActivityOnTop.stateIn( 49 scope = applicationScope, 50 started = SharingStarted.WhileSubscribed(), 51 initialValue = false, 52 ) 53 54 /** 55 * Whether AOD is fully shown (not transitioning) or partially shown during a transition to/from 56 * AOD. 57 */ 58 private val isAodFullyOrPartiallyShown: StateFlow<Boolean> = 59 keyguardTransitionInteractor 60 .transitionValue(KeyguardState.AOD) 61 .onStart { emit(0f) } 62 .map { it > 0 } 63 .stateIn( 64 scope = applicationScope, 65 started = SharingStarted.WhileSubscribed(), 66 initialValue = false, 67 ) 68 69 /** 70 * Whether the scene container should become invisible due to "occlusion" by an in-foreground 71 * "show when locked" activity. 72 */ 73 val invisibleDueToOcclusion: StateFlow<Boolean> = 74 combine( 75 isOccludingActivityShown, 76 sceneInteractor.transitionState, 77 isAodFullyOrPartiallyShown, 78 ) { isOccludingActivityShown, sceneTransitionState, isAodFullyOrPartiallyShown -> 79 invisibleDueToOcclusion( 80 isOccludingActivityShown = isOccludingActivityShown, 81 sceneTransitionState = sceneTransitionState, 82 isAodFullyOrPartiallyShown = isAodFullyOrPartiallyShown, 83 ) 84 } 85 .stateIn( 86 scope = applicationScope, 87 started = SharingStarted.WhileSubscribed(), 88 initialValue = 89 invisibleDueToOcclusion( 90 isOccludingActivityShown = isOccludingActivityShown.value, 91 sceneTransitionState = sceneInteractor.transitionState.value, 92 isAodFullyOrPartiallyShown = isAodFullyOrPartiallyShown.value, 93 ), 94 ) 95 96 private fun invisibleDueToOcclusion( 97 isOccludingActivityShown: Boolean, 98 sceneTransitionState: ObservableTransitionState, 99 isAodFullyOrPartiallyShown: Boolean, 100 ): Boolean { 101 return isOccludingActivityShown && 102 // Cannot be occluded in AOD. 103 !isAodFullyOrPartiallyShown && 104 // Only some scenes can be occluded. 105 sceneTransitionState.canBeOccluded 106 } 107 108 private val ObservableTransitionState.canBeOccluded: Boolean 109 get() = 110 when (this) { 111 is ObservableTransitionState.Idle -> currentScene.canBeOccluded 112 is ObservableTransitionState.Transition -> 113 fromScene.canBeOccluded && toScene.canBeOccluded 114 } 115 116 /** 117 * Whether the scene can be occluded by a "show when locked" activity. Some scenes should, on 118 * principle not be occlude-able because they render as if they are expanding on top of the 119 * occluding activity. 120 */ 121 private val SceneKey.canBeOccluded: Boolean 122 get() = 123 when (this) { 124 Scenes.Bouncer -> true 125 Scenes.Communal -> true 126 Scenes.Gone -> true 127 Scenes.Lockscreen -> true 128 Scenes.NotificationsShade -> false 129 Scenes.QuickSettings -> false 130 Scenes.QuickSettingsShade -> false 131 Scenes.Shade -> false 132 else -> error("SceneKey \"$this\" doesn't have a mapping for canBeOccluded!") 133 } 134 } 135