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