• 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.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