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 android.animation.ValueAnimator 20 import com.android.keyguard.KeyguardSecurityModel 21 import com.android.keyguard.KeyguardUpdateMonitor 22 import com.android.systemui.dagger.SysUISingleton 23 import com.android.systemui.dagger.qualifiers.Application 24 import com.android.systemui.flags.FeatureFlags 25 import com.android.systemui.flags.Flags 26 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository 27 import com.android.systemui.keyguard.shared.model.KeyguardState 28 import com.android.systemui.keyguard.shared.model.KeyguardSurfaceBehindModel 29 import com.android.systemui.keyguard.shared.model.WakefulnessState 30 import com.android.systemui.util.kotlin.Utils.Companion.toQuad 31 import com.android.systemui.util.kotlin.Utils.Companion.toQuint 32 import com.android.systemui.util.kotlin.Utils.Companion.toTriple 33 import com.android.systemui.util.kotlin.sample 34 import com.android.wm.shell.animation.Interpolators 35 import javax.inject.Inject 36 import kotlin.time.Duration.Companion.milliseconds 37 import kotlinx.coroutines.CoroutineScope 38 import kotlinx.coroutines.flow.Flow 39 import kotlinx.coroutines.flow.combine 40 import kotlinx.coroutines.flow.distinctUntilChanged 41 import kotlinx.coroutines.flow.onStart 42 import kotlinx.coroutines.launch 43 44 @SysUISingleton 45 class FromPrimaryBouncerTransitionInteractor 46 @Inject 47 constructor( 48 override val transitionRepository: KeyguardTransitionRepository, 49 override val transitionInteractor: KeyguardTransitionInteractor, 50 @Application private val scope: CoroutineScope, 51 private val keyguardInteractor: KeyguardInteractor, 52 private val flags: FeatureFlags, 53 private val keyguardSecurityModel: KeyguardSecurityModel, 54 ) : 55 TransitionInteractor( 56 fromState = KeyguardState.PRIMARY_BOUNCER, 57 ) { 58 59 override fun start() { 60 listenForPrimaryBouncerToGone() 61 listenForPrimaryBouncerToAodOrDozing() 62 listenForPrimaryBouncerToLockscreenOrOccluded() 63 listenForPrimaryBouncerToDreamingLockscreenHosted() 64 } 65 66 val surfaceBehindVisibility: Flow<Boolean?> = 67 combine( 68 transitionInteractor.startedKeyguardTransitionStep, 69 transitionInteractor.transitionStepsFromState(KeyguardState.PRIMARY_BOUNCER) 70 ) { startedStep, fromBouncerStep -> 71 if (startedStep.to != KeyguardState.GONE) { 72 return@combine null 73 } 74 75 fromBouncerStep.value > 0.5f 76 } 77 .onStart { 78 // Default to null ("don't care, use a reasonable default"). 79 emit(null) 80 } 81 .distinctUntilChanged() 82 83 val surfaceBehindModel: Flow<KeyguardSurfaceBehindModel?> = 84 combine( 85 transitionInteractor.startedKeyguardTransitionStep, 86 transitionInteractor.transitionStepsFromState(KeyguardState.PRIMARY_BOUNCER) 87 ) { startedStep, fromBouncerStep -> 88 if (startedStep.to != KeyguardState.GONE) { 89 // BOUNCER to anything but GONE does not require any special surface 90 // visibility handling. 91 return@combine null 92 } 93 94 if (fromBouncerStep.value > 0.5f) { 95 KeyguardSurfaceBehindModel( 96 animateFromAlpha = 0f, 97 alpha = 1f, 98 animateFromTranslationY = 500f, 99 translationY = 0f, 100 ) 101 } else { 102 KeyguardSurfaceBehindModel( 103 alpha = 0f, 104 ) 105 } 106 } 107 .onStart { 108 // Default to null ("don't care, use a reasonable default"). 109 emit(null) 110 } 111 .distinctUntilChanged() 112 113 fun dismissPrimaryBouncer() { 114 startTransitionTo(KeyguardState.GONE) 115 } 116 117 private fun listenForPrimaryBouncerToLockscreenOrOccluded() { 118 scope.launch { 119 keyguardInteractor.primaryBouncerShowing 120 .sample( 121 combine( 122 keyguardInteractor.wakefulnessModel, 123 transitionInteractor.startedKeyguardTransitionStep, 124 keyguardInteractor.isKeyguardOccluded, 125 keyguardInteractor.isActiveDreamLockscreenHosted, 126 ::toQuad 127 ), 128 ::toQuint 129 ) 130 .collect { 131 ( 132 isBouncerShowing, 133 wakefulnessState, 134 lastStartedTransitionStep, 135 occluded, 136 isActiveDreamLockscreenHosted) -> 137 if ( 138 !isBouncerShowing && 139 lastStartedTransitionStep.to == KeyguardState.PRIMARY_BOUNCER && 140 (wakefulnessState.state == WakefulnessState.AWAKE || 141 wakefulnessState.state == WakefulnessState.STARTING_TO_WAKE) && 142 !isActiveDreamLockscreenHosted 143 ) { 144 startTransitionTo( 145 if (occluded) KeyguardState.OCCLUDED else KeyguardState.LOCKSCREEN 146 ) 147 } 148 } 149 } 150 } 151 152 private fun listenForPrimaryBouncerToAodOrDozing() { 153 scope.launch { 154 keyguardInteractor.primaryBouncerShowing 155 .sample( 156 combine( 157 keyguardInteractor.wakefulnessModel, 158 transitionInteractor.startedKeyguardTransitionStep, 159 keyguardInteractor.isAodAvailable, 160 ::Triple 161 ), 162 ::toQuad 163 ) 164 .collect { 165 (isBouncerShowing, wakefulnessState, lastStartedTransitionStep, isAodAvailable) 166 -> 167 if ( 168 !isBouncerShowing && 169 lastStartedTransitionStep.to == KeyguardState.PRIMARY_BOUNCER && 170 (wakefulnessState.state == WakefulnessState.STARTING_TO_SLEEP || 171 wakefulnessState.state == WakefulnessState.ASLEEP) 172 ) { 173 startTransitionTo( 174 if (isAodAvailable) KeyguardState.AOD else KeyguardState.DOZING 175 ) 176 } 177 } 178 } 179 } 180 181 private fun listenForPrimaryBouncerToDreamingLockscreenHosted() { 182 scope.launch { 183 keyguardInteractor.primaryBouncerShowing 184 .sample( 185 combine( 186 keyguardInteractor.isActiveDreamLockscreenHosted, 187 transitionInteractor.startedKeyguardTransitionStep, 188 ::Pair 189 ), 190 ::toTriple 191 ) 192 .collect { (isBouncerShowing, isActiveDreamLockscreenHosted, lastStartedTransitionStep) -> 193 if ( 194 !isBouncerShowing && 195 isActiveDreamLockscreenHosted && 196 lastStartedTransitionStep.to == KeyguardState.PRIMARY_BOUNCER 197 ) { 198 startTransitionTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED) 199 } 200 } 201 } 202 } 203 204 private fun listenForPrimaryBouncerToGone() { 205 if (flags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) { 206 // This is handled in KeyguardSecurityContainerController and 207 // StatusBarKeyguardViewManager, which calls the transition interactor to kick off a 208 // transition vs. listening to legacy state flags. 209 return 210 } 211 212 scope.launch { 213 keyguardInteractor.isKeyguardGoingAway 214 .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair) 215 .collect { (isKeyguardGoingAway, lastStartedTransitionStep) -> 216 if ( 217 isKeyguardGoingAway && 218 lastStartedTransitionStep.to == KeyguardState.PRIMARY_BOUNCER 219 ) { 220 val securityMode = 221 keyguardSecurityModel.getSecurityMode( 222 KeyguardUpdateMonitor.getCurrentUser() 223 ) 224 // IME for password requires a slightly faster animation 225 val duration = 226 if (securityMode == KeyguardSecurityModel.SecurityMode.Password) { 227 TO_GONE_SHORT_DURATION 228 } else { 229 TO_GONE_DURATION 230 } 231 232 startTransitionTo( 233 toState = KeyguardState.GONE, 234 animator = 235 getDefaultAnimatorForTransitionsToState(KeyguardState.GONE).apply { 236 this.duration = duration.inWholeMilliseconds 237 }, 238 resetIfCancelled = true 239 ) 240 } 241 } 242 } 243 } 244 245 override fun getDefaultAnimatorForTransitionsToState(toState: KeyguardState): ValueAnimator { 246 return ValueAnimator().apply { 247 interpolator = Interpolators.LINEAR 248 duration = DEFAULT_DURATION.inWholeMilliseconds 249 } 250 } 251 252 companion object { 253 private val DEFAULT_DURATION = 300.milliseconds 254 val TO_GONE_DURATION = 500.milliseconds 255 val TO_GONE_SHORT_DURATION = 200.milliseconds 256 } 257 } 258