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

Commit 57331d48 authored by Jordan Demeulenaere's avatar Jordan Demeulenaere Committed by Automerger Merge Worker
Browse files

Merge "Polish the app reveal animation." into sc-dev am: 0cd1d01d

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/14206219

Change-Id: Icfc0a70691adb2d6e5fc9ebed8ce5973ee886182
parents 31b6a326 0cd1d01d
Loading
Loading
Loading
Loading
+7 −1
Original line number Diff line number Diff line
@@ -52,6 +52,12 @@
     when the PIP menu is shown in center. -->
    <string translatable="false" name="pip_menu_bounds">"596 280 1324 690"</string>

    <!-- Animation duration when exit starting window: fade out icon -->
    <integer name="starting_window_app_reveal_icon_fade_out_duration">133</integer>

    <!-- Animation duration when exit starting window: reveal app -->
    <integer name="starting_window_app_reveal_anim_delay">83</integer>

    <!-- Animation duration when exit starting window: reveal app -->
    <integer name="starting_window_app_reveal_anim_duration">333</integer>
    <integer name="starting_window_app_reveal_anim_duration">266</integer>
</resources>
+122 −66
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import static android.view.View.GONE;

import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.BlendMode;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -29,6 +30,7 @@ import android.graphics.Point;
import android.graphics.RadialGradient;
import android.graphics.Rect;
import android.graphics.Shader;
import android.util.MathUtils;
import android.util.Slog;
import android.view.Choreographer;
import android.view.SurfaceControl;
@@ -38,10 +40,10 @@ import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
import android.view.animation.Transformation;
import android.view.animation.TranslateYAnimation;
import android.window.SplashScreenView;

import com.android.wm.shell.R;
import com.android.wm.shell.animation.Interpolators;
import com.android.wm.shell.common.TransactionPool;

/**
@@ -53,52 +55,64 @@ public class SplashScreenExitAnimation implements Animator.AnimatorListener {
    private static final boolean DEBUG_EXIT_ANIMATION_BLEND = false;
    private static final String TAG = StartingSurfaceDrawer.TAG;

    private static final Interpolator APP_EXIT_INTERPOLATOR = new PathInterpolator(0f, 0f, 0f, 1f);
    private static final Interpolator ICON_INTERPOLATOR = new PathInterpolator(0.15f, 0f, 1f, 1f);
    private static final Interpolator MASK_RADIUS_INTERPOLATOR =
            new PathInterpolator(0f, 0f, 0.4f, 1f);
    private static final Interpolator SHIFT_UP_INTERPOLATOR = new PathInterpolator(0f, 0f, 0f, 1f);

    private final Matrix mTmpTransform = new Matrix();
    private final SurfaceControl mFirstWindowSurface;
    private final Rect mFirstWindowFrame = new Rect();
    private final SplashScreenView mSplashScreenView;
    private final int mMainWindowShiftLength;
    private final int mAppDuration;
    private final int mIconFadeOutDuration;
    private final int mAppRevealDelay;
    private final int mAppRevealDuration;
    private final int mAnimationDuration;
    private final float mIconStartAlpha;
    private final TransactionPool mTransactionPool;

    private ValueAnimator mMainAnimator;
    private ShiftUpAnimation mShiftUpAnimation;
    private RadialVanishAnimation mRadialVanishAnimation;
    private Runnable mFinishCallback;

    SplashScreenExitAnimation(SplashScreenView view, SurfaceControl leash, Rect frame,
            int appDuration, int mainWindowShiftLength, TransactionPool pool,
            Runnable handleFinish) {
    SplashScreenExitAnimation(Context context, SplashScreenView view, SurfaceControl leash,
            Rect frame, int mainWindowShiftLength, TransactionPool pool, Runnable handleFinish) {
        mSplashScreenView = view;
        mFirstWindowSurface = leash;
        if (frame != null) {
            mFirstWindowFrame.set(frame);
        }
        mAppDuration = appDuration;

        View iconView = view.getIconView();
        if (iconView == null) {
            mIconFadeOutDuration = 0;
            mIconStartAlpha = 0;
            mAppRevealDelay = 0;
        } else {
            iconView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
            mIconFadeOutDuration = context.getResources().getInteger(
                    R.integer.starting_window_app_reveal_icon_fade_out_duration);
            mAppRevealDelay = context.getResources().getInteger(
                    R.integer.starting_window_app_reveal_anim_delay);
            mIconStartAlpha = iconView.getAlpha();
        }
        mAppRevealDuration = context.getResources().getInteger(
                R.integer.starting_window_app_reveal_anim_duration);
        mAnimationDuration = Math.max(mIconFadeOutDuration, mAppRevealDelay + mAppRevealDuration);
        mMainWindowShiftLength = mainWindowShiftLength;
        mFinishCallback = handleFinish;
        mTransactionPool = pool;
    }

    void startAnimations() {
        prepareRevealAnimation();
        if (mMainAnimator != null) {
        mMainAnimator = createAnimator();
        mMainAnimator.start();
    }
        if (mShiftUpAnimation != null) {
            mShiftUpAnimation.start();
        }
    }

    // reveal splash screen, shift up main window
    private void prepareRevealAnimation() {
        // splash screen
        mMainAnimator = ValueAnimator.ofFloat(0f, 1f);
        mMainAnimator.setDuration(mAppDuration);
        mMainAnimator.setInterpolator(APP_EXIT_INTERPOLATOR);
        mMainAnimator.addListener(this);

    // fade out icon, reveal app, shift up main window
    private ValueAnimator createAnimator() {
        // reveal app
        final float transparentRatio = 0.8f;
        final int globalHeight = mSplashScreenView.getHeight();
        final int verticalCircleCenter = 0;
@@ -106,14 +120,13 @@ public class SplashScreenExitAnimation implements Animator.AnimatorListener {
        final int halfWidth = mSplashScreenView.getWidth() / 2;
        final int endRadius = (int) (0.5 + (1f / transparentRatio * (int)
                Math.sqrt(finalVerticalLength * finalVerticalLength + halfWidth * halfWidth)));
        final RadialVanishAnimation radialVanishAnimation = new RadialVanishAnimation(
                mSplashScreenView, mMainAnimator);
        radialVanishAnimation.setCircleCenter(halfWidth, verticalCircleCenter);
        radialVanishAnimation.setRadius(0/* initRadius */, endRadius);
        final int[] colors = {Color.TRANSPARENT, Color.TRANSPARENT, Color.WHITE};
        final int[] colors = {Color.WHITE, Color.WHITE, Color.TRANSPARENT};
        final float[] stops = {0f, transparentRatio, 1f};
        radialVanishAnimation.setRadialPaintParam(colors, stops);
        radialVanishAnimation.setReady();

        mRadialVanishAnimation = new RadialVanishAnimation(mSplashScreenView);
        mRadialVanishAnimation.setCircleCenter(halfWidth, verticalCircleCenter);
        mRadialVanishAnimation.setRadius(0 /* initRadius */, endRadius);
        mRadialVanishAnimation.setRadialPaintParam(colors, stops);

        if (mFirstWindowSurface != null && mFirstWindowSurface.isValid()) {
            // shift up main window
@@ -128,38 +141,47 @@ public class SplashScreenExitAnimation implements Animator.AnimatorListener {
            mSplashScreenView.addView(occludeHoleView, params);

            mShiftUpAnimation = new ShiftUpAnimation(0, -mMainWindowShiftLength, occludeHoleView);
            mShiftUpAnimation.setDuration(mAppDuration);
            mShiftUpAnimation.setInterpolator(APP_EXIT_INTERPOLATOR);

            occludeHoleView.setAnimation(mShiftUpAnimation);
        }

        ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
        animator.setDuration(mAnimationDuration);
        animator.setInterpolator(Interpolators.LINEAR);
        animator.addListener(this);
        animator.addUpdateListener(a -> onAnimationProgress((float) a.getAnimatedValue()));
        return animator;
    }

    private static class RadialVanishAnimation extends View {
        private final SplashScreenView mView;
        private int mInitRadius;
        private int mFinishRadius;
        private boolean mReady;

        private final Point mCircleCenter = new Point();
        private final Matrix mVanishMatrix = new Matrix();
        private final Paint mVanishPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

        RadialVanishAnimation(SplashScreenView target, ValueAnimator animator) {
        RadialVanishAnimation(SplashScreenView target) {
            super(target.getContext());
            mView = target;
            animator.addUpdateListener((animation) -> {
            mView.addView(this);
            mVanishPaint.setAlpha(0);
        }

        void onAnimationProgress(float linearProgress) {
            if (mVanishPaint.getShader() == null) {
                return;
            }
                final float value = (float) animation.getAnimatedValue();
                final float scale = (mFinishRadius - mInitRadius) * value + mInitRadius;

            final float radiusProgress = MASK_RADIUS_INTERPOLATOR.getInterpolation(linearProgress);
            final float alphaProgress = Interpolators.ALPHA_OUT.getInterpolation(linearProgress);
            final float scale = mInitRadius + (mFinishRadius - mInitRadius) * radiusProgress;

            mVanishMatrix.setScale(scale, scale);
            mVanishMatrix.postTranslate(mCircleCenter.x, mCircleCenter.y);
            mVanishPaint.getShader().setLocalMatrix(mVanishMatrix);
            mVanishPaint.setAlpha(Math.round(0xFF * alphaProgress));

            postInvalidate();
            });
            mView.addView(this);
        }

        void setRadius(int initRadius, int finishRadius) {
@@ -184,38 +206,44 @@ public class SplashScreenExitAnimation implements Animator.AnimatorListener {
                    new RadialGradient(0, 0, 1, colors, stops, Shader.TileMode.CLAMP);
            mVanishPaint.setShader(rShader);
            if (!DEBUG_EXIT_ANIMATION_BLEND) {
                mVanishPaint.setBlendMode(BlendMode.MODULATE);
            }
                // We blend the reveal gradient with the splash screen using DST_OUT so that the
                // splash screen is fully visible when radius = 0 (or gradient opacity is 0) and
                // fully invisible when radius = finishRadius AND gradient opacity is 1.
                mVanishPaint.setBlendMode(BlendMode.DST_OUT);
            }

        void setReady() {
            mReady = true;
        }

        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            if (mReady) {
            canvas.drawRect(0, 0, mView.getWidth(), mView.getHeight(), mVanishPaint);
        }
    }
    }

    private final class ShiftUpAnimation extends TranslateYAnimation {
        final SyncRtSurfaceTransactionApplier mApplier;
        ShiftUpAnimation(float fromYDelta, float toYDelta, View targetView) {
            super(fromYDelta, toYDelta);
            mApplier = new SyncRtSurfaceTransactionApplier(targetView);
        }
    private final class ShiftUpAnimation {
        private final float mFromYDelta;
        private final float mToYDelta;
        private final View mOccludeHoleView;
        private final SyncRtSurfaceTransactionApplier mApplier;
        private final Matrix mTmpTransform = new Matrix();

        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            super.applyTransformation(interpolatedTime, t);
        ShiftUpAnimation(float fromYDelta, float toYDelta, View occludeHoleView) {
            mFromYDelta = fromYDelta;
            mToYDelta = toYDelta;
            mOccludeHoleView = occludeHoleView;
            mApplier = new SyncRtSurfaceTransactionApplier(occludeHoleView);
        }

        void onAnimationProgress(float linearProgress) {
            if (mFirstWindowSurface == null || !mFirstWindowSurface.isValid()) {
                return;
            }
            mTmpTransform.set(t.getMatrix());

            final float progress = SHIFT_UP_INTERPOLATOR.getInterpolation(linearProgress);
            final float dy = mFromYDelta + (mToYDelta - mFromYDelta) * progress;

            mOccludeHoleView.setTranslationY(dy);
            mTmpTransform.setTranslate(0 /* dx */, dy);

            // set the vsyncId to ensure the transaction doesn't get applied too early.
            final SurfaceControl.Transaction tx = mTransactionPool.acquire();
@@ -290,4 +318,32 @@ public class SplashScreenExitAnimation implements Animator.AnimatorListener {
    public void onAnimationRepeat(Animator animation) {
        // ignore
    }

    private void onAnimationProgress(float linearProgress) {
        View iconView = mSplashScreenView.getIconView();
        if (iconView != null) {
            final float iconProgress = ICON_INTERPOLATOR.getInterpolation(
                    getProgress(linearProgress, 0 /* delay */, mIconFadeOutDuration));
            iconView.setAlpha(mIconStartAlpha * (1 - iconProgress));
        }

        final float revealLinearProgress = getProgress(linearProgress, mAppRevealDelay,
                mAppRevealDuration);

        if (mRadialVanishAnimation != null) {
            mRadialVanishAnimation.onAnimationProgress(revealLinearProgress);
        }

        if (mShiftUpAnimation != null) {
            mShiftUpAnimation.onAnimationProgress(revealLinearProgress);
        }
    }

    private float getProgress(float linearProgress, long delay, long duration) {
        return MathUtils.constrain(
                (linearProgress * (mAnimationDuration) - delay) / duration,
                0.0f,
                1.0f
        );
    }
}
+3 −6
Original line number Diff line number Diff line
@@ -74,16 +74,14 @@ public class SplashscreenContentDrawer {
    private int mDefaultIconSize;
    private int mBrandingImageWidth;
    private int mBrandingImageHeight;
    private final int mAppRevealDuration;
    private int mMainWindowShiftLength;
    private final TransactionPool mTransactionPool;
    private final SplashScreenWindowAttrs mTmpAttrs = new SplashScreenWindowAttrs();
    private final Handler mSplashscreenWorkerHandler;

    SplashscreenContentDrawer(Context context, int appRevealAnimDuration, TransactionPool pool) {
    SplashscreenContentDrawer(Context context, TransactionPool pool) {
        mContext = context;
        mIconProvider = new IconProvider(context);
        mAppRevealDuration = appRevealAnimDuration;
        mTransactionPool = pool;

        // Initialize Splashscreen worker thread
@@ -671,9 +669,8 @@ public class SplashscreenContentDrawer {
     */
    void applyExitAnimation(SplashScreenView view, SurfaceControl leash,
            Rect frame, Runnable finishCallback) {
        final SplashScreenExitAnimation animation = new SplashScreenExitAnimation(view, leash,
                frame, mAppRevealDuration, mMainWindowShiftLength, mTransactionPool,
                finishCallback);
        final SplashScreenExitAnimation animation = new SplashScreenExitAnimation(mContext, view,
                leash, frame, mMainWindowShiftLength, mTransactionPool, finishCallback);
        animation.startAnimations();
    }
}
+1 −4
Original line number Diff line number Diff line
@@ -116,10 +116,7 @@ public class StartingSurfaceDrawer {
        mContext = context;
        mDisplayManager = mContext.getSystemService(DisplayManager.class);
        mSplashScreenExecutor = splashScreenExecutor;
        final int appRevealAnimDuration = context.getResources().getInteger(
                com.android.wm.shell.R.integer.starting_window_app_reveal_anim_duration);
        mSplashscreenContentDrawer = new SplashscreenContentDrawer(mContext, appRevealAnimDuration,
                pool);
        mSplashscreenContentDrawer = new SplashscreenContentDrawer(mContext, pool);
        mSplashScreenExecutor.execute(() -> mChoreographer = Choreographer.getInstance());
    }