1 /*
<lambda>null2  * 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 android.animation.ValueAnimator
20 import com.android.app.animation.Interpolators
21 import com.android.systemui.dagger.SysUISingleton
22 import com.android.systemui.dagger.qualifiers.Background
23 import com.android.systemui.dagger.qualifiers.Main
24 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
25 import com.android.systemui.keyguard.shared.model.BiometricUnlockMode
26 import com.android.systemui.keyguard.shared.model.DozeStateModel
27 import com.android.systemui.keyguard.shared.model.KeyguardState
28 import com.android.systemui.power.domain.interactor.PowerInteractor
29 import com.android.systemui.scene.shared.flag.SceneContainerFlag
30 import com.android.systemui.util.kotlin.sample
31 import javax.inject.Inject
32 import kotlin.time.Duration.Companion.milliseconds
33 import kotlinx.coroutines.CoroutineDispatcher
34 import kotlinx.coroutines.CoroutineScope
35 import kotlinx.coroutines.delay
36 import kotlinx.coroutines.flow.onEach
37 import kotlinx.coroutines.launch
38 
39 @SysUISingleton
40 class FromDreamingLockscreenHostedTransitionInteractor
41 @Inject
42 constructor(
43     override val transitionRepository: KeyguardTransitionRepository,
44     transitionInteractor: KeyguardTransitionInteractor,
45     @Background private val scope: CoroutineScope,
46     @Background bgDispatcher: CoroutineDispatcher,
47     @Main mainDispatcher: CoroutineDispatcher,
48     keyguardInteractor: KeyguardInteractor,
49     powerInteractor: PowerInteractor,
50     keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
51 ) :
52     TransitionInteractor(
53         fromState = KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
54         transitionInteractor = transitionInteractor,
55         mainDispatcher = mainDispatcher,
56         bgDispatcher = bgDispatcher,
57         powerInteractor = powerInteractor,
58         keyguardOcclusionInteractor = keyguardOcclusionInteractor,
59         keyguardInteractor = keyguardInteractor,
60     ) {
61 
62     override fun start() {
63         listenForDreamingLockscreenHostedToLockscreen()
64         listenForDreamingLockscreenHostedToGone()
65         listenForDreamingLockscreenHostedToDozing()
66         listenForDreamingLockscreenHostedToOccluded()
67         listenForDreamingLockscreenHostedToPrimaryBouncer()
68     }
69 
70     private fun listenForDreamingLockscreenHostedToLockscreen() {
71         scope.launch {
72             keyguardInteractor.isActiveDreamLockscreenHosted
73                 // Add a slight delay to prevent transitioning to lockscreen from happening too soon
74                 // as dozing can arrive in a slight gap after the lockscreen hosted dream stops.
75                 .onEach { delay(50) }
76                 .sample(keyguardInteractor.dozeTransitionModel, ::Pair)
77                 .filterRelevantKeyguardStateAnd {
78                     (isActiveDreamLockscreenHosted, dozeTransitionModel) ->
79                     !isActiveDreamLockscreenHosted &&
80                         DozeStateModel.isDozeOff(dozeTransitionModel.to)
81                 }
82                 .collect { startTransitionTo(KeyguardState.LOCKSCREEN) }
83         }
84     }
85 
86     private fun listenForDreamingLockscreenHostedToOccluded() {
87         scope.launch {
88             keyguardInteractor.isActiveDreamLockscreenHosted
89                 .sample(keyguardInteractor.isKeyguardOccluded, ::Pair)
90                 .filterRelevantKeyguardStateAnd { (isActiveDreamLockscreenHosted, isOccluded) ->
91                     isOccluded && !isActiveDreamLockscreenHosted
92                 }
93                 .collect { startTransitionTo(KeyguardState.OCCLUDED) }
94         }
95     }
96 
97     private fun listenForDreamingLockscreenHostedToPrimaryBouncer() {
98         // TODO(b/336576536): Check if adaptation for scene framework is needed
99         if (SceneContainerFlag.isEnabled) return
100         scope.launch {
101             keyguardInteractor.primaryBouncerShowing
102                 .filterRelevantKeyguardStateAnd { isBouncerShowing -> isBouncerShowing }
103                 .collect { startTransitionTo(KeyguardState.PRIMARY_BOUNCER) }
104         }
105     }
106 
107     private fun listenForDreamingLockscreenHostedToGone() {
108         // TODO(b/336576536): Check if adaptation for scene framework is needed
109         if (SceneContainerFlag.isEnabled) return
110         scope.launch {
111             keyguardInteractor.biometricUnlockState
112                 .filterRelevantKeyguardStateAnd { biometricUnlockState ->
113                     biometricUnlockState.mode == BiometricUnlockMode.WAKE_AND_UNLOCK_FROM_DREAM
114                 }
115                 .collect { startTransitionTo(KeyguardState.GONE) }
116         }
117     }
118 
119     private fun listenForDreamingLockscreenHostedToDozing() {
120         scope.launch {
121             keyguardInteractor.dozeTransitionModel
122                 .filterRelevantKeyguardStateAnd { it.to == DozeStateModel.DOZE }
123                 .collect { startTransitionTo(KeyguardState.DOZING) }
124         }
125     }
126 
127     override fun getDefaultAnimatorForTransitionsToState(toState: KeyguardState): ValueAnimator {
128         return ValueAnimator().apply {
129             interpolator = Interpolators.LINEAR
130             duration =
131                 if (toState == KeyguardState.LOCKSCREEN) TO_LOCKSCREEN_DURATION.inWholeMilliseconds
132                 else DEFAULT_DURATION.inWholeMilliseconds
133         }
134     }
135 
136     companion object {
137         private val DEFAULT_DURATION = 500.milliseconds
138         val TO_LOCKSCREEN_DURATION = 1167.milliseconds
139     }
140 }
141