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