1 /* <lambda>null2 * Copyright (C) 2021 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 18 19 import android.animation.Animator 20 import android.animation.AnimatorListenerAdapter 21 import android.animation.ValueAnimator 22 import android.content.Context 23 import android.graphics.Matrix 24 import android.graphics.Rect 25 import android.os.Handler 26 import android.os.PowerManager 27 import android.os.RemoteException 28 import android.util.Log 29 import android.view.RemoteAnimationTarget 30 import android.view.SurfaceControl 31 import android.view.SyncRtSurfaceTransactionApplier 32 import android.view.View 33 import androidx.annotation.VisibleForTesting 34 import androidx.core.math.MathUtils 35 import com.android.internal.R 36 import com.android.keyguard.KeyguardClockSwitchController 37 import com.android.keyguard.KeyguardViewController 38 import com.android.systemui.animation.Interpolators 39 import com.android.systemui.dagger.SysUISingleton 40 import com.android.systemui.flags.FeatureFlags 41 import com.android.systemui.flags.Flags 42 import com.android.systemui.plugins.BcSmartspaceDataPlugin 43 import com.android.systemui.shared.recents.utilities.Utilities 44 import com.android.systemui.shared.system.ActivityManagerWrapper 45 import com.android.systemui.shared.system.QuickStepContract 46 import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController 47 import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController 48 import com.android.systemui.shared.system.smartspace.SmartspaceState 49 import com.android.systemui.statusbar.NotificationShadeWindowController 50 import com.android.systemui.statusbar.SysuiStatusBarStateController 51 import com.android.systemui.statusbar.phone.BiometricUnlockController 52 import com.android.systemui.statusbar.policy.KeyguardStateController 53 import dagger.Lazy 54 import javax.inject.Inject 55 56 const val TAG = "KeyguardUnlock" 57 58 /** 59 * Starting scale factor for the app/launcher surface behind the keyguard, when it's animating 60 * in during keyguard exit. 61 */ 62 const val SURFACE_BEHIND_START_SCALE_FACTOR = 0.95f 63 64 /** 65 * How much to translate the surface behind the keyguard at the beginning of the exit animation, 66 * in terms of percentage of the surface's height. 67 */ 68 const val SURFACE_BEHIND_START_TRANSLATION_Y = 0.05f 69 70 /** 71 * Y coordinate of the pivot point for the scale effect on the surface behind the keyguard. This 72 * is expressed as percentage of the surface's height, so 0.66f means the surface will scale up 73 * from the point at (width / 2, height * 0.66). 74 */ 75 const val SURFACE_BEHIND_SCALE_PIVOT_Y = 0.66f 76 77 /** 78 * Dismiss amount at which to fade in the surface behind the keyguard. The surface will then animate 79 * along with the dismiss amount until [DISMISS_AMOUNT_EXIT_KEYGUARD_THRESHOLD] is reached. 80 * 81 * The dismiss amount is the inverse of the notification panel expansion, which decreases as the 82 * lock screen is swiped away. 83 */ 84 const val DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD = 0.15f 85 86 /** 87 * Dismiss amount at which to complete the keyguard exit animation and hide the keyguard. 88 * 89 * The dismiss amount is the inverse of the notification panel expansion, which decreases as the 90 * lock screen is swiped away. 91 */ 92 const val DISMISS_AMOUNT_EXIT_KEYGUARD_THRESHOLD = 0.3f 93 94 /** 95 * How long the canned unlock animation takes. This is used if we are unlocking from biometric auth, 96 * from a tap on the unlock icon, or from the bouncer. This is not relevant if the lockscreen is 97 * swiped away via a touch gesture, or when it's flinging expanded/collapsed after a swipe. 98 */ 99 const val UNLOCK_ANIMATION_DURATION_MS = 200L 100 101 /** 102 * How long the in-window launcher icon animation takes. This is used if the launcher is underneath 103 * the lock screen and supports in-window animations. 104 * 105 * This animation will take place entirely within the Launcher window. We can safely unlock the 106 * device, end remote animations, etc. even if this is still running. 107 */ 108 const val LAUNCHER_ICONS_ANIMATION_DURATION_MS = 633L 109 110 /** 111 * How long to wait for the shade to get out of the way before starting the canned unlock animation. 112 */ 113 const val CANNED_UNLOCK_START_DELAY = 100L 114 115 /** 116 * Duration for the alpha animation on the surface behind. This plays to fade in the surface during 117 * a swipe to unlock (and to fade it back out if the swipe is cancelled). 118 */ 119 const val SURFACE_BEHIND_SWIPE_FADE_DURATION_MS = 175L 120 121 /** 122 * Start delay for the surface behind animation, used so that the lockscreen can get out of the way 123 * before the surface begins appearing. 124 */ 125 const val UNLOCK_ANIMATION_SURFACE_BEHIND_START_DELAY_MS = 75L 126 127 /** 128 * Initiates, controls, and ends the keyguard unlock animation. 129 * 130 * The unlock animation transitions between the keyguard (lock screen) and the app/launcher surface 131 * behind the keyguard. If the user is swiping away the keyguard, this controller will decide when 132 * to animate in the surface, and synchronize its appearance with the swipe gesture. If the keyguard 133 * is animating away via a canned animation (due to biometric unlock, tapping a notification, etc.) 134 * this controller will play a canned animation on the surface as well. 135 * 136 * The surface behind the keyguard is manipulated via a RemoteAnimation passed to 137 * [notifyStartSurfaceBehindRemoteAnimation] by [KeyguardViewMediator]. 138 */ 139 @SysUISingleton 140 class KeyguardUnlockAnimationController @Inject constructor( 141 private val context: Context, 142 private val keyguardStateController: KeyguardStateController, 143 private val 144 keyguardViewMediator: Lazy<KeyguardViewMediator>, 145 private val keyguardViewController: KeyguardViewController, 146 private val featureFlags: FeatureFlags, 147 private val biometricUnlockControllerLazy: Lazy<BiometricUnlockController>, 148 private val statusBarStateController: SysuiStatusBarStateController, 149 private val notificationShadeWindowController: NotificationShadeWindowController, 150 private val powerManager: PowerManager 151 ) : KeyguardStateController.Callback, ISysuiUnlockAnimationController.Stub() { 152 153 interface KeyguardUnlockAnimationListener { 154 /** 155 * Called when the remote unlock animation, controlled by 156 * [KeyguardUnlockAnimationController], first starts. 157 * 158 * [playingCannedAnimation] indicates whether we are playing a canned animation to show the 159 * app/launcher behind the keyguard, vs. this being a swipe to unlock where the dismiss 160 * amount drives the animation. 161 * 162 * [fromWakeAndUnlock] tells us whether we are unlocking directly from AOD - in this case, 163 * the lockscreen is dismissed instantly, so we shouldn't run any animations that rely on it 164 * being visible. 165 * 166 * [unlockAnimationStartDelay] and [unlockAnimationDuration] provide the timing parameters 167 * for the canned animation (if applicable) so interested parties can sync with it. If no 168 * canned animation is playing, these are both 0. 169 */ 170 @JvmDefault 171 fun onUnlockAnimationStarted( 172 playingCannedAnimation: Boolean, 173 fromWakeAndUnlock: Boolean, 174 unlockAnimationStartDelay: Long, 175 unlockAnimationDuration: Long 176 ) {} 177 178 /** 179 * Called when the remote unlock animation ends, in all cases, canned or swipe-to-unlock. 180 * The keyguard is no longer visible in this state and the app/launcher behind the keyguard 181 * is now completely visible. 182 */ 183 @JvmDefault 184 fun onUnlockAnimationFinished() {} 185 } 186 187 /** The SmartSpace view on the lockscreen, provided by [KeyguardClockSwitchController]. */ 188 var lockscreenSmartspace: View? = null 189 190 /** 191 * The state of the Launcher's smartspace, delivered via [onLauncherSmartspaceStateUpdated]. 192 * This is pushed to us from Launcher whenever their smartspace moves or its visibility changes. 193 * We'll animate the lockscreen smartspace to this location during an unlock. 194 */ 195 var launcherSmartspaceState: SmartspaceState? = null 196 197 /** 198 * Whether a canned unlock animation is playing, vs. currently unlocking in response to a swipe 199 * gesture or panel fling. If we're swiping/flinging, the unlock animation is driven by the 200 * dismiss amount, via [onKeyguardDismissAmountChanged]. If we're using a canned animation, it's 201 * being driven by ValueAnimators started in [playCannedUnlockAnimation]. 202 */ 203 var playingCannedUnlockAnimation = false 204 205 /** 206 * Remote callback provided by Launcher that allows us to control the Launcher's unlock 207 * animation and smartspace. 208 * 209 * If this is null, we will not be animating any Launchers today and should fall back to window 210 * animations. 211 */ 212 private var launcherUnlockController: ILauncherUnlockAnimationController? = null 213 214 private val listeners = ArrayList<KeyguardUnlockAnimationListener>() 215 216 /** 217 * Called from SystemUiProxy to pass us the launcher's unlock animation controller. If this 218 * doesn't happen, we won't use in-window animations or the smartspace shared element 219 * transition, but that's okay! 220 */ 221 override fun setLauncherUnlockController(callback: ILauncherUnlockAnimationController?) { 222 launcherUnlockController = callback 223 } 224 225 /** 226 * Called from SystemUiProxy to pass us the latest state of the Launcher's smartspace. This is 227 * only done when the state has changed in some way. 228 */ 229 override fun onLauncherSmartspaceStateUpdated(state: SmartspaceState?) { 230 launcherSmartspaceState = state 231 } 232 233 /** 234 * Information used to start, run, and finish a RemoteAnimation on the app or launcher surface 235 * behind the keyguard. 236 * 237 * If we're swiping to unlock, the "animation" is controlled via the gesture, tied to the 238 * dismiss amounts received in [onKeyguardDismissAmountChanged]. It does not have a fixed 239 * duration, and it ends when the gesture reaches a certain threshold or is cancell 240 * 241 * If we're unlocking via biometrics, PIN entry, or from clicking a notification, a canned 242 * animation is started in [playCannedUnlockAnimation]. 243 */ 244 @VisibleForTesting 245 var surfaceTransactionApplier: SyncRtSurfaceTransactionApplier? = null 246 private var surfaceBehindRemoteAnimationTargets: Array<RemoteAnimationTarget>? = null 247 private var surfaceBehindRemoteAnimationStartTime: Long = 0 248 249 /** 250 * Alpha value applied to [surfaceBehindRemoteAnimationTarget], which is the surface of the 251 * app/launcher behind the keyguard. 252 * 253 * If we're doing a swipe gesture, we fade in the surface when the swipe passes a certain 254 * threshold. If we're doing a canned animation, it'll be faded in while a translate/scale 255 * animation plays. 256 */ 257 private var surfaceBehindAlpha = 1f 258 259 @VisibleForTesting 260 var surfaceBehindAlphaAnimator = ValueAnimator.ofFloat(0f, 1f) 261 262 /** 263 * Matrix applied to [surfaceBehindRemoteAnimationTarget], which is the surface of the 264 * app/launcher behind the keyguard. 265 * 266 * This is used during the unlock animation/swipe gesture to scale and translate the surface. 267 */ 268 private val surfaceBehindMatrix = Matrix() 269 270 /** 271 * Animator that animates in the surface behind the keyguard. This is used to play a canned 272 * animation on the surface, if we're not doing a swipe gesture. 273 */ 274 @VisibleForTesting 275 val surfaceBehindEntryAnimator = ValueAnimator.ofFloat(0f, 1f) 276 277 /** Rounded corner radius to apply to the surface behind the keyguard. */ 278 private var roundedCornerRadius = 0f 279 280 /** 281 * Whether we decided in [prepareForInWindowLauncherAnimations] that we are able to and want to 282 * play the in-window launcher unlock animations rather than simply animating the Launcher 283 * window like any other app. This can be true while [willUnlockWithSmartspaceTransition] is 284 * false, if the smartspace is not available or was not ready in time. 285 */ 286 @VisibleForTesting 287 var willUnlockWithInWindowLauncherAnimations: Boolean = false 288 289 /** 290 * Whether we called [ILauncherUnlockAnimationController.prepareForUnlock], but have not yet 291 * called [ILauncherUnlockAnimationController.playUnlockAnimation]. This is used exclusively for 292 * logging purposes to help track down bugs where the Launcher surface is prepared for unlock 293 * but then never animated. 294 */ 295 private var launcherPreparedForUnlock = false 296 297 /** 298 * Whether we decided in [prepareForInWindowLauncherAnimations] that we are able to and want to 299 * play the smartspace shared element animation. If true, 300 * [willUnlockWithInWindowLauncherAnimations] will also always be true since in-window 301 * animations are a prerequisite for the smartspace transition. 302 */ 303 private var willUnlockWithSmartspaceTransition: Boolean = false 304 305 private val handler = Handler() 306 307 private val tmpFloat = FloatArray(9) 308 309 init { 310 with(surfaceBehindAlphaAnimator) { 311 duration = SURFACE_BEHIND_SWIPE_FADE_DURATION_MS 312 interpolator = Interpolators.LINEAR 313 addUpdateListener { valueAnimator: ValueAnimator -> 314 surfaceBehindAlpha = valueAnimator.animatedValue as Float 315 updateSurfaceBehindAppearAmount() 316 } 317 addListener(object : AnimatorListenerAdapter() { 318 override fun onAnimationEnd(animation: Animator) { 319 // If we animated the surface alpha to 0f, it means we cancelled a swipe to 320 // dismiss. In this case, we should ask the KeyguardViewMediator to end the 321 // remote animation to hide the surface behind the keyguard, but should *not* 322 // call onKeyguardExitRemoteAnimationFinished since that will hide the keyguard 323 // and unlock the device as well as hiding the surface. 324 if (surfaceBehindAlpha == 0f) { 325 Log.d(TAG, "surfaceBehindAlphaAnimator#onAnimationEnd") 326 surfaceBehindRemoteAnimationTargets = null 327 keyguardViewMediator.get().finishSurfaceBehindRemoteAnimation( 328 false /* cancelled */) 329 } else { 330 Log.d(TAG, "skip finishSurfaceBehindRemoteAnimation" + 331 " surfaceBehindAlpha=$surfaceBehindAlpha") 332 } 333 } 334 }) 335 } 336 337 with(surfaceBehindEntryAnimator) { 338 duration = UNLOCK_ANIMATION_DURATION_MS 339 startDelay = UNLOCK_ANIMATION_SURFACE_BEHIND_START_DELAY_MS 340 interpolator = Interpolators.TOUCH_RESPONSE 341 addUpdateListener { valueAnimator: ValueAnimator -> 342 surfaceBehindAlpha = valueAnimator.animatedValue as Float 343 setSurfaceBehindAppearAmount(valueAnimator.animatedValue as Float) 344 } 345 addListener(object : AnimatorListenerAdapter() { 346 override fun onAnimationEnd(animation: Animator) { 347 Log.d(TAG, "surfaceBehindEntryAnimator#onAnimationEnd") 348 playingCannedUnlockAnimation = false 349 keyguardViewMediator.get().exitKeyguardAndFinishSurfaceBehindRemoteAnimation( 350 false /* cancelled */ 351 ) 352 } 353 }) 354 } 355 356 // Listen for changes in the dismiss amount. 357 keyguardStateController.addCallback(this) 358 359 roundedCornerRadius = 360 context.resources.getDimensionPixelSize(R.dimen.rounded_corner_radius).toFloat() 361 } 362 363 /** 364 * Add a listener to be notified of various stages of the unlock animation. 365 */ 366 fun addKeyguardUnlockAnimationListener(listener: KeyguardUnlockAnimationListener) { 367 listeners.add(listener) 368 } 369 370 fun removeKeyguardUnlockAnimationListener(listener: KeyguardUnlockAnimationListener) { 371 listeners.remove(listener) 372 } 373 374 /** 375 * Whether we should be able to do the in-window launcher animations given the current state of 376 * the device. 377 */ 378 fun canPerformInWindowLauncherAnimations(): Boolean { 379 return isNexusLauncherUnderneath() && 380 // If the launcher is underneath, but we're about to launch an activity, don't do 381 // the animations since they won't be visible. 382 !notificationShadeWindowController.isLaunchingActivity && 383 launcherUnlockController != null 384 } 385 386 /** 387 * Logging helper to log the conditions under which we decide to perform the in-window 388 * animations. This is used if we prepare to unlock but then somehow decide later to not play 389 * the animation, which would leave Launcher in a bad state. 390 */ 391 private fun logInWindowAnimationConditions() { 392 Log.wtf(TAG, "canPerformInWindowLauncherAnimations expected all of these to be true: ") 393 Log.wtf(TAG, " isNexusLauncherUnderneath: ${isNexusLauncherUnderneath()}") 394 Log.wtf(TAG, " !notificationShadeWindowController.isLaunchingActivity: " + 395 "${!notificationShadeWindowController.isLaunchingActivity}") 396 Log.wtf(TAG, " launcherUnlockController != null: ${launcherUnlockController != null}") 397 Log.wtf(TAG, " !isFoldable(context): ${!isFoldable(context)}") 398 } 399 400 /** 401 * Called from [KeyguardStateController] to let us know that the keyguard going away state has 402 * changed. 403 */ 404 override fun onKeyguardGoingAwayChanged() { 405 if (keyguardStateController.isKeyguardGoingAway && 406 !statusBarStateController.leaveOpenOnKeyguardHide()) { 407 prepareForInWindowLauncherAnimations() 408 } 409 410 // If the keyguard is no longer going away and we were unlocking with in-window animations, 411 // make sure that we've left the launcher at 100% unlocked. This is a fail-safe to prevent 412 // against "tiny launcher" and similar states where the launcher is left in the prepared to 413 // animate state. 414 if (!keyguardStateController.isKeyguardGoingAway && 415 willUnlockWithInWindowLauncherAnimations) { 416 launcherUnlockController?.setUnlockAmount(1f, true /* forceIfAnimating */) 417 } 418 } 419 420 /** 421 * Prepare for in-window Launcher unlock animations, if we're able to do so. 422 * 423 * The in-window animations consist of the staggered ring icon unlock animation, and optionally 424 * the shared element smartspace transition. 425 */ 426 fun prepareForInWindowLauncherAnimations() { 427 willUnlockWithInWindowLauncherAnimations = canPerformInWindowLauncherAnimations() 428 429 if (!willUnlockWithInWindowLauncherAnimations) { 430 return 431 } 432 433 // There are additional conditions under which we should not perform the smartspace 434 // transition specifically, so check those. 435 willUnlockWithSmartspaceTransition = shouldPerformSmartspaceTransition() 436 437 var lockscreenSmartspaceBounds = Rect() 438 439 // Grab the bounds of our lockscreen smartspace and send them to launcher so they can 440 // position their smartspace there initially, then animate it to its resting position. 441 if (willUnlockWithSmartspaceTransition) { 442 lockscreenSmartspaceBounds = Rect().apply { 443 lockscreenSmartspace!!.getBoundsOnScreen(this) 444 445 // The smartspace container on the lockscreen has left and top padding to align it 446 // with other lockscreen content. This padding is inside the bounds on screen, so 447 // add it to those bounds so that the padding-less launcher smartspace is properly 448 // aligned. 449 offset(lockscreenSmartspace!!.paddingLeft, lockscreenSmartspace!!.paddingTop) 450 451 // Also offset by the current card's top padding, if it has any. This allows us to 452 // align the tops of the lockscreen/launcher smartspace cards. Some cards, such as 453 // the three-line date/weather/alarm card, only have three lines on lockscreen but 454 // two on launcher. 455 offset(0, (lockscreenSmartspace 456 as? BcSmartspaceDataPlugin.SmartspaceView)?.currentCardTopPadding ?: 0) 457 } 458 } 459 460 // Currently selected lockscreen smartspace page, or -1 if it's not available. 461 val selectedPage = 462 (lockscreenSmartspace as BcSmartspaceDataPlugin.SmartspaceView?)?.selectedPage ?: -1 463 464 try { 465 // Let the launcher know to prepare for this animation. 466 launcherUnlockController?.prepareForUnlock( 467 willUnlockWithSmartspaceTransition, /* willAnimateSmartspace */ 468 lockscreenSmartspaceBounds, /* lockscreenSmartspaceBounds */ 469 selectedPage /* selectedPage */ 470 ) 471 472 launcherPreparedForUnlock = true 473 } catch (e: RemoteException) { 474 Log.e(TAG, "Remote exception in prepareForInWindowUnlockAnimations.", e) 475 } 476 } 477 478 /** 479 * Called from [KeyguardViewMediator] to tell us that the RemoteAnimation on the surface behind 480 * the keyguard has started successfully. We can use these parameters to directly manipulate the 481 * surface for the unlock gesture/animation. 482 * 483 * When we're done with it, we'll call [KeyguardViewMediator.finishSurfaceBehindRemoteAnimation] 484 * to end the RemoteAnimation. The KeyguardViewMediator will then end the animation and let us 485 * know that it's over by calling [notifyFinishedKeyguardExitAnimation]. 486 * 487 * [requestedShowSurfaceBehindKeyguard] indicates whether the animation started because of a 488 * call to [KeyguardViewMediator.showSurfaceBehindKeyguard], as happens during a swipe gesture, 489 * as opposed to being called because the device was unlocked instantly by some other means 490 * (fingerprint, tap, etc.) and the keyguard is going away. 491 */ 492 fun notifyStartSurfaceBehindRemoteAnimation( 493 targets: Array<RemoteAnimationTarget>, 494 startTime: Long, 495 requestedShowSurfaceBehindKeyguard: Boolean 496 ) { 497 if (surfaceTransactionApplier == null) { 498 surfaceTransactionApplier = SyncRtSurfaceTransactionApplier( 499 keyguardViewController.viewRootImpl.view) 500 } 501 502 surfaceBehindRemoteAnimationTargets = targets 503 surfaceBehindRemoteAnimationStartTime = startTime 504 505 // If we specifically requested that the surface behind be made visible (vs. it being made 506 // visible because we're unlocking), then we're in the middle of a swipe-to-unlock touch 507 // gesture and the surface behind the keyguard should be made visible so that we can animate 508 // it in. 509 if (requestedShowSurfaceBehindKeyguard) { 510 511 // If we're flinging to dismiss here, it means the touch gesture ended in a fling during 512 // the time it takes the keyguard exit animation to start. This is an edge case race 513 // condition, which we handle by just playing a canned animation on the now-visible 514 // surface behind the keyguard to finish unlocking. 515 if (keyguardStateController.isFlingingToDismissKeyguard) { 516 playCannedUnlockAnimation() 517 } else if (keyguardStateController.isDismissingFromSwipe && 518 willUnlockWithInWindowLauncherAnimations) { 519 // If we're swiping to unlock to the Launcher, and can play in-window animations, 520 // make the launcher surface fully visible and play the in-window unlock animation 521 // on the launcher icons. System UI will remain locked, using the swipe-to-unlock 522 // translation logic on the launcher window, until the swipe gesture ends (either in 523 // a successful unlock, or an aborted unlock). 524 surfaceBehindAlpha = 1f 525 setSurfaceBehindAppearAmount(1f) 526 527 launcherUnlockController?.playUnlockAnimation( 528 true, 529 UNLOCK_ANIMATION_DURATION_MS + CANNED_UNLOCK_START_DELAY, 530 0 /* startDelay */) 531 532 launcherPreparedForUnlock = false 533 } else { 534 // Otherwise, we're swiping in an app and should just fade it in. The swipe gesture 535 // will translate it until the end of the swipe gesture. 536 fadeInSurfaceBehind() 537 } 538 } else { 539 // The surface was made visible since we're unlocking not from a swipe (fingerprint, 540 // lock icon long-press, etc). Play the full unlock animation. 541 playCannedUnlockAnimation() 542 } 543 544 listeners.forEach { 545 it.onUnlockAnimationStarted( 546 playingCannedUnlockAnimation /* playingCannedAnimation */, 547 biometricUnlockControllerLazy.get().isWakeAndUnlock /* isWakeAndUnlock */, 548 CANNED_UNLOCK_START_DELAY /* unlockStartDelay */, 549 LAUNCHER_ICONS_ANIMATION_DURATION_MS /* unlockAnimationDuration */) } 550 551 // Finish the keyguard remote animation if the dismiss amount has crossed the threshold. 552 // Check it here in case there is no more change to the dismiss amount after the last change 553 // that starts the keyguard animation. @see #updateKeyguardViewMediatorIfThresholdsReached() 554 finishKeyguardExitRemoteAnimationIfReachThreshold() 555 } 556 557 /** 558 * Play a canned unlock animation to unlock the device. This is used when we were *not* swiping 559 * to unlock using a touch gesture. If we were swiping to unlock, the animation will be driven 560 * by the dismiss amount via [onKeyguardDismissAmountChanged]. 561 */ 562 private fun playCannedUnlockAnimation() { 563 Log.d(TAG, "playCannedUnlockAnimation") 564 playingCannedUnlockAnimation = true 565 566 when { 567 // If we're set up for in-window launcher animations, ask Launcher to play its in-window 568 // canned animation. 569 willUnlockWithInWindowLauncherAnimations -> { 570 Log.d(TAG, "playCannedUnlockAnimation, unlockToLauncherWithInWindowAnimations") 571 unlockToLauncherWithInWindowAnimations() 572 } 573 574 // If we're waking and unlocking to a non-Launcher app surface (or Launcher in-window 575 // animations are not available), show it immediately and end the remote animation. The 576 // circular light reveal will show the app surface, and it looks weird if it's moving 577 // around behind that. 578 biometricUnlockControllerLazy.get().isWakeAndUnlock -> { 579 Log.d(TAG, "playCannedUnlockAnimation, isWakeAndUnlock") 580 setSurfaceBehindAppearAmount(1f) 581 keyguardViewMediator.get().exitKeyguardAndFinishSurfaceBehindRemoteAnimation( 582 false /* cancelled */) 583 } 584 585 // Otherwise, we're doing a normal full-window unlock. Start this animator, which will 586 // scale/translate the window underneath the lockscreen. 587 else -> { 588 Log.d(TAG, "playCannedUnlockAnimation, surfaceBehindEntryAnimator#start") 589 surfaceBehindEntryAnimator.start() 590 } 591 } 592 593 if (launcherPreparedForUnlock && !willUnlockWithInWindowLauncherAnimations) { 594 Log.wtf(TAG, "Launcher is prepared for unlock, so we should have started the " + 595 "in-window animation, however we apparently did not.") 596 logInWindowAnimationConditions() 597 } 598 } 599 600 /** 601 * Unlock to the launcher, using in-window animations, and the smartspace shared element 602 * transition if possible. 603 */ 604 private fun unlockToLauncherWithInWindowAnimations() { 605 setSurfaceBehindAppearAmount(1f) 606 607 // Begin the animation, waiting for the shade to animate out. 608 launcherUnlockController?.playUnlockAnimation( 609 true /* unlocked */, 610 LAUNCHER_ICONS_ANIMATION_DURATION_MS /* duration */, 611 CANNED_UNLOCK_START_DELAY /* startDelay */) 612 613 launcherPreparedForUnlock = false 614 615 // Now that the Launcher surface (with its smartspace positioned identically to ours) is 616 // visible, hide our smartspace. 617 lockscreenSmartspace?.visibility = View.INVISIBLE 618 619 // As soon as the shade has animated out of the way, finish the keyguard exit animation. The 620 // in-window animations in the Launcher window will end on their own. 621 handler.postDelayed({ 622 if (keyguardViewMediator.get().isShowingAndNotOccluded && 623 !keyguardStateController.isKeyguardGoingAway) { 624 Log.e(TAG, "Finish keyguard exit animation delayed Runnable ran, but we are " + 625 "showing and not going away.") 626 return@postDelayed 627 } 628 629 keyguardViewMediator.get().exitKeyguardAndFinishSurfaceBehindRemoteAnimation( 630 false /* cancelled */) 631 }, CANNED_UNLOCK_START_DELAY) 632 } 633 634 /** 635 * Sets the appearance amount of the surface behind the keyguard, according to the current 636 * keyguard dismiss amount and the method of dismissal. 637 */ 638 private fun updateSurfaceBehindAppearAmount() { 639 if (surfaceBehindRemoteAnimationTargets == null) { 640 return 641 } 642 643 if (playingCannedUnlockAnimation) { 644 return 645 } 646 647 // For fling animations, we want to animate the surface in over the full distance. If we're 648 // dismissing the keyguard via a swipe gesture (or cancelling the swipe gesture), we want to 649 // bring in the surface behind over a relatively short swipe distance (~15%), to keep the 650 // interaction tight. 651 if (keyguardStateController.isFlingingToDismissKeyguard) { 652 setSurfaceBehindAppearAmount(keyguardStateController.dismissAmount) 653 } else if (keyguardStateController.isDismissingFromSwipe || 654 keyguardStateController.isSnappingKeyguardBackAfterSwipe) { 655 val totalSwipeDistanceToDismiss = 656 (DISMISS_AMOUNT_EXIT_KEYGUARD_THRESHOLD - DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD) 657 val swipedDistanceSoFar: Float = 658 keyguardStateController.dismissAmount - DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD 659 val progress = swipedDistanceSoFar / totalSwipeDistanceToDismiss 660 setSurfaceBehindAppearAmount(progress) 661 } 662 } 663 664 override fun onKeyguardDismissAmountChanged() { 665 if (!willHandleUnlockAnimation()) { 666 return 667 } 668 669 if (keyguardStateController.isShowing && !playingCannedUnlockAnimation) { 670 showOrHideSurfaceIfDismissAmountThresholdsReached() 671 672 // If the surface is visible or it's about to be, start updating its appearance to 673 // reflect the new dismiss amount. 674 if ((keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard() || 675 keyguardViewMediator.get() 676 .isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe) && 677 !playingCannedUnlockAnimation) { 678 updateSurfaceBehindAppearAmount() 679 } 680 } 681 } 682 683 /** 684 * Lets the KeyguardViewMediator know if the dismiss amount has crossed a threshold of interest, 685 * such as reaching the point in the dismiss swipe where we need to make the surface behind the 686 * keyguard visible. 687 */ 688 private fun showOrHideSurfaceIfDismissAmountThresholdsReached() { 689 if (!featureFlags.isEnabled(Flags.NEW_UNLOCK_SWIPE_ANIMATION)) { 690 return 691 } 692 693 // If we are playing the canned unlock animation, we flung away the keyguard to hide it and 694 // started a canned animation to show the surface behind the keyguard. The fling will cause 695 // panel height/dismiss amount updates, but we should ignore those updates here since the 696 // surface behind is already visible and animating. 697 if (playingCannedUnlockAnimation) { 698 return 699 } 700 701 if (!keyguardStateController.isShowing) { 702 return 703 } 704 705 val dismissAmount = keyguardStateController.dismissAmount 706 707 if (dismissAmount >= DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD && 708 !keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard()) { 709 710 keyguardViewMediator.get().showSurfaceBehindKeyguard() 711 } else if (dismissAmount < DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD && 712 keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard()) { 713 // We're no longer past the threshold but we are showing the surface. Animate it 714 // out. 715 keyguardViewMediator.get().hideSurfaceBehindKeyguard() 716 fadeOutSurfaceBehind() 717 } 718 719 finishKeyguardExitRemoteAnimationIfReachThreshold() 720 } 721 722 /** 723 * Hides the keyguard if we're fully dismissed, or if we're swiping to dismiss and have crossed 724 * the threshold to finish the dismissal. 725 */ 726 private fun finishKeyguardExitRemoteAnimationIfReachThreshold() { 727 // no-op if keyguard is not showing or animation is not enabled. 728 if (!KeyguardService.sEnableRemoteKeyguardGoingAwayAnimation || 729 !keyguardStateController.isShowing) { 730 return 731 } 732 733 // no-op if animation is not requested yet. 734 if (!keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard() || 735 !keyguardViewMediator.get().isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe) { 736 return 737 } 738 739 val dismissAmount = keyguardStateController.dismissAmount 740 if (dismissAmount >= 1f || 741 (keyguardStateController.isDismissingFromSwipe && 742 // Don't hide if we're flinging during a swipe, since we need to finish 743 // animating it out. This will be called again after the fling ends. 744 !keyguardStateController.isFlingingToDismissKeyguardDuringSwipeGesture && 745 dismissAmount >= DISMISS_AMOUNT_EXIT_KEYGUARD_THRESHOLD)) { 746 setSurfaceBehindAppearAmount(1f) 747 keyguardViewMediator.get().exitKeyguardAndFinishSurfaceBehindRemoteAnimation( 748 false /* cancelled */) 749 } 750 } 751 752 /** 753 * Scales in and translates up the surface behind the keyguard. This is used during unlock 754 * animations and swipe gestures to animate the surface's entry (and exit, if the swipe is 755 * cancelled). 756 */ 757 fun setSurfaceBehindAppearAmount(amount: Float) { 758 surfaceBehindRemoteAnimationTargets?.forEach { surfaceBehindRemoteAnimationTarget -> 759 val surfaceHeight: Int = surfaceBehindRemoteAnimationTarget.screenSpaceBounds.height() 760 761 var scaleFactor = (SURFACE_BEHIND_START_SCALE_FACTOR + 762 (1f - SURFACE_BEHIND_START_SCALE_FACTOR) * 763 MathUtils.clamp(amount, 0f, 1f)) 764 765 // If we're dismissing via swipe to the Launcher, we'll play in-window scale animations, 766 // so don't also scale the window. 767 if (keyguardStateController.isDismissingFromSwipe && 768 willUnlockWithInWindowLauncherAnimations) { 769 scaleFactor = 1f 770 } 771 772 // Translate up from the bottom. 773 surfaceBehindMatrix.setTranslate( 774 surfaceBehindRemoteAnimationTarget.screenSpaceBounds.left.toFloat(), 775 surfaceHeight * SURFACE_BEHIND_START_TRANSLATION_Y * (1f - amount) 776 ) 777 778 // Scale up from a point at the center-bottom of the surface. 779 surfaceBehindMatrix.postScale( 780 scaleFactor, 781 scaleFactor, 782 keyguardViewController.viewRootImpl.width / 2f, 783 surfaceHeight * SURFACE_BEHIND_SCALE_PIVOT_Y 784 ) 785 786 787 val animationAlpha = when { 788 // If we're snapping the keyguard back, immediately begin fading it out. 789 keyguardStateController.isSnappingKeyguardBackAfterSwipe -> amount 790 // If the screen has turned back off, the unlock animation is going to be cancelled, 791 // so set the surface alpha to 0f so it's no longer visible. 792 !powerManager.isInteractive -> 0f 793 else -> surfaceBehindAlpha 794 } 795 796 // SyncRtSurfaceTransactionApplier cannot apply transaction when the target view is 797 // unable to draw 798 val sc: SurfaceControl? = surfaceBehindRemoteAnimationTarget.leash 799 if (keyguardViewController.viewRootImpl.view?.visibility != View.VISIBLE && 800 sc?.isValid == true) { 801 with(SurfaceControl.Transaction()) { 802 setMatrix(sc, surfaceBehindMatrix, tmpFloat) 803 setCornerRadius(sc, roundedCornerRadius) 804 setAlpha(sc, animationAlpha) 805 apply() 806 } 807 } else { 808 applyParamsToSurface( 809 SyncRtSurfaceTransactionApplier.SurfaceParams.Builder( 810 surfaceBehindRemoteAnimationTarget.leash) 811 .withMatrix(surfaceBehindMatrix) 812 .withCornerRadius(roundedCornerRadius) 813 .withAlpha(animationAlpha) 814 .build() 815 ) 816 } 817 } 818 } 819 820 /** 821 * Called by [KeyguardViewMediator] to let us know that the remote animation has finished, and 822 * we should clean up all of our state. 823 * 824 * This is generally triggered by us, calling 825 * [KeyguardViewMediator.finishSurfaceBehindRemoteAnimation]. 826 */ 827 fun notifyFinishedKeyguardExitAnimation(cancelled: Boolean) { 828 // Cancel any pending actions. 829 handler.removeCallbacksAndMessages(null) 830 831 // Make sure we made the surface behind fully visible, just in case. It should already be 832 // fully visible. The exit animation is finished, and we should not hold the leash anymore, 833 // so forcing it to 1f. 834 surfaceBehindAlpha = 1f 835 setSurfaceBehindAppearAmount(1f) 836 surfaceBehindAlphaAnimator.cancel() 837 surfaceBehindEntryAnimator.cancel() 838 try { 839 launcherUnlockController?.setUnlockAmount(1f, false /* forceIfAnimating */) 840 } catch (e: RemoteException) { 841 Log.e(TAG, "Remote exception in notifyFinishedKeyguardExitAnimation", e) 842 } 843 844 // That target is no longer valid since the animation finished, null it out. 845 surfaceBehindRemoteAnimationTargets = null 846 847 playingCannedUnlockAnimation = false 848 willUnlockWithInWindowLauncherAnimations = false 849 willUnlockWithSmartspaceTransition = false 850 851 // The lockscreen surface is gone, so it is now safe to re-show the smartspace. 852 lockscreenSmartspace?.visibility = View.VISIBLE 853 854 listeners.forEach { it.onUnlockAnimationFinished() } 855 } 856 857 /** 858 * Asks the keyguard view to hide, using the start time from the beginning of the remote 859 * animation. 860 */ 861 fun hideKeyguardViewAfterRemoteAnimation() { 862 if (keyguardStateController.isShowing) { 863 // Hide the keyguard, with no fade out since we animated it away during the unlock. 864 865 keyguardViewController.hide( 866 surfaceBehindRemoteAnimationStartTime, 867 0 /* fadeOutDuration */ 868 ) 869 } else { 870 Log.e(TAG, "#hideKeyguardViewAfterRemoteAnimation called when keyguard view is not " + 871 "showing. Ignoring...") 872 } 873 } 874 875 private fun applyParamsToSurface(params: SyncRtSurfaceTransactionApplier.SurfaceParams) { 876 surfaceTransactionApplier!!.scheduleApply(params) 877 } 878 879 private fun fadeInSurfaceBehind() { 880 Log.d(TAG, "fadeInSurfaceBehind") 881 surfaceBehindAlphaAnimator.cancel() 882 surfaceBehindAlphaAnimator.start() 883 } 884 885 private fun fadeOutSurfaceBehind() { 886 Log.d(TAG, "fadeOutSurfaceBehind") 887 surfaceBehindAlphaAnimator.cancel() 888 surfaceBehindAlphaAnimator.reverse() 889 } 890 891 private fun shouldPerformSmartspaceTransition(): Boolean { 892 // Feature is disabled, so we don't want to. 893 if (!featureFlags.isEnabled(Flags.SMARTSPACE_SHARED_ELEMENT_TRANSITION_ENABLED)) { 894 return false 895 } 896 897 // If our controllers are null, or we haven't received a smartspace state from Launcher yet, 898 // we will not be doing any smartspace transitions today. 899 if (launcherUnlockController == null || 900 lockscreenSmartspace == null || 901 launcherSmartspaceState == null) { 902 return false 903 } 904 905 // If the launcher does not have a visible smartspace (either because it's paged off-screen, 906 // or the smartspace just doesn't exist), we can't do the transition. 907 if ((launcherSmartspaceState?.visibleOnScreen) != true) { 908 return false 909 } 910 911 // If our launcher isn't underneath, then we're unlocking to an app or custom launcher, 912 // neither of which have a smartspace. 913 if (!isNexusLauncherUnderneath()) { 914 return false 915 } 916 917 // TODO(b/213910911): Unfortunately the keyguard is hidden instantly on wake and unlock, so 918 // we won't have a lockscreen smartspace to animate. This is sad, and we should fix that! 919 if (biometricUnlockControllerLazy.get().isWakeAndUnlock) { 920 return false 921 } 922 923 // If we can't dismiss the lock screen via a swipe, then the only way we can do the shared 924 // element transition is if we're doing a biometric unlock. Otherwise, it means the bouncer 925 // is showing, and you can't see the lockscreen smartspace, so a shared element transition 926 // would not make sense. 927 if (!keyguardStateController.canDismissLockScreen() && 928 !biometricUnlockControllerLazy.get().isBiometricUnlock) { 929 return false 930 } 931 932 // The smartspace is not visible if the bouncer is showing, so don't shared element it. 933 if (keyguardStateController.isPrimaryBouncerShowing) { 934 return false 935 } 936 937 // We started to swipe to dismiss, but now we're doing a fling animation to complete the 938 // dismiss. In this case, the smartspace swiped away with the rest of the keyguard, so don't 939 // do the shared element transition. 940 if (keyguardStateController.isFlingingToDismissKeyguardDuringSwipeGesture) { 941 return false 942 } 943 944 // If we're swiping to dismiss, the smartspace will be swiped off the top of the screen 945 // so we can't shared element animate it. 946 if (keyguardStateController.isDismissingFromSwipe) { 947 return false 948 } 949 950 // We don't do the shared element on large screens because the smartspace has to fly across 951 // large distances, which is distracting. 952 if (Utilities.isLargeScreen(context)) { 953 return false 954 } 955 956 return true 957 } 958 959 /** 960 * Whether we are currently in the process of unlocking the keyguard, and we are performing the 961 * shared element SmartSpace transition. 962 */ 963 fun isUnlockingWithSmartSpaceTransition(): Boolean { 964 return willUnlockWithSmartspaceTransition 965 } 966 967 /** 968 * Whether this animation controller will be handling the unlock. We require remote animations 969 * to be enabled to do this. 970 * 971 * If this is not true, nothing in this class is relevant, and the unlock will be handled in 972 * [KeyguardViewMediator]. 973 */ 974 fun willHandleUnlockAnimation(): Boolean { 975 return KeyguardService.sEnableRemoteKeyguardGoingAwayAnimation 976 } 977 978 /** 979 * Whether the RemoteAnimation on the app/launcher surface behind the keyguard is 'running'. 980 */ 981 fun isAnimatingBetweenKeyguardAndSurfaceBehind(): Boolean { 982 return keyguardViewMediator.get().isAnimatingBetweenKeyguardAndSurfaceBehind 983 } 984 985 /** 986 * Whether we are playing a canned unlock animation, vs. unlocking from a touch gesture such as 987 * a swipe. 988 */ 989 fun isPlayingCannedUnlockAnimation(): Boolean { 990 return playingCannedUnlockAnimation 991 } 992 993 companion object { 994 /** 995 * Return whether the Google Nexus launcher is underneath the keyguard, vs. some other 996 * launcher or an app. If so, we can communicate with it to perform in-window/shared element 997 * transitions! 998 */ 999 fun isNexusLauncherUnderneath(): Boolean { 1000 return ActivityManagerWrapper.getInstance() 1001 .runningTask?.topActivity?.className?.equals( 1002 QuickStepContract.LAUNCHER_ACTIVITY_CLASS_NAME) ?: false 1003 } 1004 1005 fun isFoldable(context: Context): Boolean { 1006 return context.resources.getIntArray(R.array.config_foldedDeviceStates).isNotEmpty() 1007 } 1008 } 1009 } 1010