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