Merge "Polish the app reveal animation." into sc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
0cd1d01d63
@@ -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>
|
||||
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user