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