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

Commit 2c52d52f authored by Jordan Demeulenaere's avatar Jordan Demeulenaere Committed by Android (Google) Code Review
Browse files

Merge "Animate app launches from lockscreen" into sc-dev

parents ad1cfd0d f90ca12d
Loading
Loading
Loading
Loading
+59 −29
Original line number Diff line number Diff line
@@ -32,7 +32,10 @@ import kotlin.math.roundToInt
 * A class that allows activities to be started in a seamless way from a view that is transforming
 * nicely into the starting window.
 */
class ActivityLaunchAnimator(context: Context) {
class ActivityLaunchAnimator(
    private val keyguardHandler: KeyguardHandler,
    context: Context
) {
    private val TAG = this::class.java.simpleName

    companion object {
@@ -104,15 +107,22 @@ class ActivityLaunchAnimator(context: Context) {

        Log.d(TAG, "Starting intent with a launch animation")
        val runner = Runner(controller)
        val animationAdapter = RemoteAnimationAdapter(
        val isOnKeyguard = keyguardHandler.isOnKeyguard()

        // Pass the RemoteAnimationAdapter to the intent starter only if we are not on the keyguard.
        val animationAdapter = if (!isOnKeyguard) {
            RemoteAnimationAdapter(
                    runner,
                    ANIMATION_DURATION,
                    ANIMATION_DURATION - 150 /* statusBarTransitionDelay */
            )
        } else {
            null
        }

        // Register the remote animation for the given package to also animate trampoline
        // activity launches.
        if (packageName != null) {
        if (packageName != null && animationAdapter != null) {
            try {
                ActivityTaskManager.getService().registerRemoteAnimationForNextActivityStart(
                    packageName, animationAdapter)
@@ -122,20 +132,30 @@ class ActivityLaunchAnimator(context: Context) {
        }

        val launchResult = intentStarter(animationAdapter)
        val willAnimate = launchResult == ActivityManager.START_TASK_TO_FRONT ||
            launchResult == ActivityManager.START_SUCCESS

        Log.d(TAG, "launchResult=$launchResult willAnimate=$willAnimate")
        // Only animate if the app is not already on top and will be opened, unless we are on the
        // keyguard.
        val willAnimate =
                launchResult == ActivityManager.START_TASK_TO_FRONT ||
                        launchResult == ActivityManager.START_SUCCESS ||
                        (launchResult == ActivityManager.START_DELIVERED_TO_TOP && isOnKeyguard)

        Log.d(TAG, "launchResult=$launchResult willAnimate=$willAnimate isOnKeyguard=$isOnKeyguard")
        controller.callOnIntentStartedOnMainThread(willAnimate)

        // If we expect an animation, post a timeout to cancel it in case the remote animation is
        // never started.
        if (willAnimate) {
            runner.postTimeout()

            // Hide the keyguard using the launch animation instead of the default unlock animation.
            if (isOnKeyguard) {
                keyguardHandler.hideKeyguardWithAnimation(runner)
            }
        }
    }

    internal fun Controller.callOnIntentStartedOnMainThread(willAnimate: Boolean) {
    private fun Controller.callOnIntentStartedOnMainThread(willAnimate: Boolean) {
        if (Looper.myLooper() != Looper.getMainLooper()) {
            this.launchContainer.context.mainExecutor.execute {
                this.onIntentStarted(willAnimate)
@@ -179,6 +199,14 @@ class ActivityLaunchAnimator(context: Context) {
        fun startPendingIntent(animationAdapter: RemoteAnimationAdapter?): Int
    }

    interface KeyguardHandler {
        /** Whether we are currently on the keyguard or not. */
        fun isOnKeyguard(): Boolean

        /** Hide the keyguard and animate using [runner]. */
        fun hideKeyguardWithAnimation(runner: IRemoteAnimationRunner)
    }

    /**
     * A controller that takes care of applying the animation to an expanding view.
     *
@@ -337,17 +365,17 @@ class ActivityLaunchAnimator(context: Context) {

        override fun onAnimationStart(
            @WindowManager.TransitionOldType transit: Int,
            remoteAnimationTargets: Array<out RemoteAnimationTarget>,
            remoteAnimationWallpaperTargets: Array<out RemoteAnimationTarget>,
            remoteAnimationNonAppTargets: Array<out RemoteAnimationTarget>,
            iRemoteAnimationFinishedCallback: IRemoteAnimationFinishedCallback
            apps: Array<out RemoteAnimationTarget>?,
            wallpapers: Array<out RemoteAnimationTarget>?,
            nonApps: Array<out RemoteAnimationTarget>?,
            iCallback: IRemoteAnimationFinishedCallback?
        ) {
            removeTimeout()

            // The animation was started too late and we already notified the controller that it
            // timed out.
            if (timedOut) {
                invokeCallback(iRemoteAnimationFinishedCallback)
                iCallback?.invoke()
                return
            }

@@ -358,30 +386,29 @@ class ActivityLaunchAnimator(context: Context) {
            }

            context.mainExecutor.execute {
                startAnimation(remoteAnimationTargets, remoteAnimationNonAppTargets,
                        iRemoteAnimationFinishedCallback)
                startAnimation(apps, nonApps, iCallback)
            }
        }

        private fun startAnimation(
            remoteAnimationTargets: Array<out RemoteAnimationTarget>,
            remoteAnimationNonAppTargets: Array<out RemoteAnimationTarget>,
            iCallback: IRemoteAnimationFinishedCallback
            apps: Array<out RemoteAnimationTarget>?,
            nonApps: Array<out RemoteAnimationTarget>?,
            iCallback: IRemoteAnimationFinishedCallback?
        ) {
            Log.d(TAG, "Remote animation started")
            val window = remoteAnimationTargets.firstOrNull {
            val window = apps?.firstOrNull {
                it.mode == RemoteAnimationTarget.MODE_OPENING
            }

            if (window == null) {
                Log.d(TAG, "Aborting the animation as no window is opening")
                removeTimeout()
                invokeCallback(iCallback)
                iCallback?.invoke()
                controller.onLaunchAnimationCancelled()
                return
            }

            val navigationBar = remoteAnimationNonAppTargets.firstOrNull {
            val navigationBar = nonApps?.firstOrNull {
                it.windowType == WindowManager.LayoutParams.TYPE_NAVIGATION_BAR
            }

@@ -439,7 +466,7 @@ class ActivityLaunchAnimator(context: Context) {

                override fun onAnimationEnd(animation: Animator?) {
                    Log.d(TAG, "Animation ended")
                    invokeCallback(iCallback)
                    iCallback?.invoke()
                    controller.onLaunchAnimationEnd(isExpandingFullyAbove)
                }
            })
@@ -519,8 +546,11 @@ class ActivityLaunchAnimator(context: Context) {
            )

            // The scale will also be applied to the corner radius, so we divide by the scale to
            // keep the original radius.
            val cornerRadius = minOf(state.topCornerRadius, state.bottomCornerRadius) / scale
            // keep the original radius. We use the max of (topCornerRadius, bottomCornerRadius) to
            // make sure that the window does not draw itself behind the expanding view. This is
            // especially important for lock screen animations, where the window is not clipped by
            // the shade.
            val cornerRadius = maxOf(state.topCornerRadius, state.bottomCornerRadius) / scale
            val params = SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(window.leash)
                .withAlpha(1f)
                .withMatrix(matrix)
@@ -585,9 +615,9 @@ class ActivityLaunchAnimator(context: Context) {
            }
        }

        private fun invokeCallback(iCallback: IRemoteAnimationFinishedCallback) {
        private fun IRemoteAnimationFinishedCallback.invoke() {
            try {
                iCallback.onAnimationFinished()
                onAnimationFinished()
            } catch (e: RemoteException) {
                e.printStackTrace()
            }
+1 −1
Original line number Diff line number Diff line
@@ -70,7 +70,7 @@ public class KeyguardService extends Service {
    /**
     * @see #ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY
     */
    static boolean sEnableRemoteKeyguardAnimation =
    public static boolean sEnableRemoteKeyguardAnimation =
            SystemProperties.getBoolean(ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY, false);

    private final KeyguardViewMediator mKeyguardViewMediator;
+62 −19
Original line number Diff line number Diff line
@@ -52,6 +52,7 @@ import android.media.SoundPool;
import android.os.Bundle;
import android.os.DeadObjectException;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
@@ -71,6 +72,7 @@ import android.util.Slog;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.view.IRemoteAnimationFinishedCallback;
import android.view.IRemoteAnimationRunner;
import android.view.RemoteAnimationTarget;
import android.view.SyncRtSurfaceTransactionApplier;
import android.view.View;
@@ -426,6 +428,11 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
     */
    private IRemoteAnimationFinishedCallback mSurfaceBehindRemoteAnimationFinishedCallback;

    /**
     * The animation runner to use for the next exit animation.
     */
    private IRemoteAnimationRunner mKeyguardExitAnimationRunner;

    private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener =
            new DeviceConfig.OnPropertiesChangedListener() {
            @Override
@@ -1605,6 +1612,16 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
        Trace.endSection();
    }

    /** Hide the keyguard and let {@code runner} handle the animation. */
    public void hideWithAnimation(IRemoteAnimationRunner runner) {
        if (!mShowing) {
            return;
        }

        mKeyguardExitAnimationRunner = runner;
        hideLocked();
    }

    public boolean isSecure() {
        return isSecure(KeyguardUpdateMonitor.getCurrentUser());
    }
@@ -2050,6 +2067,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
                // TODO: We should stop it early by disabling the swipe up flow. Right now swipe up
                // still completes and makes the screen blank.
                if (DEBUG) Log.d(TAG, "Split system user, quit unlocking.");
                mKeyguardExitAnimationRunner = null;
                return;
            }
            mHiding = true;
@@ -2103,9 +2121,37 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
                playSounds(false);
            }

            IRemoteAnimationRunner runner = mKeyguardExitAnimationRunner;
            mKeyguardExitAnimationRunner = null;

            if (KeyguardService.sEnableRemoteKeyguardAnimation && runner != null
                    && finishedCallback != null) {
                // Wrap finishedCallback to clean up the keyguard state once the animation is done.
                IRemoteAnimationFinishedCallback callback =
                        new IRemoteAnimationFinishedCallback() {
                            @Override
                            public void onAnimationFinished() throws RemoteException {
                                finishedCallback.onAnimationFinished();
                                onKeyguardExitFinished();
                                mKeyguardViewControllerLazy.get().hide(0 /* startTime */,
                                        0 /* fadeoutDuration */);
                            }

                            @Override
                            public IBinder asBinder() {
                                return finishedCallback.asBinder();
                            }
                        };
                try {
                    runner.onAnimationStart(WindowManager.TRANSIT_KEYGUARD_GOING_AWAY, apps,
                            wallpapers, nonApps, callback);
                } catch (RemoteException e) {
                    Slog.w(TAG, "Failed to call onAnimationStart", e);
                }

            // When remaining on the shade, there's no need to do a fancy remote animation,
            // it will dismiss the panel in that case.
            if (KeyguardService.sEnableRemoteKeyguardAnimation
            } else if (KeyguardService.sEnableRemoteKeyguardAnimation
                    && !mStatusBarStateController.leaveOpenOnKeyguardHide()
                    && apps != null && apps.length > 0) {
                mSurfaceBehindRemoteAnimationFinishedCallback = finishedCallback;
@@ -2115,9 +2161,6 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
                mKeyguardUnlockAnimationControllerLazy.get().notifyStartKeyguardExitAnimation(
                        apps[0], startTime, mSurfaceBehindRemoteAnimationRequested);
            } else {
                setShowingLocked(false);
                mWakeAndUnlocking = false;
                mDismissCallbackRegistry.notifyDismissSucceeded();
                mKeyguardViewControllerLazy.get().hide(startTime, fadeoutDuration);

                // TODO(bc-animation): When remote animation is enabled for keyguard exit animation,
@@ -2165,16 +2208,24 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
                    });
                    anim.start();
                });
                resetKeyguardDonePendingLocked();
                mHideAnimationRun = false;
                adjustStatusBarLocked();
                sendUserPresentBroadcast();

                onKeyguardExitFinished();
            }
        }

        Trace.endSection();
    }

    private void onKeyguardExitFinished() {
        setShowingLocked(false);
        mWakeAndUnlocking = false;
        mDismissCallbackRegistry.notifyDismissSucceeded();
        resetKeyguardDonePendingLocked();
        mHideAnimationRun = false;
        adjustStatusBarLocked();
        sendUserPresentBroadcast();
    }

    /**
     * Whether we're currently animating between the keyguard and the app/launcher surface behind
     * it, or will be shortly (which happens if we started a fling to dismiss the keyguard).
@@ -2211,21 +2262,13 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
        // Block the panel from expanding, in case we were doing a swipe to dismiss gesture.
        mKeyguardViewControllerLazy.get().blockPanelExpansionFromCurrentTouch();
        final boolean wasShowing = mShowing;
        setShowingLocked(false);

        mWakeAndUnlocking = false;
        mDismissCallbackRegistry.notifyDismissSucceeded();
        onKeyguardExitFinished();

        if (mKeyguardStateController.isDismissingFromSwipe() || !wasShowing) {
            mKeyguardUnlockAnimationControllerLazy.get().hideKeyguardViewAfterRemoteAnimation();
        }

        finishSurfaceBehindRemoteAnimation();

        resetKeyguardDonePendingLocked();
        mHideAnimationRun = false;
        adjustStatusBarLocked();
        sendUserPresentBroadcast();
        mSurfaceBehindRemoteAnimationRequested = false;
        mKeyguardUnlockAnimationControllerLazy.get().notifyFinishedKeyguardExitAnimation();
    }
+6 −0
Original line number Diff line number Diff line
@@ -211,6 +211,12 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
        return super.onInterceptTouchEvent(ev);
    }

    /**
     * Called by the TouchHandler when this view is tapped. This will be called for actual taps
     * only, i.e. taps that have been filtered by the FalsingManager.
     */
    public void onTap() {}

    /** Sets the last action up time this view was touched. */
    void setLastActionUpTime(long eventTime) {
        mLastActionUpTime = eventTime;
+5 −1
Original line number Diff line number Diff line
@@ -118,7 +118,11 @@ public class ActivatableNotificationViewController

            if (ev.getAction() == MotionEvent.ACTION_UP) {
                // If this is a false tap, capture the even so it doesn't result in a click.
                return mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY);
                boolean falseTap = mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY);
                if (!falseTap && v instanceof ActivatableNotificationView) {
                    ((ActivatableNotificationView) v).onTap();
                }
                return falseTap;
            }
            return result;
        }
Loading