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 android.util.Log 21 import com.android.app.tracing.coroutines.launchTraced as launch 22 import com.android.keyguard.KeyguardSecurityModel 23 import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor 24 import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor 25 import com.android.systemui.communal.shared.model.CommunalScenes 26 import com.android.systemui.dagger.SysUISingleton 27 import com.android.systemui.dagger.qualifiers.Background 28 import com.android.systemui.dagger.qualifiers.Main 29 import com.android.systemui.keyguard.KeyguardWmStateRefactor 30 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository 31 import com.android.systemui.keyguard.shared.model.Edge 32 import com.android.systemui.keyguard.shared.model.KeyguardState 33 import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled 34 import com.android.systemui.keyguard.shared.model.TransitionStep 35 import com.android.systemui.power.domain.interactor.PowerInteractor 36 import com.android.systemui.scene.shared.flag.SceneContainerFlag 37 import com.android.systemui.user.domain.interactor.SelectedUserInteractor 38 import com.android.systemui.util.kotlin.Utils.Companion.sample 39 import com.android.systemui.util.kotlin.sample 40 import com.android.wm.shell.shared.animation.Interpolators 41 import javax.inject.Inject 42 import kotlin.time.Duration.Companion.milliseconds 43 import kotlinx.coroutines.CoroutineDispatcher 44 import kotlinx.coroutines.CoroutineScope 45 import kotlinx.coroutines.flow.Flow 46 import kotlinx.coroutines.flow.distinctUntilChanged 47 import kotlinx.coroutines.flow.filter 48 import kotlinx.coroutines.flow.map 49 import kotlinx.coroutines.flow.onStart 50 51 @SysUISingleton 52 class FromPrimaryBouncerTransitionInteractor 53 @Inject 54 constructor( 55 override val transitionRepository: KeyguardTransitionRepository, 56 override val internalTransitionInteractor: InternalKeyguardTransitionInteractor, 57 transitionInteractor: KeyguardTransitionInteractor, 58 @Background private val scope: CoroutineScope, 59 @Background bgDispatcher: CoroutineDispatcher, 60 @Main mainDispatcher: CoroutineDispatcher, 61 keyguardInteractor: KeyguardInteractor, 62 private val communalSceneInteractor: CommunalSceneInteractor, 63 private val communalSettingsInteractor: CommunalSettingsInteractor, 64 private val keyguardSecurityModel: KeyguardSecurityModel, 65 private val selectedUserInteractor: SelectedUserInteractor, 66 powerInteractor: PowerInteractor, 67 keyguardOcclusionInteractor: KeyguardOcclusionInteractor, 68 ) : 69 TransitionInteractor( 70 fromState = KeyguardState.PRIMARY_BOUNCER, 71 transitionInteractor = transitionInteractor, 72 mainDispatcher = mainDispatcher, 73 bgDispatcher = bgDispatcher, 74 powerInteractor = powerInteractor, 75 keyguardOcclusionInteractor = keyguardOcclusionInteractor, 76 keyguardInteractor = keyguardInteractor, 77 ) { 78 79 override fun start() { 80 listenForPrimaryBouncerToGone() 81 listenForPrimaryBouncerToAsleep() 82 listenForPrimaryBouncerNotShowing() 83 listenForTransitionToCamera(scope, keyguardInteractor) 84 } 85 86 val surfaceBehindVisibility: Flow<Boolean?> = 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?> { it.value > TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD } 94 .onStart { 95 // Default to null ("don't care, use a reasonable default"). 96 emit(null) 97 } 98 .distinctUntilChanged() 99 100 fun dismissPrimaryBouncer() { 101 scope.launch { 102 startTransitionTo(KeyguardState.GONE) 103 closeHubImmediatelyIfNeeded() 104 } 105 } 106 107 private fun listenForPrimaryBouncerNotShowing() { 108 if (SceneContainerFlag.isEnabled) return 109 if (KeyguardWmStateRefactor.isEnabled) { 110 scope.launch { 111 keyguardInteractor.primaryBouncerShowing 112 .sample(powerInteractor.isAwake, communalSceneInteractor.isIdleOnCommunal) 113 .filterRelevantKeyguardStateAnd { (isBouncerShowing, _, _) -> 114 // TODO(b/307976454) - See if we need to listen for SHOW_WHEN_LOCKED 115 // activities showing up over the bouncer. Camera launch can't show up over 116 // bouncer since the first power press hides bouncer. Do occluding 117 // activities auto hide bouncer? Not sure. 118 !isBouncerShowing 119 } 120 .collect { (_, isAwake, isIdleOnCommunal) -> 121 if ( 122 !maybeStartTransitionToOccludedOrInsecureCamera { state, reason -> 123 startTransitionTo(state, ownerReason = reason) 124 } && isAwake 125 ) { 126 val toState = 127 if (isIdleOnCommunal) { 128 KeyguardState.GLANCEABLE_HUB 129 } else { 130 KeyguardState.LOCKSCREEN 131 } 132 startTransitionTo(toState) 133 } 134 } 135 } 136 } else { 137 scope.launch { 138 keyguardInteractor.primaryBouncerShowing 139 .filterRelevantKeyguardStateAnd { isBouncerShowing -> !isBouncerShowing } 140 .sample( 141 powerInteractor.isAwake, 142 keyguardInteractor.isDreaming, 143 communalSceneInteractor.isIdleOnCommunal, 144 ) 145 .collect { (_, isAwake, isDreaming, isIdleOnCommunal) -> 146 val isOccluded = keyguardInteractor.isKeyguardOccluded.value 147 val hubV2 = communalSettingsInteractor.isV2FlagEnabled() 148 val toState = 149 if (isAwake) { 150 if (isOccluded && !isDreaming) { 151 KeyguardState.OCCLUDED 152 } else if (!hubV2 && isIdleOnCommunal) { 153 KeyguardState.GLANCEABLE_HUB 154 } else if (isDreaming) { 155 KeyguardState.DREAMING 156 } else if (hubV2 && isIdleOnCommunal) { 157 KeyguardState.GLANCEABLE_HUB 158 } else { 159 KeyguardState.LOCKSCREEN 160 } 161 } else { 162 // This shouldn't necessarily happen, but there's a bug in the 163 // bouncer logic which is incorrectly showing/hiding rapidly 164 Log.i( 165 TAG, 166 "Going back to sleeping state to correct an attempt to " + 167 "show bouncer", 168 ) 169 keyguardInteractor.asleepKeyguardState.value 170 } 171 if (hubV2 && toState != KeyguardState.GLANCEABLE_HUB && isIdleOnCommunal) { 172 // If bouncer is showing over the hub, we need to make sure we 173 // properly dismiss the hub when transitioning away. 174 communalSceneInteractor.changeScene( 175 newScene = CommunalScenes.Blank, 176 loggingReason = "bouncer no longer showing over GH", 177 keyguardState = toState, 178 ) 179 } else { 180 startTransitionTo(toState) 181 } 182 } 183 } 184 } 185 } 186 187 private fun closeHubImmediatelyIfNeeded() { 188 // If the hub is showing, and we are not animating a widget launch nor transitioning to 189 // edit mode, then close the hub immediately. 190 if ( 191 communalSceneInteractor.isIdleOnCommunal.value && 192 !communalSceneInteractor.isLaunchingWidget.value && 193 communalSceneInteractor.editModeState.value == null 194 ) { 195 communalSceneInteractor.snapToScene( 196 newScene = CommunalScenes.Blank, 197 loggingReason = "FromPrimaryBouncerTransitionInteractor", 198 ) 199 } 200 } 201 202 private fun listenForPrimaryBouncerToAsleep() { 203 if (SceneContainerFlag.isEnabled) return 204 scope.launch { 205 if (communalSettingsInteractor.isV2FlagEnabled()) { 206 powerInteractor.isAsleep 207 .filter { isAsleep -> isAsleep } 208 .filterRelevantKeyguardState() 209 .sample(communalSceneInteractor.isIdleOnCommunal) 210 .collect { isIdleOnCommunal -> 211 if (isIdleOnCommunal) { 212 // If the bouncer is showing on top of the hub, then ensure we also 213 // hide the hub. 214 communalSceneInteractor.changeScene( 215 newScene = CommunalScenes.Blank, 216 loggingReason = "Sleep while primary bouncer showing over hub", 217 keyguardState = keyguardInteractor.asleepKeyguardState.value, 218 ) 219 } else { 220 startTransitionTo( 221 toState = keyguardInteractor.asleepKeyguardState.value, 222 ownerReason = "Sleep transition triggered", 223 ) 224 } 225 } 226 } else { 227 listenForSleepTransition() 228 } 229 } 230 } 231 232 private fun listenForPrimaryBouncerToGone() { 233 if (SceneContainerFlag.isEnabled) return 234 if (KeyguardWmStateRefactor.isEnabled) { 235 // This is handled in KeyguardSecurityContainerController and 236 // StatusBarKeyguardViewManager, which calls the transition interactor to kick off a 237 // transition vs. listening to legacy state flags. 238 return 239 } 240 241 scope.launch { 242 keyguardInteractor.isKeyguardGoingAway 243 .filterRelevantKeyguardStateAnd { isKeyguardGoingAway -> isKeyguardGoingAway } 244 .collect { 245 val securityMode = 246 keyguardSecurityModel.getSecurityMode( 247 selectedUserInteractor.getSelectedUserId() 248 ) 249 // IME for password requires a slightly faster animation 250 val duration = 251 if (securityMode == KeyguardSecurityModel.SecurityMode.Password) { 252 TO_GONE_SHORT_DURATION 253 } else { 254 TO_GONE_DURATION 255 } 256 257 startTransitionTo( 258 toState = KeyguardState.GONE, 259 animator = 260 getDefaultAnimatorForTransitionsToState(KeyguardState.GONE).apply { 261 this.duration = duration.inWholeMilliseconds 262 }, 263 modeOnCanceled = TransitionModeOnCanceled.RESET, 264 ) 265 closeHubImmediatelyIfNeeded() 266 } 267 } 268 } 269 270 override fun getDefaultAnimatorForTransitionsToState(toState: KeyguardState): ValueAnimator { 271 return ValueAnimator().apply { 272 interpolator = Interpolators.LINEAR 273 duration = 274 when (toState) { 275 KeyguardState.AOD -> TO_AOD_DURATION 276 KeyguardState.DOZING -> TO_DOZING_DURATION 277 KeyguardState.GONE -> TO_GONE_DURATION 278 KeyguardState.LOCKSCREEN -> TO_LOCKSCREEN_DURATION 279 KeyguardState.GLANCEABLE_HUB -> TO_GLANCEABLE_HUB_DURATION 280 KeyguardState.OCCLUDED -> TO_OCCLUDED_DURATION 281 else -> DEFAULT_DURATION 282 }.inWholeMilliseconds 283 } 284 } 285 286 companion object { 287 private const val TAG = "FromPrimaryBouncerTransitionInteractor" 288 private val DEFAULT_DURATION = 300.milliseconds 289 val TO_AOD_DURATION = DEFAULT_DURATION 290 val TO_DOZING_DURATION = DEFAULT_DURATION 291 val TO_GONE_DURATION = 500.milliseconds 292 val TO_GONE_SHORT_DURATION = 200.milliseconds 293 val TO_LOCKSCREEN_DURATION = 450.milliseconds 294 val TO_OCCLUDED_DURATION = 550.milliseconds 295 val TO_GLANCEABLE_HUB_DURATION = DEFAULT_DURATION 296 val TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD = 0.1f 297 val TO_DREAMING_DURATION = DEFAULT_DURATION 298 } 299 } 300