1 /* <lambda>null2 * 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 android.animation.ValueAnimator 20 import com.android.app.animation.Interpolators 21 import com.android.app.tracing.coroutines.launch 22 import com.android.systemui.dagger.SysUISingleton 23 import com.android.systemui.dagger.qualifiers.Background 24 import com.android.systemui.dagger.qualifiers.Main 25 import com.android.systemui.deviceentry.data.repository.DeviceEntryRepository 26 import com.android.systemui.keyguard.KeyguardWmStateRefactor 27 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository 28 import com.android.systemui.keyguard.shared.model.BiometricUnlockMode.Companion.isWakeAndUnlock 29 import com.android.systemui.keyguard.shared.model.KeyguardState 30 import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled 31 import com.android.systemui.power.domain.interactor.PowerInteractor 32 import com.android.systemui.scene.shared.flag.SceneContainerFlag 33 import com.android.systemui.util.kotlin.Utils.Companion.sample 34 import javax.inject.Inject 35 import kotlin.time.Duration.Companion.milliseconds 36 import kotlinx.coroutines.CoroutineDispatcher 37 import kotlinx.coroutines.CoroutineScope 38 import kotlinx.coroutines.flow.Flow 39 import kotlinx.coroutines.flow.combine 40 import kotlinx.coroutines.flow.debounce 41 42 @SysUISingleton 43 class FromAodTransitionInteractor 44 @Inject 45 constructor( 46 override val transitionRepository: KeyguardTransitionRepository, 47 transitionInteractor: KeyguardTransitionInteractor, 48 @Background private val scope: CoroutineScope, 49 @Background bgDispatcher: CoroutineDispatcher, 50 @Main mainDispatcher: CoroutineDispatcher, 51 keyguardInteractor: KeyguardInteractor, 52 powerInteractor: PowerInteractor, 53 keyguardOcclusionInteractor: KeyguardOcclusionInteractor, 54 val deviceEntryRepository: DeviceEntryRepository, 55 ) : 56 TransitionInteractor( 57 fromState = KeyguardState.AOD, 58 transitionInteractor = transitionInteractor, 59 mainDispatcher = mainDispatcher, 60 bgDispatcher = bgDispatcher, 61 powerInteractor = powerInteractor, 62 keyguardOcclusionInteractor = keyguardOcclusionInteractor, 63 keyguardInteractor = keyguardInteractor, 64 ) { 65 66 override fun start() { 67 listenForAodToAwake() 68 listenForAodToOccluded() 69 listenForAodToPrimaryBouncer() 70 listenForTransitionToCamera(scope, keyguardInteractor) 71 } 72 73 private val canDismissLockscreen: Flow<Boolean> = 74 combine( 75 keyguardInteractor.isKeyguardShowing, 76 keyguardInteractor.isKeyguardDismissible, 77 keyguardInteractor.biometricUnlockState, 78 ) { isKeyguardShowing, isKeyguardDismissible, biometricUnlockState -> 79 (isWakeAndUnlock(biometricUnlockState.mode) || 80 (!isKeyguardShowing && isKeyguardDismissible)) 81 } 82 83 /** 84 * Listen for the signal that we're waking up and figure what state we need to transition to. 85 */ 86 private fun listenForAodToAwake() { 87 // Use PowerInteractor's wakefulness, which is the earliest wake signal available. We 88 // have all of the information we need at this time to make a decision about where to 89 // transition. 90 scope.launch("$TAG#listenForAodToAwake") { 91 powerInteractor.detailedWakefulness 92 .filterRelevantKeyguardStateAnd { wakefulness -> wakefulness.isAwake() } 93 .debounce(50L) 94 .sample( 95 startedKeyguardTransitionStep, 96 keyguardInteractor.biometricUnlockState, 97 keyguardInteractor.primaryBouncerShowing, 98 keyguardInteractor.isKeyguardOccluded, 99 canDismissLockscreen, 100 ) 101 .collect { 102 ( 103 _, 104 startedStep, 105 biometricUnlockState, 106 primaryBouncerShowing, 107 isKeyguardOccludedLegacy, 108 canDismissLockscreen, 109 ) -> 110 if (!maybeHandleInsecurePowerGesture()) { 111 val shouldTransitionToLockscreen = 112 if (KeyguardWmStateRefactor.isEnabled) { 113 // Check with the superclass to see if an occlusion transition is 114 // needed. Also, don't react to wake and unlock events, as we'll be 115 // receiving a call to #dismissAod() shortly when the authentication 116 // completes. 117 !maybeStartTransitionToOccludedOrInsecureCamera() && 118 !isWakeAndUnlock(biometricUnlockState.mode) && 119 !primaryBouncerShowing 120 } else { 121 !isKeyguardOccludedLegacy && 122 !isWakeAndUnlock(biometricUnlockState.mode) && 123 !primaryBouncerShowing 124 } 125 126 // With the refactor enabled, maybeStartTransitionToOccludedOrInsecureCamera 127 // handles transitioning to OCCLUDED. 128 val shouldTransitionToOccluded = 129 !KeyguardWmStateRefactor.isEnabled && isKeyguardOccludedLegacy 130 131 val shouldTransitionToGone = 132 (!KeyguardWmStateRefactor.isEnabled && canDismissLockscreen) || 133 (KeyguardWmStateRefactor.isEnabled && 134 !deviceEntryRepository.isLockscreenEnabled()) 135 136 if (shouldTransitionToGone) { 137 startTransitionTo( 138 toState = KeyguardState.GONE, 139 ) 140 } else if (shouldTransitionToLockscreen) { 141 val modeOnCanceled = 142 if (startedStep.from == KeyguardState.LOCKSCREEN) { 143 TransitionModeOnCanceled.REVERSE 144 } else if (startedStep.from == KeyguardState.GONE) { 145 TransitionModeOnCanceled.RESET 146 } else { 147 TransitionModeOnCanceled.LAST_VALUE 148 } 149 150 startTransitionTo( 151 toState = KeyguardState.LOCKSCREEN, 152 modeOnCanceled = modeOnCanceled, 153 ownerReason = "listen for aod to awake" 154 ) 155 } else if (shouldTransitionToOccluded) { 156 startTransitionTo( 157 toState = KeyguardState.OCCLUDED, 158 ownerReason = "waking up and isOccluded=true", 159 ) 160 } 161 } 162 } 163 } 164 } 165 166 /** 167 * There are cases where the transition to AOD begins but never completes, such as tapping power 168 * during an incoming phone call when unlocked. In this case, GONE->AOD should be interrupted to 169 * run AOD->OCCLUDED. 170 */ 171 private fun listenForAodToOccluded() { 172 if (KeyguardWmStateRefactor.isEnabled) { 173 // Handled by calls to maybeStartTransitionToOccludedOrInsecureCamera on waking. 174 return 175 } 176 177 scope.launch("$TAG#listenForAodToOccluded") { 178 keyguardInteractor.isKeyguardOccluded 179 .filterRelevantKeyguardStateAnd { isOccluded -> isOccluded } 180 .collect { 181 if (!maybeHandleInsecurePowerGesture()) { 182 startTransitionTo( 183 toState = KeyguardState.OCCLUDED, 184 modeOnCanceled = TransitionModeOnCanceled.RESET, 185 ownerReason = "isOccluded = true", 186 ) 187 } 188 } 189 } 190 } 191 192 /** 193 * If there is a biometric lockout and FPS is tapped while on AOD, it should go directly to the 194 * PRIMARY_BOUNCER. 195 */ 196 private fun listenForAodToPrimaryBouncer() { 197 if (SceneContainerFlag.isEnabled) return 198 scope.launch("$TAG#listenForAodToPrimaryBouncer") { 199 keyguardInteractor.primaryBouncerShowing 200 .filterRelevantKeyguardStateAnd { primaryBouncerShowing -> primaryBouncerShowing } 201 .collect { startTransitionTo(KeyguardState.PRIMARY_BOUNCER) } 202 } 203 } 204 205 /** 206 * Dismisses AOD and transitions to GONE. This is called whenever authentication occurs while on 207 * AOD. 208 */ 209 fun dismissAod() { 210 scope.launch("$TAG#dismissAod") { startTransitionTo(KeyguardState.GONE) } 211 } 212 213 override fun getDefaultAnimatorForTransitionsToState(toState: KeyguardState): ValueAnimator { 214 return ValueAnimator().apply { 215 interpolator = Interpolators.LINEAR 216 duration = 217 when (toState) { 218 KeyguardState.LOCKSCREEN -> TO_LOCKSCREEN_DURATION 219 else -> DEFAULT_DURATION 220 }.inWholeMilliseconds 221 } 222 } 223 224 companion object { 225 private const val TAG = "FromAodTransitionInteractor" 226 private val DEFAULT_DURATION = 500.milliseconds 227 val TO_LOCKSCREEN_DURATION = 500.milliseconds 228 val TO_GONE_DURATION = DEFAULT_DURATION 229 val TO_OCCLUDED_DURATION = DEFAULT_DURATION 230 val TO_PRIMARY_BOUNCER_DURATION = DEFAULT_DURATION 231 } 232 } 233