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