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.statusbar.domain.interactor 18 19 import com.android.systemui.dagger.SysUISingleton 20 import com.android.systemui.keyguard.domain.interactor.KeyguardOcclusionInteractor 21 import com.android.systemui.keyguard.domain.interactor.KeyguardSurfaceBehindInteractor 22 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor 23 import com.android.systemui.keyguard.domain.interactor.WindowManagerLockscreenVisibilityInteractor 24 import com.android.systemui.keyguard.shared.model.KeyguardState 25 import com.android.systemui.power.domain.interactor.PowerInteractor 26 import com.android.systemui.scene.shared.model.Scenes 27 import com.android.systemui.util.kotlin.sample 28 import javax.inject.Inject 29 import kotlinx.coroutines.flow.Flow 30 import kotlinx.coroutines.flow.combine 31 import kotlinx.coroutines.flow.distinctUntilChanged 32 import kotlinx.coroutines.flow.distinctUntilChangedBy 33 import kotlinx.coroutines.flow.filterNotNull 34 import kotlinx.coroutines.flow.map 35 import kotlinx.coroutines.flow.merge 36 37 /** 38 * Whether to set the status bar keyguard view occluded or not, and whether to animate that change. 39 */ 40 data class OccludedState(val occluded: Boolean, val animate: Boolean = false) 41 42 /** Handles logic around calls to [StatusBarKeyguardViewManager] in legacy code. */ 43 @Deprecated("Will be removed once all of SBKVM's responsibilies are refactored.") 44 @SysUISingleton 45 class StatusBarKeyguardViewManagerInteractor 46 @Inject 47 constructor( 48 keyguardTransitionInteractor: KeyguardTransitionInteractor, 49 keyguardOcclusionInteractor: KeyguardOcclusionInteractor, 50 powerInteractor: PowerInteractor, 51 wmLockscreenVisibilityInteractor: WindowManagerLockscreenVisibilityInteractor, 52 surfaceBehindInteractor: KeyguardSurfaceBehindInteractor, 53 ) { 54 /** Occlusion state to apply whenever a keyguard transition is STARTED, if any. */ 55 private val occlusionStateFromStartedStep: Flow<OccludedState> = 56 keyguardTransitionInteractor.startedKeyguardTransitionStep 57 .sample(powerInteractor.detailedWakefulness, ::Pair) 58 .map { (startedStep, wakefulness) -> 59 val transitioningFromPowerButtonGesture = 60 KeyguardState.deviceIsAsleepInState(startedStep.from) && 61 startedStep.to == KeyguardState.OCCLUDED && 62 wakefulness.powerButtonLaunchGestureTriggered 63 64 if ( 65 startedStep.to == KeyguardState.OCCLUDED && !transitioningFromPowerButtonGesture 66 ) { 67 // Set occluded upon STARTED, *unless* we're transitioning from the power 68 // button, in which case we're going to play an animation over the lockscreen UI 69 // and need to remain unoccluded until the transition finishes. 70 return@map OccludedState(occluded = true, animate = false) 71 } 72 73 if ( 74 startedStep.from == KeyguardState.OCCLUDED && 75 startedStep.to == KeyguardState.LOCKSCREEN 76 ) { 77 // Set unoccluded immediately ONLY if we're transitioning back to the lockscreen 78 // since we need the views visible to animate them back down. This is a special 79 // case due to the way unocclusion remote animations are run. We can remove this 80 // once the unocclude animation uses the return animation framework. 81 return@map OccludedState(occluded = false, animate = false) 82 } 83 84 // Otherwise, wait for the transition to FINISH to decide. 85 return@map null 86 } 87 .filterNotNull() 88 89 /** Occlusion state to apply whenever a keyguard transition is FINISHED. */ 90 private val occlusionStateFromFinishedStep = 91 combine( 92 keyguardTransitionInteractor.isFinishedIn( 93 content = Scenes.Gone, 94 stateWithoutSceneContainer = KeyguardState.GONE, 95 ), 96 keyguardTransitionInteractor.isFinishedIn(KeyguardState.OCCLUDED), 97 keyguardOcclusionInteractor.isShowWhenLockedActivityOnTop, 98 ::Triple, 99 ) 100 .map { (isOnGone, isOnOccluded, showWhenLockedOnTop) -> 101 // If we're FINISHED in OCCLUDED, we want to render as occluded. We also need to 102 // remain occluded if a SHOW_WHEN_LOCKED activity is on the top of the task stack, 103 // and we're in any state other than GONE. This is necessary, for example, when we 104 // transition from OCCLUDED to a bouncer state. Otherwise, we should not be 105 // occluded. 106 val occluded = isOnOccluded || (showWhenLockedOnTop && !isOnGone) 107 OccludedState(occluded = occluded, animate = false) 108 } 109 110 /** Occlusion state to apply to SBKVM's setOccluded call. */ 111 val keyguardViewOcclusionState = 112 merge(occlusionStateFromStartedStep, occlusionStateFromFinishedStep) 113 .distinctUntilChangedBy { 114 // Don't switch 'animate' values mid-transition. 115 it.occluded 116 } 117 118 /** Visibility state to apply to SBKVM via show() and hide(). */ 119 val keyguardViewVisibility = 120 combine( 121 wmLockscreenVisibilityInteractor.lockscreenVisibility, 122 surfaceBehindInteractor.isAnimatingSurface, 123 ) { lockscreenVisible, animatingSurface -> 124 lockscreenVisible || animatingSurface 125 } 126 .distinctUntilChanged() 127 } 128