• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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