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