1 /*
2  *  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 
18 package com.android.systemui.keyguard.domain.interactor
19 
20 import android.util.Log
21 import com.android.systemui.dagger.SysUISingleton
22 import com.android.systemui.dagger.qualifiers.Application
23 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
24 import com.android.systemui.keyguard.shared.model.KeyguardState
25 import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
26 import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
27 import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
28 import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
29 import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING_LOCKSCREEN_HOSTED
30 import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
31 import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
32 import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
33 import com.android.systemui.keyguard.shared.model.KeyguardState.OFF
34 import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
35 import com.android.systemui.keyguard.shared.model.TransitionState
36 import com.android.systemui.keyguard.shared.model.TransitionStep
37 import javax.inject.Inject
38 import kotlinx.coroutines.CoroutineScope
39 import kotlinx.coroutines.flow.Flow
40 import kotlinx.coroutines.flow.SharingStarted
41 import kotlinx.coroutines.flow.StateFlow
42 import kotlinx.coroutines.flow.combine
43 import kotlinx.coroutines.flow.filter
44 import kotlinx.coroutines.flow.map
45 import kotlinx.coroutines.flow.merge
46 import kotlinx.coroutines.flow.stateIn
47 
48 /** Encapsulates business-logic related to the keyguard transitions. */
49 @SysUISingleton
50 class KeyguardTransitionInteractor
51 @Inject
52 constructor(
53     @Application val scope: CoroutineScope,
54     private val repository: KeyguardTransitionRepository,
55     private val keyguardInteractor: dagger.Lazy<KeyguardInteractor>,
56     private val fromLockscreenTransitionInteractor: dagger.Lazy<FromLockscreenTransitionInteractor>,
57     private val fromPrimaryBouncerTransitionInteractor:
58         dagger.Lazy<FromPrimaryBouncerTransitionInteractor>,
59 ) {
60     private val TAG = this::class.simpleName
61 
62     /** (any)->GONE transition information */
63     val anyStateToGoneTransition: Flow<TransitionStep> =
64         repository.transitions.filter { step -> step.to == GONE }
65 
66     /** (any)->AOD transition information */
67     val anyStateToAodTransition: Flow<TransitionStep> =
68         repository.transitions.filter { step -> step.to == AOD }
69 
70     /** DREAMING->(any) transition information. */
71     val fromDreamingTransition: Flow<TransitionStep> =
72         repository.transitions.filter { step -> step.from == DREAMING }
73 
74     /** (any)->Lockscreen transition information */
75     val anyStateToLockscreenTransition: Flow<TransitionStep> =
76         repository.transitions.filter { step -> step.to == LOCKSCREEN }
77 
78     /** (any)->Occluded transition information */
79     val anyStateToOccludedTransition: Flow<TransitionStep> =
80         repository.transitions.filter { step -> step.to == OCCLUDED }
81 
82     /** (any)->PrimaryBouncer transition information */
83     val anyStateToPrimaryBouncerTransition: Flow<TransitionStep> =
84         repository.transitions.filter { step -> step.to == PRIMARY_BOUNCER }
85 
86     /** (any)->Dreaming transition information */
87     val anyStateToDreamingTransition: Flow<TransitionStep> =
88         repository.transitions.filter { step -> step.to == DREAMING }
89 
90     /** (any)->AlternateBouncer transition information */
91     val anyStateToAlternateBouncerTransition: Flow<TransitionStep> =
92         repository.transitions.filter { step -> step.to == ALTERNATE_BOUNCER }
93 
94     /** AOD->LOCKSCREEN transition information. */
95     val aodToLockscreenTransition: Flow<TransitionStep> = repository.transition(AOD, LOCKSCREEN)
96 
97     /** DREAMING->LOCKSCREEN transition information. */
98     val dreamingToLockscreenTransition: Flow<TransitionStep> =
99         repository.transition(DREAMING, LOCKSCREEN)
100 
101     /** DREAMING_LOCKSCREEN_HOSTED->LOCKSCREEN transition information. */
102     val dreamingLockscreenHostedToLockscreenTransition: Flow<TransitionStep> =
103         repository.transition(DREAMING_LOCKSCREEN_HOSTED, LOCKSCREEN)
104 
105     /** GONE->AOD transition information. */
106     val goneToAodTransition: Flow<TransitionStep> = repository.transition(GONE, AOD)
107 
108     /** GONE->DREAMING transition information. */
109     val goneToDreamingTransition: Flow<TransitionStep> = repository.transition(GONE, DREAMING)
110 
111     /** GONE->DREAMING_LOCKSCREEN_HOSTED transition information. */
112     val goneToDreamingLockscreenHostedTransition: Flow<TransitionStep> =
113         repository.transition(GONE, DREAMING_LOCKSCREEN_HOSTED)
114 
115     /** LOCKSCREEN->AOD transition information. */
116     val lockscreenToAodTransition: Flow<TransitionStep> = repository.transition(LOCKSCREEN, AOD)
117 
118     /** LOCKSCREEN->DREAMING transition information. */
119     val lockscreenToDreamingTransition: Flow<TransitionStep> =
120         repository.transition(LOCKSCREEN, DREAMING)
121 
122     /** LOCKSCREEN->DREAMING_LOCKSCREEN_HOSTED transition information. */
123     val lockscreenToDreamingLockscreenHostedTransition: Flow<TransitionStep> =
124         repository.transition(LOCKSCREEN, DREAMING_LOCKSCREEN_HOSTED)
125 
126     /** LOCKSCREEN->OCCLUDED transition information. */
127     val lockscreenToOccludedTransition: Flow<TransitionStep> =
128         repository.transition(LOCKSCREEN, OCCLUDED)
129 
130     /** OCCLUDED->LOCKSCREEN transition information. */
131     val occludedToLockscreenTransition: Flow<TransitionStep> =
132         repository.transition(OCCLUDED, LOCKSCREEN)
133 
134     /** PRIMARY_BOUNCER->GONE transition information. */
135     val primaryBouncerToGoneTransition: Flow<TransitionStep> =
136         repository.transition(PRIMARY_BOUNCER, GONE)
137 
138     /** OFF->LOCKSCREEN transition information. */
139     val offToLockscreenTransition: Flow<TransitionStep> = repository.transition(OFF, LOCKSCREEN)
140 
141     /** DOZING->LOCKSCREEN transition information. */
142     val dozingToLockscreenTransition: Flow<TransitionStep> =
143         repository.transition(DOZING, LOCKSCREEN)
144 
145     /**
146      * AOD<->LOCKSCREEN transition information, mapped to dozeAmount range of AOD (1f) <->
147      * Lockscreen (0f).
148      */
149     val dozeAmountTransition: Flow<TransitionStep> =
150         merge(
151             aodToLockscreenTransition.map { step -> step.copy(value = 1f - step.value) },
152             lockscreenToAodTransition,
153         )
154 
155     /** The last [TransitionStep] with a [TransitionState] of STARTED */
156     val startedKeyguardTransitionStep: Flow<TransitionStep> =
157         repository.transitions.filter { step -> step.transitionState == TransitionState.STARTED }
158 
159     /** The last [TransitionStep] with a [TransitionState] of CANCELED */
160     val canceledKeyguardTransitionStep: Flow<TransitionStep> =
161         repository.transitions.filter { step -> step.transitionState == TransitionState.CANCELED }
162 
163     /** The last [TransitionStep] with a [TransitionState] of FINISHED */
164     val finishedKeyguardTransitionStep: Flow<TransitionStep> =
165         repository.transitions.filter { step -> step.transitionState == TransitionState.FINISHED }
166 
167     /** The destination state of the last started transition. */
168     val startedKeyguardState: StateFlow<KeyguardState> =
169         startedKeyguardTransitionStep
170             .map { step -> step.to }
171             .stateIn(scope, SharingStarted.Eagerly, OFF)
172 
173     /** The last completed [KeyguardState] transition */
174     val finishedKeyguardState: StateFlow<KeyguardState> =
175         finishedKeyguardTransitionStep
176             .map { step -> step.to }
177             .stateIn(scope, SharingStarted.Eagerly, LOCKSCREEN)
178 
179     /**
180      * Whether we're currently in a transition to a new [KeyguardState] and haven't yet completed
181      * it.
182      */
183     val isInTransitionToAnyState =
184             combine(
185                     startedKeyguardTransitionStep,
186                     finishedKeyguardState,
187             ) { startedStep, finishedState ->
188                 startedStep.to != finishedState
189             }
190 
191     /**
192      * The amount of transition into or out of the given [KeyguardState].
193      *
194      * The value will be `0` (or close to `0`, due to float point arithmetic) if not in this step or
195      * `1` when fully in the given state.
196      */
197     fun transitionValue(
198         state: KeyguardState,
199     ): Flow<Float> {
200         return repository.transitions
201             .filter { it.from == state || it.to == state }
202             .map {
203                 if (it.from == state) {
204                     1 - it.value
205                 } else {
206                     it.value
207                 }
208             }
209     }
210 
211     fun transitionStepsFromState(fromState: KeyguardState): Flow<TransitionStep> {
212         return repository.transitions.filter { step -> step.from == fromState }
213     }
214 
215     fun transitionStepsToState(toState: KeyguardState): Flow<TransitionStep> {
216         return repository.transitions.filter { step -> step.to == toState }
217     }
218 
219     /**
220      * Called to start a transition that will ultimately dismiss the keyguard from the current
221      * state.
222      */
223     fun startDismissKeyguardTransition() {
224         when (startedKeyguardState.value) {
225             LOCKSCREEN -> fromLockscreenTransitionInteractor.get().dismissKeyguard()
226             PRIMARY_BOUNCER -> fromPrimaryBouncerTransitionInteractor.get().dismissPrimaryBouncer()
227             else ->
228                 Log.e(
229                     "KeyguardTransitionInteractor",
230                     "We don't know how to dismiss keyguard from state " +
231                         "${startedKeyguardState.value}"
232                 )
233         }
234     }
235 
236     /** Whether we're in a transition to the given [KeyguardState], but haven't yet completed it. */
237     fun isInTransitionToState(
238             state: KeyguardState,
239     ): Flow<Boolean> {
240         return combine(
241                 startedKeyguardTransitionStep,
242                 finishedKeyguardState,
243         ) { startedStep, finishedState ->
244             startedStep.to == state && finishedState != state
245         }
246     }
247 }
248