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.keyguard.KeyguardSecurityModel 21 import com.android.systemui.communal.domain.interactor.CommunalInteractor 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.keyguard.KeyguardWmStateRefactor 26 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository 27 import com.android.systemui.keyguard.shared.model.Edge 28 import com.android.systemui.keyguard.shared.model.KeyguardState 29 import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled 30 import com.android.systemui.keyguard.shared.model.TransitionStep 31 import com.android.systemui.power.domain.interactor.PowerInteractor 32 import com.android.systemui.scene.shared.flag.SceneContainerFlag 33 import com.android.systemui.user.domain.interactor.SelectedUserInteractor 34 import com.android.systemui.util.kotlin.Utils.Companion.sample 35 import com.android.systemui.util.kotlin.sample 36 import com.android.wm.shell.animation.Interpolators 37 import javax.inject.Inject 38 import kotlin.time.Duration.Companion.milliseconds 39 import kotlinx.coroutines.CoroutineDispatcher 40 import kotlinx.coroutines.CoroutineScope 41 import kotlinx.coroutines.flow.Flow 42 import kotlinx.coroutines.flow.distinctUntilChanged 43 import kotlinx.coroutines.flow.flowOf 44 import kotlinx.coroutines.flow.map 45 import kotlinx.coroutines.flow.onStart 46 import kotlinx.coroutines.launch 47 48 @SysUISingleton 49 class FromPrimaryBouncerTransitionInteractor 50 @Inject 51 constructor( 52 override val transitionRepository: KeyguardTransitionRepository, 53 transitionInteractor: KeyguardTransitionInteractor, 54 @Background private val scope: CoroutineScope, 55 @Background bgDispatcher: CoroutineDispatcher, 56 @Main mainDispatcher: CoroutineDispatcher, 57 keyguardInteractor: KeyguardInteractor, 58 private val communalInteractor: CommunalInteractor, 59 private val keyguardSecurityModel: KeyguardSecurityModel, 60 private val selectedUserInteractor: SelectedUserInteractor, 61 powerInteractor: PowerInteractor, 62 keyguardOcclusionInteractor: KeyguardOcclusionInteractor, 63 ) : 64 TransitionInteractor( 65 fromState = KeyguardState.PRIMARY_BOUNCER, 66 transitionInteractor = transitionInteractor, 67 mainDispatcher = mainDispatcher, 68 bgDispatcher = bgDispatcher, 69 powerInteractor = powerInteractor, 70 keyguardOcclusionInteractor = keyguardOcclusionInteractor, 71 keyguardInteractor = keyguardInteractor, 72 ) { 73 74 override fun start() { 75 listenForPrimaryBouncerToGone() 76 listenForPrimaryBouncerToAsleep() 77 listenForPrimaryBouncerToLockscreenHubOrOccluded() 78 listenForPrimaryBouncerToDreamingLockscreenHosted() 79 listenForTransitionToCamera(scope, keyguardInteractor) 80 } 81 82 val surfaceBehindVisibility: Flow<Boolean?> = 83 if (SceneContainerFlag.isEnabled) { 84 // The edge Scenes.Bouncer <-> Scenes.Gone is handled by STL 85 flowOf(null) 86 } else { 87 transitionInteractor 88 .transition( 89 edge = Edge.INVALID, 90 edgeWithoutSceneContainer = 91 Edge.create(from = KeyguardState.PRIMARY_BOUNCER, to = KeyguardState.GONE) 92 ) 93 .map<TransitionStep, Boolean?> { 94 it.value > TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD 95 } 96 .onStart { 97 // Default to null ("don't care, use a reasonable default"). 98 emit(null) 99 } 100 .distinctUntilChanged() 101 } 102 103 fun dismissPrimaryBouncer() { 104 scope.launch { startTransitionTo(KeyguardState.GONE) } 105 } 106 107 private fun listenForPrimaryBouncerToLockscreenHubOrOccluded() { 108 // TODO(b/336576536): Check if adaptation for scene framework is needed 109 if (SceneContainerFlag.isEnabled) return 110 if (KeyguardWmStateRefactor.isEnabled) { 111 scope.launch { 112 keyguardInteractor.primaryBouncerShowing 113 .sample( 114 powerInteractor.isAwake, 115 keyguardInteractor.isActiveDreamLockscreenHosted, 116 communalInteractor.isIdleOnCommunal 117 ) 118 .filterRelevantKeyguardState() 119 .collect { 120 (isBouncerShowing, isAwake, isActiveDreamLockscreenHosted, isIdleOnCommunal) 121 -> 122 if ( 123 !maybeStartTransitionToOccludedOrInsecureCamera() && 124 !isBouncerShowing && 125 isAwake && 126 !isActiveDreamLockscreenHosted 127 ) { 128 val toState = 129 if (isIdleOnCommunal) { 130 KeyguardState.GLANCEABLE_HUB 131 } else { 132 KeyguardState.LOCKSCREEN 133 } 134 startTransitionTo(toState) 135 } 136 } 137 } 138 } else { 139 scope.launch { 140 keyguardInteractor.primaryBouncerShowing 141 .sample( 142 powerInteractor.isAwake, 143 keyguardInteractor.isKeyguardOccluded, 144 keyguardInteractor.isDreaming, 145 keyguardInteractor.isActiveDreamLockscreenHosted, 146 communalInteractor.isIdleOnCommunal, 147 ) 148 .filterRelevantKeyguardStateAnd { 149 (isBouncerShowing, isAwake, _, _, isActiveDreamLockscreenHosted, _) -> 150 !isBouncerShowing && isAwake && !isActiveDreamLockscreenHosted 151 } 152 .collect { (_, _, occluded, isDreaming, _, isIdleOnCommunal) -> 153 val toState = 154 if (occluded && !isDreaming) { 155 KeyguardState.OCCLUDED 156 } else if (isIdleOnCommunal) { 157 KeyguardState.GLANCEABLE_HUB 158 } else if (isDreaming) { 159 KeyguardState.DREAMING 160 } else { 161 KeyguardState.LOCKSCREEN 162 } 163 startTransitionTo(toState) 164 } 165 } 166 } 167 } 168 169 private fun listenForPrimaryBouncerToAsleep() { 170 // TODO(b/336576536): Check if adaptation for scene framework is needed 171 if (SceneContainerFlag.isEnabled) return 172 scope.launch { listenForSleepTransition() } 173 } 174 175 private fun listenForPrimaryBouncerToDreamingLockscreenHosted() { 176 // TODO(b/336576536): Check if adaptation for scene framework is needed 177 if (SceneContainerFlag.isEnabled) return 178 scope.launch { 179 keyguardInteractor.primaryBouncerShowing 180 .sample(keyguardInteractor.isActiveDreamLockscreenHosted, ::Pair) 181 .filterRelevantKeyguardStateAnd { (isBouncerShowing, isActiveDreamLockscreenHosted) 182 -> 183 !isBouncerShowing && isActiveDreamLockscreenHosted 184 } 185 .collect { startTransitionTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED) } 186 } 187 } 188 189 private fun listenForPrimaryBouncerToGone() { 190 // TODO(b/336576536): Check if adaptation for scene framework is needed 191 if (SceneContainerFlag.isEnabled) return 192 if (KeyguardWmStateRefactor.isEnabled) { 193 // This is handled in KeyguardSecurityContainerController and 194 // StatusBarKeyguardViewManager, which calls the transition interactor to kick off a 195 // transition vs. listening to legacy state flags. 196 return 197 } 198 199 scope.launch { 200 keyguardInteractor.isKeyguardGoingAway 201 .filterRelevantKeyguardStateAnd { isKeyguardGoingAway -> isKeyguardGoingAway } 202 .collect { 203 val securityMode = 204 keyguardSecurityModel.getSecurityMode( 205 selectedUserInteractor.getSelectedUserId() 206 ) 207 // IME for password requires a slightly faster animation 208 val duration = 209 if (securityMode == KeyguardSecurityModel.SecurityMode.Password) { 210 TO_GONE_SHORT_DURATION 211 } else { 212 TO_GONE_DURATION 213 } 214 215 startTransitionTo( 216 toState = KeyguardState.GONE, 217 animator = 218 getDefaultAnimatorForTransitionsToState(KeyguardState.GONE).apply { 219 this.duration = duration.inWholeMilliseconds 220 }, 221 modeOnCanceled = TransitionModeOnCanceled.RESET, 222 ) 223 } 224 } 225 } 226 227 override fun getDefaultAnimatorForTransitionsToState(toState: KeyguardState): ValueAnimator { 228 return ValueAnimator().apply { 229 interpolator = Interpolators.LINEAR 230 duration = 231 when (toState) { 232 KeyguardState.AOD -> TO_AOD_DURATION 233 KeyguardState.DOZING -> TO_DOZING_DURATION 234 KeyguardState.GONE -> TO_GONE_DURATION 235 KeyguardState.LOCKSCREEN -> TO_LOCKSCREEN_DURATION 236 else -> DEFAULT_DURATION 237 }.inWholeMilliseconds 238 } 239 } 240 241 companion object { 242 private val DEFAULT_DURATION = 300.milliseconds 243 val TO_AOD_DURATION = DEFAULT_DURATION 244 val TO_DOZING_DURATION = DEFAULT_DURATION 245 val TO_GONE_DURATION = 500.milliseconds 246 val TO_GONE_SHORT_DURATION = 200.milliseconds 247 val TO_LOCKSCREEN_DURATION = 450.milliseconds 248 val TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD = 0.5f 249 } 250 } 251