Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6b095216 authored by Josh Tsuji's avatar Josh Tsuji Committed by Android (Google) Code Review
Browse files

Merge "Adds the continuous unlock animation!" into sc-dev

parents d2b8ec4f ea36ab65
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -145,6 +145,13 @@ public interface KeyguardViewController {
     */
    void startPreHideAnimation(Runnable finishRunnable);

    /**
     * Blocks the current touch gesture from affecting the expansion amount of the notification
     * panel. This is used after a completed unlock gesture to ignore further dragging before an
     * ACTION_UP.
     */
    void blockPanelExpansionFromCurrentTouch();

    /**
     * @return the ViewRootImpl of the View where the Keyguard is mounted.
     */
+2 −1
Original line number Diff line number Diff line
@@ -70,7 +70,7 @@ public class KeyguardService extends Service {
    /**
     * @see #ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY
     */
    private static boolean sEnableRemoteKeyguardAnimation =
    static boolean sEnableRemoteKeyguardAnimation =
            SystemProperties.getBoolean(ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY, false);

    private final KeyguardViewMediator mKeyguardViewMediator;
@@ -138,6 +138,7 @@ public class KeyguardService extends Service {

        @Override // Binder interface
        public void onAnimationCancelled() {
            mKeyguardViewMediator.cancelKeyguardExitAnimation();
        }
    };

+329 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.keyguard

import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.content.Context
import android.graphics.Matrix
import android.view.RemoteAnimationTarget
import android.view.SyncRtSurfaceTransactionApplier
import androidx.core.math.MathUtils
import com.android.internal.R
import com.android.keyguard.KeyguardViewController
import com.android.systemui.animation.Interpolators
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.policy.KeyguardStateController
import dagger.Lazy
import javax.inject.Inject

/**
 * Starting scale factor for the app/launcher surface behind the keyguard, when it's animating
 * in during keyguard exit.
 */
const val SURFACE_BEHIND_START_SCALE_FACTOR = 0.95f

/**
 * How much to translate the surface behind the keyguard at the beginning of the exit animation,
 * in terms of percentage of the surface's height.
 */
const val SURFACE_BEHIND_START_TRANSLATION_Y = 0.05f

/**
 * Y coordinate of the pivot point for the scale effect on the surface behind the keyguard. This
 * is expressed as percentage of the surface's height, so 0.66f means the surface will scale up
 * from the point at (width / 2, height * 0.66).
 */
const val SURFACE_BEHIND_SCALE_PIVOT_Y = 0.66f

/**
 * Dismiss amount at which to fade in the surface behind the keyguard. The surface will then animate
 * along with the dismiss amount until [DISMISS_AMOUNT_EXIT_KEYGUARD_THRESHOLD] is reached.
 *
 * The dismiss amount is the inverse of the notification panel expansion, which decreases as the
 * lock screen is swiped away.
 */
const val DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD = 0.1f

/**
 * Dismiss amount at which to complete the keyguard exit animation and hide the keyguard.
 *
 * The dismiss amount is the inverse of the notification panel expansion, which decreases as the
 * lock screen is swiped away.
 */
const val DISMISS_AMOUNT_EXIT_KEYGUARD_THRESHOLD = 0.3f

/**
 * Initiates, controls, and ends the keyguard unlock animation.
 *
 * The unlock animation transitions between the keyguard (lock screen) and the app/launcher surface
 * behind the keyguard. If the user is swiping away the keyguard, this controller will decide when
 * to animate in the surface, and synchronize its appearance with the swipe gesture. If the keyguard
 * is animating away via a canned animation (due to biometric unlock, tapping a notification, etc.)
 * this controller will play a canned animation on the surface as well.
 *
 * The surface behind the keyguard is manipulated via a RemoteAnimation passed to
 * [notifyStartKeyguardExitAnimation] by [KeyguardViewMediator].
 */
@SysUISingleton
class KeyguardUnlockAnimationController @Inject constructor(
    context: Context,
    private val keyguardStateController: KeyguardStateController,
    private val keyguardViewMediator: Lazy<KeyguardViewMediator>,
    private val keyguardViewController: KeyguardViewController
) : KeyguardStateController.Callback {

    /**
     * Information used to start, run, and finish a RemoteAnimation on the app or launcher surface
     * behind the keyguard.
     *
     * If we're swiping to unlock, the "animation" is controlled via the gesture, tied to the
     * dismiss amounts received in [onKeyguardDismissAmountChanged]. It does not have a fixed
     * duration, and it ends when the gesture reaches a certain threshold or is cancelled.
     *
     * If we're unlocking via biometrics, PIN entry, or from clicking a notification, a canned
     * animation is started in [notifyStartKeyguardExitAnimation].
     */
    private var surfaceTransactionApplier: SyncRtSurfaceTransactionApplier? = null
    private var surfaceBehindRemoteAnimationTarget: RemoteAnimationTarget? = null
    private var surfaceBehindRemoteAnimationStartTime: Long = 0

    /**
     * Alpha value applied to [surfaceBehindRemoteAnimationTarget], which is the surface of the
     * app/launcher behind the keyguard.
     *
     * If we're doing a swipe gesture, we fade in the surface when the swipe passes a certain
     * threshold. If we're doing a canned animation, it'll be faded in while a translate/scale
     * animation plays.
     */
    private var surfaceBehindAlpha = 1f
    private var surfaceBehindAlphaAnimator = ValueAnimator.ofFloat(0f, 1f)

    /**
     * Matrix applied to [surfaceBehindRemoteAnimationTarget], which is the surface of the
     * app/launcher behind the keyguard.
     *
     * This is used during the unlock animation/swipe gesture to scale and translate the surface.
     */
    private val surfaceBehindMatrix = Matrix()

    /**
     * Animator that animates in the surface behind the keyguard. This is used to play a canned
     * animation on the surface, if we're not doing a swipe gesture.
     */
    private val surfaceBehindEntryAnimator = ValueAnimator.ofFloat(0f, 1f)

    /** Rounded corner radius to apply to the surface behind the keyguard. */
    private var roundedCornerRadius = 0f

    init {
        surfaceBehindAlphaAnimator.duration = 150
        surfaceBehindAlphaAnimator.interpolator = Interpolators.ALPHA_IN
        surfaceBehindAlphaAnimator.addUpdateListener { valueAnimator: ValueAnimator ->
            surfaceBehindAlpha = valueAnimator.animatedValue as Float
            updateSurfaceBehindAppearAmount()
        }
        surfaceBehindAlphaAnimator.addListener(object : AnimatorListenerAdapter() {
            override fun onAnimationEnd(animation: Animator) {
                // If the surface alpha is 0f, it's no longer visible so we can safely be done with
                // the animation.
                if (surfaceBehindAlpha == 0f) {
                    keyguardViewMediator.get().finishSurfaceBehindRemoteAnimation()
                }
            }
        })

        surfaceBehindEntryAnimator.duration = 450
        surfaceBehindEntryAnimator.interpolator = Interpolators.DECELERATE_QUINT
        surfaceBehindEntryAnimator.addUpdateListener { valueAnimator: ValueAnimator ->
            surfaceBehindAlpha = valueAnimator.animatedValue as Float
            setSurfaceBehindAppearAmount(valueAnimator.animatedValue as Float)
        }
        surfaceBehindEntryAnimator.addListener(object : AnimatorListenerAdapter() {
            override fun onAnimationEnd(animation: Animator) {
                keyguardViewMediator.get().onKeyguardExitRemoteAnimationFinished()
            }
        })

        // Listen for changes in the dismiss amount.
        keyguardStateController.addCallback(this)

        roundedCornerRadius =
                context.resources.getDimensionPixelSize(R.dimen.rounded_corner_radius).toFloat()
    }

    /**
     * Called from [KeyguardViewMediator] to tell us that the RemoteAnimation on the surface behind
     * the keyguard has started successfully. We can use these parameters to directly manipulate the
     * surface for the unlock gesture/animation.
     *
     * When we're done with it, we'll call [KeyguardViewMediator.finishSurfaceBehindRemoteAnimation]
     * to end the RemoteAnimation.
     *
     * [requestedShowSurfaceBehindKeyguard] denotes whether the exit animation started because of a
     * call to [KeyguardViewMediator.showSurfaceBehindKeyguard], as happens during a swipe gesture,
     * as opposed to the keyguard hiding.
     */
    fun notifyStartKeyguardExitAnimation(
        target: RemoteAnimationTarget,
        startTime: Long,
        requestedShowSurfaceBehindKeyguard: Boolean
    ) {

        if (surfaceTransactionApplier == null) {
            surfaceTransactionApplier = SyncRtSurfaceTransactionApplier(
                    keyguardViewController.viewRootImpl.view)
        }

        surfaceBehindRemoteAnimationTarget = target
        surfaceBehindRemoteAnimationStartTime = startTime

        // If the surface behind wasn't made visible during a swipe, we'll do a canned animation
        // to animate it in. Otherwise, the swipe touch events will continue animating it.
        if (!requestedShowSurfaceBehindKeyguard) {
            keyguardViewController.hide(startTime, 350)
            surfaceBehindEntryAnimator.start()
        }
    }

    fun notifyCancelKeyguardExitAnimation() {
        surfaceBehindRemoteAnimationTarget = null
    }

    fun notifyFinishedKeyguardExitAnimation() {
        surfaceBehindRemoteAnimationTarget = null
    }

    fun hideKeyguardViewAfterRemoteAnimation() {
        keyguardViewController.hide(surfaceBehindRemoteAnimationStartTime, 350)
    }

    /**
     * Scales in and translates up the surface behind the keyguard. This is used during unlock
     * animations and swipe gestures to animate the surface's entry (and exit, if the swipe is
     * cancelled).
     */
    private fun setSurfaceBehindAppearAmount(amount: Float) {
        if (surfaceBehindRemoteAnimationTarget == null) {
            return
        }

        val surfaceHeight: Int = surfaceBehindRemoteAnimationTarget!!.screenSpaceBounds.height()
        val scaleFactor = (SURFACE_BEHIND_START_SCALE_FACTOR +
                (1f - SURFACE_BEHIND_START_SCALE_FACTOR) *
                MathUtils.clamp(amount, 0f, 1f))

        // Scale up from a point at the center-bottom of the surface.
        surfaceBehindMatrix.setScale(
                scaleFactor,
                scaleFactor,
                surfaceBehindRemoteAnimationTarget!!.screenSpaceBounds.width() / 2f,
                surfaceHeight * SURFACE_BEHIND_SCALE_PIVOT_Y)

        // Translate up from the bottom.
        surfaceBehindMatrix.postTranslate(0f,
                surfaceHeight * SURFACE_BEHIND_START_TRANSLATION_Y * (1f - amount))

        // If we're snapping the keyguard back, immediately begin fading it out.
        val animationAlpha =
                if (keyguardStateController.isSnappingKeyguardBackAfterSwipe) amount
                else surfaceBehindAlpha

        val params = SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(
                surfaceBehindRemoteAnimationTarget!!.leash)
                .withMatrix(surfaceBehindMatrix)
                .withCornerRadius(roundedCornerRadius)
                .withAlpha(animationAlpha)
                .build()
        surfaceTransactionApplier!!.scheduleApply(params)
    }

    /**
     * Sets the appearance amount of the surface behind the keyguard, according to the current
     * keyguard dismiss amount and the method of dismissal.
     */
    private fun updateSurfaceBehindAppearAmount() {
        if (surfaceBehindRemoteAnimationTarget == null) {
            return
        }

        // For fling animations, we want to animate the surface in over the full distance. If we're
        // dismissing the keyguard via a swipe gesture (or cancelling the swipe gesture), we want to
        // bring in the surface behind over a relatively short swipe distance (~15%), to keep the
        // interaction tight.
        if (keyguardStateController.isFlingingToDismissKeyguard) {
            setSurfaceBehindAppearAmount(keyguardStateController.dismissAmount)
        } else if (keyguardStateController.isDismissingFromSwipe ||
                keyguardStateController.isSnappingKeyguardBackAfterSwipe) {
            val totalSwipeDistanceToDismiss =
                    (DISMISS_AMOUNT_EXIT_KEYGUARD_THRESHOLD - DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD)
            val swipedDistanceSoFar: Float =
                    keyguardStateController.dismissAmount - DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD
            val progress = swipedDistanceSoFar / totalSwipeDistanceToDismiss
            setSurfaceBehindAppearAmount(progress)
        }
    }

    override fun onKeyguardDismissAmountChanged() {
        if (!KeyguardService.sEnableRemoteKeyguardAnimation) {
            return
        }

        val dismissAmount = keyguardStateController.dismissAmount

        // Hide the keyguard if we're fully dismissed, or if we're swiping to dismiss and have
        // crossed the threshold to finish the dismissal.
        val reachedHideKeyguardThreshold = (dismissAmount >= 1f ||
                (keyguardStateController.isDismissingFromSwipe &&
                // Don't hide if we're flinging during a swipe, since we need to finish
                // animating it out. This will be called again after the fling ends.
                !keyguardStateController.isFlingingToDismissKeyguardDuringSwipeGesture &&
                dismissAmount >= DISMISS_AMOUNT_EXIT_KEYGUARD_THRESHOLD))

        if (dismissAmount >= DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD &&
                !keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard()) {
            // We passed the threshold, and we're not yet showing the surface behind the keyguard.
            // Animate it in.
            keyguardViewMediator.get().showSurfaceBehindKeyguard()
            fadeInSurfaceBehind()
        } else if (dismissAmount < DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD &&
                keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard()) {
            // We're no longer past the threshold but we are showing the surface. Animate it out.
            keyguardViewMediator.get().hideSurfaceBehindKeyguard()
            fadeOutSurfaceBehind()
        } else if (keyguardViewMediator.get().isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe &&
                reachedHideKeyguardThreshold) {
            keyguardViewMediator.get().onKeyguardExitRemoteAnimationFinished()
        }

        if (keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard() ||
                keyguardViewMediator.get().isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe) {
            updateSurfaceBehindAppearAmount()
        }
    }

    private fun fadeInSurfaceBehind() {
        surfaceBehindAlphaAnimator.cancel()
        surfaceBehindAlphaAnimator.start()
    }

    private fun fadeOutSurfaceBehind() {
        surfaceBehindAlphaAnimator.cancel()
        surfaceBehindAlphaAnimator.reverse()
    }
}
 No newline at end of file
+231 −51

File changed.

Preview size limit exceeded, changes collapsed.

+8 −2
Original line number Diff line number Diff line
@@ -42,6 +42,7 @@ import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.DismissCallbackRegistry;
import com.android.systemui.keyguard.FaceAuthScreenBrightnessController;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -49,6 +50,7 @@ import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.KeyguardLiftController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.sensors.AsyncSensorManager;
import com.android.systemui.util.settings.GlobalSettings;
@@ -92,7 +94,9 @@ public class KeyguardModule {
            NavigationModeController navigationModeController,
            KeyguardDisplayManager keyguardDisplayManager,
            DozeParameters dozeParameters,
            StatusBarStateController statusBarStateController) {
            StatusBarStateController statusBarStateController,
            KeyguardStateController keyguardStateController,
            Lazy<KeyguardUnlockAnimationController> keyguardUnlockAnimationController) {
        return new KeyguardViewMediator(
                context,
                falsingCollector,
@@ -109,7 +113,9 @@ public class KeyguardModule {
                navigationModeController,
                keyguardDisplayManager,
                dozeParameters,
                statusBarStateController
                statusBarStateController,
                keyguardStateController,
                keyguardUnlockAnimationController
        );
    }

Loading