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 package com.android.systemui.keyguard.domain.interactor
18 
19 import com.android.systemui.dagger.SysUISingleton
20 import com.android.systemui.dagger.qualifiers.Application
21 import com.android.systemui.keyguard.data.repository.LightRevealScrimRepository
22 import com.android.systemui.keyguard.shared.model.KeyguardState
23 import com.android.systemui.keyguard.shared.model.TransitionStep
24 import com.android.systemui.statusbar.LightRevealEffect
25 import com.android.systemui.util.kotlin.sample
26 import javax.inject.Inject
27 import kotlinx.coroutines.CoroutineScope
28 import kotlinx.coroutines.ExperimentalCoroutinesApi
29 import kotlinx.coroutines.flow.Flow
30 import kotlinx.coroutines.launch
31 
32 @ExperimentalCoroutinesApi
33 @SysUISingleton
34 class LightRevealScrimInteractor
35 @Inject
36 constructor(
37     private val transitionInteractor: KeyguardTransitionInteractor,
38     private val lightRevealScrimRepository: LightRevealScrimRepository,
39     @Application private val scope: CoroutineScope,
40 ) {
41 
42     init {
43         listenForStartedKeyguardTransitionStep()
44     }
45 
46     private fun listenForStartedKeyguardTransitionStep() {
47         scope.launch {
48             transitionInteractor.startedKeyguardTransitionStep.collect {
49                 if (willTransitionChangeEndState(it)) {
50                     lightRevealScrimRepository.startRevealAmountAnimator(
51                         willBeRevealedInState(it.to)
52                     )
53                 }
54             }
55         }
56     }
57 
58     /**
59      * Whenever a keyguard transition starts, sample the latest reveal effect from the repository
60      * and use that for the starting transition.
61      *
62      * We can't simply use the nextRevealEffect since the effect may change midway through a
63      * transition, but we don't want to change effects part way through. For example, if we're using
64      * a CircleReveal to animate a biometric unlock, but the biometric unlock mode changes to NONE
65      * from WAKE_AND_UNLOCK before the unlock animation ends, we don't want to end up switching to a
66      * LiftReveal.
67      */
68     val lightRevealEffect: Flow<LightRevealEffect> =
69         transitionInteractor.startedKeyguardTransitionStep.sample(
70             lightRevealScrimRepository.revealEffect
71         )
72 
73     val revealAmount = lightRevealScrimRepository.revealAmount
74 
75     companion object {
76 
77         /**
78          * Whether the transition requires a change in the reveal amount of the light reveal scrim.
79          * If not, we don't care about the transition and don't need to listen to it.
80          */
81         fun willTransitionChangeEndState(transition: TransitionStep): Boolean {
82             return willBeRevealedInState(transition.from) != willBeRevealedInState(transition.to)
83         }
84 
85         /**
86          * Whether the light reveal scrim will be fully revealed (revealAmount = 1.0f) in the given
87          * state after the transition is complete. If false, scrim will be fully hidden.
88          */
89         fun willBeRevealedInState(state: KeyguardState): Boolean {
90             return when (state) {
91                 KeyguardState.OFF -> false
92                 KeyguardState.DOZING -> false
93                 KeyguardState.AOD -> false
94                 KeyguardState.DREAMING -> true
95                 KeyguardState.DREAMING_LOCKSCREEN_HOSTED -> true
96                 KeyguardState.ALTERNATE_BOUNCER -> true
97                 KeyguardState.PRIMARY_BOUNCER -> true
98                 KeyguardState.LOCKSCREEN -> true
99                 KeyguardState.GONE -> true
100                 KeyguardState.OCCLUDED -> true
101             }
102         }
103     }
104 }
105