1 /*
<lambda>null2  * Copyright (C) 2022 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 android.animation.ValueAnimator
20 import com.android.keyguard.KeyguardSecurityModel
21 import com.android.systemui.communal.domain.interactor.CommunalInteractor
22 import com.android.systemui.dagger.SysUISingleton
23 import com.android.systemui.dagger.qualifiers.Background
24 import com.android.systemui.dagger.qualifiers.Main
25 import com.android.systemui.keyguard.KeyguardWmStateRefactor
26 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
27 import com.android.systemui.keyguard.shared.model.Edge
28 import com.android.systemui.keyguard.shared.model.KeyguardState
29 import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
30 import com.android.systemui.keyguard.shared.model.TransitionStep
31 import com.android.systemui.power.domain.interactor.PowerInteractor
32 import com.android.systemui.scene.shared.flag.SceneContainerFlag
33 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
34 import com.android.systemui.util.kotlin.Utils.Companion.sample
35 import com.android.systemui.util.kotlin.sample
36 import com.android.wm.shell.animation.Interpolators
37 import javax.inject.Inject
38 import kotlin.time.Duration.Companion.milliseconds
39 import kotlinx.coroutines.CoroutineDispatcher
40 import kotlinx.coroutines.CoroutineScope
41 import kotlinx.coroutines.flow.Flow
42 import kotlinx.coroutines.flow.distinctUntilChanged
43 import kotlinx.coroutines.flow.flowOf
44 import kotlinx.coroutines.flow.map
45 import kotlinx.coroutines.flow.onStart
46 import kotlinx.coroutines.launch
47 
48 @SysUISingleton
49 class FromPrimaryBouncerTransitionInteractor
50 @Inject
51 constructor(
52     override val transitionRepository: KeyguardTransitionRepository,
53     transitionInteractor: KeyguardTransitionInteractor,
54     @Background private val scope: CoroutineScope,
55     @Background bgDispatcher: CoroutineDispatcher,
56     @Main mainDispatcher: CoroutineDispatcher,
57     keyguardInteractor: KeyguardInteractor,
58     private val communalInteractor: CommunalInteractor,
59     private val keyguardSecurityModel: KeyguardSecurityModel,
60     private val selectedUserInteractor: SelectedUserInteractor,
61     powerInteractor: PowerInteractor,
62     keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
63 ) :
64     TransitionInteractor(
65         fromState = KeyguardState.PRIMARY_BOUNCER,
66         transitionInteractor = transitionInteractor,
67         mainDispatcher = mainDispatcher,
68         bgDispatcher = bgDispatcher,
69         powerInteractor = powerInteractor,
70         keyguardOcclusionInteractor = keyguardOcclusionInteractor,
71         keyguardInteractor = keyguardInteractor,
72     ) {
73 
74     override fun start() {
75         listenForPrimaryBouncerToGone()
76         listenForPrimaryBouncerToAsleep()
77         listenForPrimaryBouncerToLockscreenHubOrOccluded()
78         listenForPrimaryBouncerToDreamingLockscreenHosted()
79         listenForTransitionToCamera(scope, keyguardInteractor)
80     }
81 
82     val surfaceBehindVisibility: Flow<Boolean?> =
83         if (SceneContainerFlag.isEnabled) {
84             // The edge Scenes.Bouncer <-> Scenes.Gone is handled by STL
85             flowOf(null)
86         } else {
87             transitionInteractor
88                 .transition(
89                     edge = Edge.INVALID,
90                     edgeWithoutSceneContainer =
91                         Edge.create(from = KeyguardState.PRIMARY_BOUNCER, to = KeyguardState.GONE)
92                 )
93                 .map<TransitionStep, Boolean?> {
94                     it.value > TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD
95                 }
96                 .onStart {
97                     // Default to null ("don't care, use a reasonable default").
98                     emit(null)
99                 }
100                 .distinctUntilChanged()
101         }
102 
103     fun dismissPrimaryBouncer() {
104         scope.launch { startTransitionTo(KeyguardState.GONE) }
105     }
106 
107     private fun listenForPrimaryBouncerToLockscreenHubOrOccluded() {
108         // TODO(b/336576536): Check if adaptation for scene framework is needed
109         if (SceneContainerFlag.isEnabled) return
110         if (KeyguardWmStateRefactor.isEnabled) {
111             scope.launch {
112                 keyguardInteractor.primaryBouncerShowing
113                     .sample(
114                         powerInteractor.isAwake,
115                         keyguardInteractor.isActiveDreamLockscreenHosted,
116                         communalInteractor.isIdleOnCommunal
117                     )
118                     .filterRelevantKeyguardState()
119                     .collect {
120                         (isBouncerShowing, isAwake, isActiveDreamLockscreenHosted, isIdleOnCommunal)
121                         ->
122                         if (
123                             !maybeStartTransitionToOccludedOrInsecureCamera() &&
124                                 !isBouncerShowing &&
125                                 isAwake &&
126                                 !isActiveDreamLockscreenHosted
127                         ) {
128                             val toState =
129                                 if (isIdleOnCommunal) {
130                                     KeyguardState.GLANCEABLE_HUB
131                                 } else {
132                                     KeyguardState.LOCKSCREEN
133                                 }
134                             startTransitionTo(toState)
135                         }
136                     }
137             }
138         } else {
139             scope.launch {
140                 keyguardInteractor.primaryBouncerShowing
141                     .sample(
142                         powerInteractor.isAwake,
143                         keyguardInteractor.isKeyguardOccluded,
144                         keyguardInteractor.isDreaming,
145                         keyguardInteractor.isActiveDreamLockscreenHosted,
146                         communalInteractor.isIdleOnCommunal,
147                     )
148                     .filterRelevantKeyguardStateAnd {
149                         (isBouncerShowing, isAwake, _, _, isActiveDreamLockscreenHosted, _) ->
150                         !isBouncerShowing && isAwake && !isActiveDreamLockscreenHosted
151                     }
152                     .collect { (_, _, occluded, isDreaming, _, isIdleOnCommunal) ->
153                         val toState =
154                             if (occluded && !isDreaming) {
155                                 KeyguardState.OCCLUDED
156                             } else if (isIdleOnCommunal) {
157                                 KeyguardState.GLANCEABLE_HUB
158                             } else if (isDreaming) {
159                                 KeyguardState.DREAMING
160                             } else {
161                                 KeyguardState.LOCKSCREEN
162                             }
163                         startTransitionTo(toState)
164                     }
165             }
166         }
167     }
168 
169     private fun listenForPrimaryBouncerToAsleep() {
170         // TODO(b/336576536): Check if adaptation for scene framework is needed
171         if (SceneContainerFlag.isEnabled) return
172         scope.launch { listenForSleepTransition() }
173     }
174 
175     private fun listenForPrimaryBouncerToDreamingLockscreenHosted() {
176         // TODO(b/336576536): Check if adaptation for scene framework is needed
177         if (SceneContainerFlag.isEnabled) return
178         scope.launch {
179             keyguardInteractor.primaryBouncerShowing
180                 .sample(keyguardInteractor.isActiveDreamLockscreenHosted, ::Pair)
181                 .filterRelevantKeyguardStateAnd { (isBouncerShowing, isActiveDreamLockscreenHosted)
182                     ->
183                     !isBouncerShowing && isActiveDreamLockscreenHosted
184                 }
185                 .collect { startTransitionTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED) }
186         }
187     }
188 
189     private fun listenForPrimaryBouncerToGone() {
190         // TODO(b/336576536): Check if adaptation for scene framework is needed
191         if (SceneContainerFlag.isEnabled) return
192         if (KeyguardWmStateRefactor.isEnabled) {
193             // This is handled in KeyguardSecurityContainerController and
194             // StatusBarKeyguardViewManager, which calls the transition interactor to kick off a
195             // transition vs. listening to legacy state flags.
196             return
197         }
198 
199         scope.launch {
200             keyguardInteractor.isKeyguardGoingAway
201                 .filterRelevantKeyguardStateAnd { isKeyguardGoingAway -> isKeyguardGoingAway }
202                 .collect {
203                     val securityMode =
204                         keyguardSecurityModel.getSecurityMode(
205                             selectedUserInteractor.getSelectedUserId()
206                         )
207                     // IME for password requires a slightly faster animation
208                     val duration =
209                         if (securityMode == KeyguardSecurityModel.SecurityMode.Password) {
210                             TO_GONE_SHORT_DURATION
211                         } else {
212                             TO_GONE_DURATION
213                         }
214 
215                     startTransitionTo(
216                         toState = KeyguardState.GONE,
217                         animator =
218                             getDefaultAnimatorForTransitionsToState(KeyguardState.GONE).apply {
219                                 this.duration = duration.inWholeMilliseconds
220                             },
221                         modeOnCanceled = TransitionModeOnCanceled.RESET,
222                     )
223                 }
224         }
225     }
226 
227     override fun getDefaultAnimatorForTransitionsToState(toState: KeyguardState): ValueAnimator {
228         return ValueAnimator().apply {
229             interpolator = Interpolators.LINEAR
230             duration =
231                 when (toState) {
232                     KeyguardState.AOD -> TO_AOD_DURATION
233                     KeyguardState.DOZING -> TO_DOZING_DURATION
234                     KeyguardState.GONE -> TO_GONE_DURATION
235                     KeyguardState.LOCKSCREEN -> TO_LOCKSCREEN_DURATION
236                     else -> DEFAULT_DURATION
237                 }.inWholeMilliseconds
238         }
239     }
240 
241     companion object {
242         private val DEFAULT_DURATION = 300.milliseconds
243         val TO_AOD_DURATION = DEFAULT_DURATION
244         val TO_DOZING_DURATION = DEFAULT_DURATION
245         val TO_GONE_DURATION = 500.milliseconds
246         val TO_GONE_SHORT_DURATION = 200.milliseconds
247         val TO_LOCKSCREEN_DURATION = 450.milliseconds
248         val TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD = 0.5f
249     }
250 }
251