Merge "Polish the app reveal animation." into sc-dev

This commit is contained in:
Jordan Demeulenaere
2021-05-10 14:09:51 +00:00
committed by Android (Google) Code Review
4 changed files with 133 additions and 77 deletions

View File

@@ -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_duration">333</integer>
<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">266</integer>
</resources>

View File

@@ -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.start();
}
if (mShiftUpAnimation != null) {
mShiftUpAnimation.start();
}
mMainAnimator = createAnimator();
mMainAnimator.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) -> {
if (mVanishPaint.getShader() == null) {
return;
}
final float value = (float) animation.getAnimatedValue();
final float scale = (mFinishRadius - mInitRadius) * value + mInitRadius;
mVanishMatrix.setScale(scale, scale);
mVanishMatrix.postTranslate(mCircleCenter.x, mCircleCenter.y);
mVanishPaint.getShader().setLocalMatrix(mVanishMatrix);
postInvalidate();
});
mView.addView(this);
mVanishPaint.setAlpha(0);
}
void onAnimationProgress(float linearProgress) {
if (mVanishPaint.getShader() == null) {
return;
}
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();
}
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);
}
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();
ShiftUpAnimation(float fromYDelta, float toYDelta, View occludeHoleView) {
mFromYDelta = fromYDelta;
mToYDelta = toYDelta;
mOccludeHoleView = occludeHoleView;
mApplier = new SyncRtSurfaceTransactionApplier(occludeHoleView);
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
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
);
}
}

View File

@@ -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();
}
}

View File

@@ -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());
}