1 /* 2 * 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.keyguard.domain.interactor 18 19 import com.android.systemui.dagger.SysUISingleton 20 import com.android.systemui.keyguard.shared.model.BiometricUnlockModel 21 import com.android.systemui.keyguard.shared.model.KeyguardState 22 import kotlinx.coroutines.ExperimentalCoroutinesApi 23 import kotlinx.coroutines.flow.Flow 24 import kotlinx.coroutines.flow.combine 25 import kotlinx.coroutines.flow.distinctUntilChanged 26 import kotlinx.coroutines.flow.flatMapLatest 27 import kotlinx.coroutines.flow.flowOf 28 import kotlinx.coroutines.flow.map 29 import javax.inject.Inject 30 31 @SysUISingleton 32 class WindowManagerLockscreenVisibilityInteractor 33 @Inject 34 constructor( 35 keyguardInteractor: KeyguardInteractor, 36 transitionInteractor: KeyguardTransitionInteractor, 37 surfaceBehindInteractor: KeyguardSurfaceBehindInteractor, 38 fromLockscreenInteractor: FromLockscreenTransitionInteractor, 39 fromBouncerInteractor: FromPrimaryBouncerTransitionInteractor, 40 ) { 41 private val defaultSurfaceBehindVisibility = 42 transitionInteractor.finishedKeyguardState.map(::isSurfaceVisible) 43 44 /** 45 * Surface visibility provided by the From*TransitionInteractor responsible for the currently 46 * RUNNING transition, or null if the current transition does not require special surface 47 * visibility handling. 48 * 49 * An example of transition-specific visibility is swipe to unlock, where the surface should 50 * only be visible after swiping 20% of the way up the screen, and should become invisible again 51 * if the user swipes back down. 52 */ 53 @OptIn(ExperimentalCoroutinesApi::class) 54 private val transitionSpecificSurfaceBehindVisibility: Flow<Boolean?> = 55 transitionInteractor.startedKeyguardTransitionStep 56 .flatMapLatest { startedStep -> 57 when (startedStep.from) { 58 KeyguardState.LOCKSCREEN -> { 59 fromLockscreenInteractor.surfaceBehindVisibility 60 } 61 KeyguardState.PRIMARY_BOUNCER -> { 62 fromBouncerInteractor.surfaceBehindVisibility 63 } 64 else -> flowOf(null) 65 } 66 } 67 .distinctUntilChanged() 68 69 /** 70 * Surface visibility, which is either determined by the default visibility in the FINISHED 71 * KeyguardState, or the transition-specific visibility used during certain RUNNING transitions. 72 */ 73 @OptIn(ExperimentalCoroutinesApi::class) 74 val surfaceBehindVisibility: Flow<Boolean> = 75 transitionInteractor 76 .isInTransitionToAnyState 77 .flatMapLatest { isInTransition -> 78 if (!isInTransition) { 79 defaultSurfaceBehindVisibility 80 } else { 81 combine( 82 transitionSpecificSurfaceBehindVisibility, 83 defaultSurfaceBehindVisibility, 84 ) { transitionVisibility, defaultVisibility -> 85 // Defer to the transition-specific visibility since we're RUNNING a 86 // transition, but fall back to the default visibility if the current 87 // transition's interactor did not specify a visibility. 88 transitionVisibility ?: defaultVisibility 89 } 90 } 91 } 92 .distinctUntilChanged() 93 94 /** 95 * Whether we're animating, or intend to animate, the surface behind the keyguard via remote 96 * animation. This is used to keep the RemoteAnimationTarget alive until we're done using it. 97 */ 98 val usingKeyguardGoingAwayAnimation: Flow<Boolean> = 99 combine( 100 transitionInteractor.isInTransitionToState(KeyguardState.GONE), 101 transitionInteractor.finishedKeyguardState, 102 surfaceBehindInteractor.isAnimatingSurface 103 ) { isInTransitionToGone, finishedState, isAnimatingSurface -> 104 // We may still be animating the surface after the keyguard is fully GONE, since 105 // some animations (like the translation spring) are not tied directly to the 106 // transition step amount. 107 isInTransitionToGone || (finishedState == KeyguardState.GONE && isAnimatingSurface) 108 } 109 .distinctUntilChanged() 110 111 /** 112 * Whether the lockscreen is visible, from the Window Manager (WM) perspective. 113 * 114 * Note: This may briefly be true even if the lockscreen UI has animated out (alpha = 0f), as we 115 * only inform WM once we're done with the keyguard and we're fully GONE. Don't use this if you 116 * want to know if the AOD/clock/notifs/etc. are visible. 117 */ 118 val lockscreenVisibility: Flow<Boolean> = 119 combine( 120 transitionInteractor.startedKeyguardTransitionStep, 121 transitionInteractor.finishedKeyguardState, 122 ) { startedStep, finishedState -> 123 // If we finished the transition, use the finished state. If we're running a 124 // transition, use the state we're transitioning FROM. This can be different from 125 // the last finished state if a transition is interrupted. For example, if we were 126 // transitioning from GONE to AOD and then started AOD -> LOCKSCREEN mid-transition, 127 // we want to immediately use the visibility for AOD (lockscreenVisibility=true) 128 // even though the lastFinishedState is still GONE (lockscreenVisibility=false). 129 if (finishedState == startedStep.to) finishedState else startedStep.from 130 } 131 .map(::isLockscreenVisible) 132 .distinctUntilChanged() 133 134 /** 135 * Whether always-on-display (AOD) is visible when the lockscreen is visible, from window 136 * manager's perspective. 137 * 138 * Note: This may be true even if AOD is not user-visible, such as when the light sensor 139 * indicates the device is in the user's pocket. Don't use this if you want to know if the AOD 140 * clock/smartspace/notif icons are visible. 141 */ 142 val aodVisibility: Flow<Boolean> = 143 combine( 144 keyguardInteractor.isDozing, 145 keyguardInteractor.biometricUnlockState, 146 ) { isDozing, biometricUnlockState -> 147 // AOD is visible if we're dozing, unless we are wake and unlocking (where we go 148 // directly from AOD to unlocked while dozing). 149 isDozing && !BiometricUnlockModel.isWakeAndUnlock(biometricUnlockState) 150 } 151 .distinctUntilChanged() 152 153 companion object { 154 fun isSurfaceVisible(state: KeyguardState): Boolean { 155 return !isLockscreenVisible(state) 156 } 157 158 fun isLockscreenVisible(state: KeyguardState): Boolean { 159 return state != KeyguardState.GONE 160 } 161 } 162 } 163