Refactor SplashScreenAnimation so it can be reused in other parts of the code.
Bug: 202826469
Test: manual
Change-Id: Ib2ce8b8b69bc3f9e4b3003978bac8b3884563678
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index 82573b2..f615ad6 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -45,6 +45,9 @@
"src/com/android/wm/shell/util/**/*.java",
"src/com/android/wm/shell/common/split/SplitScreenConstants.java",
"src/com/android/wm/shell/sysui/ShellSharedConstants.java",
+ "src/com/android/wm/shell/common/TransactionPool.java",
+ "src/com/android/wm/shell/animation/Interpolators.java",
+ "src/com/android/wm/shell/startingsurface/SplashScreenExitAnimationUtils.java",
],
path: "src",
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
index 014f02b..8bba4404 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
@@ -15,38 +15,20 @@
*/
package com.android.wm.shell.startingsurface;
-import static android.view.Choreographer.CALLBACK_COMMIT;
import static android.view.View.GONE;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_SPLASHSCREEN_EXIT_ANIM;
import android.animation.Animator;
-import android.animation.ValueAnimator;
import android.content.Context;
-import android.graphics.BlendMode;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-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;
-import android.view.SyncRtSurfaceTransactionApplier;
import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.view.animation.Interpolator;
-import android.view.animation.PathInterpolator;
import android.window.SplashScreenView;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.wm.shell.R;
-import com.android.wm.shell.animation.Interpolators;
import com.android.wm.shell.common.TransactionPool;
/**
@@ -55,14 +37,8 @@
*/
public class SplashScreenExitAnimation implements Animator.AnimatorListener {
private static final boolean DEBUG_EXIT_ANIMATION = false;
- private static final boolean DEBUG_EXIT_ANIMATION_BLEND = false;
private static final String TAG = StartingWindowController.TAG;
- 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 SurfaceControl mFirstWindowSurface;
private final Rect mFirstWindowFrame = new Rect();
private final SplashScreenView mSplashScreenView;
@@ -75,9 +51,6 @@
private final float mBrandingStartAlpha;
private final TransactionPool mTransactionPool;
- private ValueAnimator mMainAnimator;
- private ShiftUpAnimation mShiftUpAnimation;
- private RadialVanishAnimation mRadialVanishAnimation;
private Runnable mFinishCallback;
SplashScreenExitAnimation(Context context, SplashScreenView view, SurfaceControl leash,
@@ -121,187 +94,10 @@
}
void startAnimations() {
- mMainAnimator = createAnimator();
- mMainAnimator.start();
- }
-
- // 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;
- final int finalVerticalLength = globalHeight - verticalCircleCenter;
- final int halfWidth = mSplashScreenView.getWidth() / 2;
- final int endRadius = (int) (0.5 + (1f / transparentRatio * (int)
- Math.sqrt(finalVerticalLength * finalVerticalLength + halfWidth * halfWidth)));
- final int[] colors = {Color.WHITE, Color.WHITE, Color.TRANSPARENT};
- final float[] stops = {0f, transparentRatio, 1f};
-
- 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
- View occludeHoleView = new View(mSplashScreenView.getContext());
- if (DEBUG_EXIT_ANIMATION_BLEND) {
- occludeHoleView.setBackgroundColor(Color.BLUE);
- } else {
- occludeHoleView.setBackgroundColor(mSplashScreenView.getInitBackgroundColor());
- }
- final ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(
- WindowManager.LayoutParams.MATCH_PARENT, mMainWindowShiftLength);
- mSplashScreenView.addView(occludeHoleView, params);
-
- mShiftUpAnimation = new ShiftUpAnimation(0, -mMainWindowShiftLength, occludeHoleView);
- }
-
- 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 final Point mCircleCenter = new Point();
- private final Matrix mVanishMatrix = new Matrix();
- private final Paint mVanishPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
-
- RadialVanishAnimation(SplashScreenView target) {
- super(target.getContext());
- mView = target;
- 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) {
- if (DEBUG_EXIT_ANIMATION) {
- Slog.v(TAG, "RadialVanishAnimation setRadius init: " + initRadius
- + " final " + finishRadius);
- }
- mInitRadius = initRadius;
- mFinishRadius = finishRadius;
- }
-
- void setCircleCenter(int x, int y) {
- if (DEBUG_EXIT_ANIMATION) {
- Slog.v(TAG, "RadialVanishAnimation setCircleCenter x: " + x + " y " + y);
- }
- mCircleCenter.set(x, y);
- }
-
- void setRadialPaintParam(int[] colors, float[] stops) {
- // setup gradient shader
- final RadialGradient rShader =
- new RadialGradient(0, 0, 1, colors, stops, Shader.TileMode.CLAMP);
- mVanishPaint.setShader(rShader);
- if (!DEBUG_EXIT_ANIMATION_BLEND) {
- // 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);
- }
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- canvas.drawRect(0, 0, mView.getWidth(), mView.getHeight(), mVanishPaint);
- }
- }
-
- 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);
- }
-
- void onAnimationProgress(float linearProgress) {
- if (mFirstWindowSurface == null || !mFirstWindowSurface.isValid()
- || !mSplashScreenView.isAttachedToWindow()) {
- return;
- }
-
- 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();
- tx.setFrameTimelineVsync(Choreographer.getSfInstance().getVsyncId());
- mTmpTransform.postTranslate(mFirstWindowFrame.left,
- mFirstWindowFrame.top + mMainWindowShiftLength);
-
- SyncRtSurfaceTransactionApplier.SurfaceParams
- params = new SyncRtSurfaceTransactionApplier.SurfaceParams
- .Builder(mFirstWindowSurface)
- .withMatrix(mTmpTransform)
- .withMergeTransaction(tx)
- .build();
- mApplier.scheduleApply(params);
-
- mTransactionPool.release(tx);
- }
-
- void finish() {
- if (mFirstWindowSurface == null || !mFirstWindowSurface.isValid()) {
- return;
- }
- final SurfaceControl.Transaction tx = mTransactionPool.acquire();
- if (mSplashScreenView.isAttachedToWindow()) {
- tx.setFrameTimelineVsync(Choreographer.getSfInstance().getVsyncId());
-
- SyncRtSurfaceTransactionApplier.SurfaceParams
- params = new SyncRtSurfaceTransactionApplier.SurfaceParams
- .Builder(mFirstWindowSurface)
- .withWindowCrop(null)
- .withMergeTransaction(tx)
- .build();
- mApplier.scheduleApply(params);
- } else {
- tx.setWindowCrop(mFirstWindowSurface, null);
- tx.apply();
- }
- mTransactionPool.release(tx);
-
- Choreographer.getSfInstance().postCallback(CALLBACK_COMMIT,
- mFirstWindowSurface::release, null);
- }
+ SplashScreenExitAnimationUtils.startAnimations(mSplashScreenView, mFirstWindowSurface,
+ mMainWindowShiftLength, mTransactionPool, mFirstWindowFrame, mAnimationDuration,
+ mIconFadeOutDuration, mIconStartAlpha, mBrandingStartAlpha, mAppRevealDelay,
+ mAppRevealDuration, this);
}
private void reset() {
@@ -316,9 +112,6 @@
mFinishCallback = null;
}
}
- if (mShiftUpAnimation != null) {
- mShiftUpAnimation.finish();
- }
}
@Override
@@ -342,40 +135,4 @@
public void onAnimationRepeat(Animator animation) {
// ignore
}
-
- private void onFadeOutProgress(float linearProgress) {
- final float iconProgress = ICON_INTERPOLATOR.getInterpolation(
- getProgress(linearProgress, 0 /* delay */, mIconFadeOutDuration));
- final View iconView = mSplashScreenView.getIconView();
- final View brandingView = mSplashScreenView.getBrandingView();
- if (iconView != null) {
- iconView.setAlpha(mIconStartAlpha * (1 - iconProgress));
- }
- if (brandingView != null) {
- brandingView.setAlpha(mBrandingStartAlpha * (1 - iconProgress));
- }
- }
-
- private void onAnimationProgress(float linearProgress) {
- onFadeOutProgress(linearProgress);
-
- 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
- );
- }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimationUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimationUtils.java
new file mode 100644
index 0000000..3098e55
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimationUtils.java
@@ -0,0 +1,358 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.startingsurface;
+
+import static android.view.Choreographer.CALLBACK_COMMIT;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.BlendMode;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+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;
+import android.view.SyncRtSurfaceTransactionApplier;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
+import android.window.SplashScreenView;
+
+import com.android.wm.shell.animation.Interpolators;
+import com.android.wm.shell.common.TransactionPool;
+
+/**
+ * Utilities for creating the splash screen window animations.
+ * @hide
+ */
+public class SplashScreenExitAnimationUtils {
+ private static final boolean DEBUG_EXIT_ANIMATION = false;
+ private static final boolean DEBUG_EXIT_ANIMATION_BLEND = false;
+ private static final String TAG = "SplashScreenExitAnimationUtils";
+
+ 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);
+
+ /**
+ * Creates and starts the animator to fade out the icon, reveal the app, and shift up main
+ * window.
+ * @hide
+ */
+ public static void startAnimations(ViewGroup splashScreenView,
+ SurfaceControl firstWindowSurface, int mainWindowShiftLength,
+ TransactionPool transactionPool, Rect firstWindowFrame, int animationDuration,
+ int iconFadeOutDuration, float iconStartAlpha, float brandingStartAlpha,
+ int appRevealDelay, int appRevealDuration, Animator.AnimatorListener animatorListener) {
+ ValueAnimator animator =
+ createAnimator(splashScreenView, firstWindowSurface, mainWindowShiftLength,
+ transactionPool, firstWindowFrame, animationDuration, iconFadeOutDuration,
+ iconStartAlpha, brandingStartAlpha, appRevealDelay, appRevealDuration,
+ animatorListener);
+ animator.start();
+ }
+
+ /**
+ * Creates the animator to fade out the icon, reveal the app, and shift up main window.
+ * @hide
+ */
+ private static ValueAnimator createAnimator(ViewGroup splashScreenView,
+ SurfaceControl firstWindowSurface, int mMainWindowShiftLength,
+ TransactionPool transactionPool, Rect firstWindowFrame, int animationDuration,
+ int iconFadeOutDuration, float iconStartAlpha, float brandingStartAlpha,
+ int appRevealDelay, int appRevealDuration, Animator.AnimatorListener animatorListener) {
+ // reveal app
+ final float transparentRatio = 0.8f;
+ final int globalHeight = splashScreenView.getHeight();
+ final int verticalCircleCenter = 0;
+ final int finalVerticalLength = globalHeight - verticalCircleCenter;
+ final int halfWidth = splashScreenView.getWidth() / 2;
+ final int endRadius = (int) (0.5 + (1f / transparentRatio * (int)
+ Math.sqrt(finalVerticalLength * finalVerticalLength + halfWidth * halfWidth)));
+ final int[] colors = {Color.WHITE, Color.WHITE, Color.TRANSPARENT};
+ final float[] stops = {0f, transparentRatio, 1f};
+
+ RadialVanishAnimation radialVanishAnimation = new RadialVanishAnimation(splashScreenView);
+ radialVanishAnimation.setCircleCenter(halfWidth, verticalCircleCenter);
+ radialVanishAnimation.setRadius(0 /* initRadius */, endRadius);
+ radialVanishAnimation.setRadialPaintParam(colors, stops);
+
+ View occludeHoleView = null;
+ ShiftUpAnimation shiftUpAnimation = null;
+ if (firstWindowSurface != null && firstWindowSurface.isValid()) {
+ // shift up main window
+ occludeHoleView = new View(splashScreenView.getContext());
+ if (DEBUG_EXIT_ANIMATION_BLEND) {
+ occludeHoleView.setBackgroundColor(Color.BLUE);
+ } else if (splashScreenView instanceof SplashScreenView) {
+ occludeHoleView.setBackgroundColor(
+ ((SplashScreenView) splashScreenView).getInitBackgroundColor());
+ } else {
+ occludeHoleView.setBackgroundColor(
+ isDarkTheme(splashScreenView.getContext()) ? Color.BLACK : Color.WHITE);
+ }
+ final ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(
+ WindowManager.LayoutParams.MATCH_PARENT, mMainWindowShiftLength);
+ splashScreenView.addView(occludeHoleView, params);
+
+ shiftUpAnimation = new ShiftUpAnimation(0, -mMainWindowShiftLength, occludeHoleView,
+ firstWindowSurface, splashScreenView, transactionPool, firstWindowFrame,
+ mMainWindowShiftLength);
+ }
+
+ ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
+ animator.setDuration(animationDuration);
+ animator.setInterpolator(Interpolators.LINEAR);
+ if (animatorListener != null) {
+ animator.addListener(animatorListener);
+ }
+ View finalOccludeHoleView = occludeHoleView;
+ ShiftUpAnimation finalShiftUpAnimation = shiftUpAnimation;
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ if (finalShiftUpAnimation != null) {
+ finalShiftUpAnimation.finish();
+ }
+ splashScreenView.removeView(radialVanishAnimation);
+ splashScreenView.removeView(finalOccludeHoleView);
+ }
+ });
+ animator.addUpdateListener(animation -> {
+ float linearProgress = (float) animation.getAnimatedValue();
+
+ // Fade out progress
+ final float iconProgress =
+ ICON_INTERPOLATOR.getInterpolation(getProgress(
+ linearProgress, 0 /* delay */, iconFadeOutDuration, animationDuration));
+ View iconView = null;
+ View brandingView = null;
+ if (splashScreenView instanceof SplashScreenView) {
+ iconView = ((SplashScreenView) splashScreenView).getIconView();
+ brandingView = ((SplashScreenView) splashScreenView).getBrandingView();
+ }
+ if (iconView != null) {
+ iconView.setAlpha(iconStartAlpha * (1 - iconProgress));
+ }
+ if (brandingView != null) {
+ brandingView.setAlpha(brandingStartAlpha * (1 - iconProgress));
+ }
+
+ final float revealLinearProgress = getProgress(linearProgress, appRevealDelay,
+ appRevealDuration, animationDuration);
+
+ radialVanishAnimation.onAnimationProgress(revealLinearProgress);
+
+ if (finalShiftUpAnimation != null) {
+ finalShiftUpAnimation.onAnimationProgress(revealLinearProgress);
+ }
+ });
+ return animator;
+ }
+
+ private static float getProgress(float linearProgress, long delay, long duration,
+ int animationDuration) {
+ return MathUtils.constrain(
+ (linearProgress * (animationDuration) - delay) / duration,
+ 0.0f,
+ 1.0f
+ );
+ }
+
+ private static boolean isDarkTheme(Context context) {
+ Configuration configuration = context.getResources().getConfiguration();
+ int nightMode = configuration.uiMode & Configuration.UI_MODE_NIGHT_MASK;
+ return nightMode == Configuration.UI_MODE_NIGHT_YES;
+ }
+
+ /**
+ * View which creates a circular reveal of the underlying view.
+ * @hide
+ */
+ @SuppressLint("ViewConstructor")
+ public static class RadialVanishAnimation extends View {
+ private final ViewGroup mView;
+ private int mInitRadius;
+ private int mFinishRadius;
+
+ private final Point mCircleCenter = new Point();
+ private final Matrix mVanishMatrix = new Matrix();
+ private final Paint mVanishPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+
+ public RadialVanishAnimation(ViewGroup target) {
+ super(target.getContext());
+ mView = target;
+ mView.addView(this);
+ if (getLayoutParams() instanceof ViewGroup.MarginLayoutParams) {
+ ((ViewGroup.MarginLayoutParams) getLayoutParams()).setMargins(0, 0, 0, 0);
+ }
+ 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) {
+ if (DEBUG_EXIT_ANIMATION) {
+ Slog.v(TAG, "RadialVanishAnimation setRadius init: " + initRadius
+ + " final " + finishRadius);
+ }
+ mInitRadius = initRadius;
+ mFinishRadius = finishRadius;
+ }
+
+ void setCircleCenter(int x, int y) {
+ if (DEBUG_EXIT_ANIMATION) {
+ Slog.v(TAG, "RadialVanishAnimation setCircleCenter x: " + x + " y " + y);
+ }
+ mCircleCenter.set(x, y);
+ }
+
+ void setRadialPaintParam(int[] colors, float[] stops) {
+ // setup gradient shader
+ final RadialGradient rShader =
+ new RadialGradient(0, 0, 1, colors, stops, Shader.TileMode.CLAMP);
+ mVanishPaint.setShader(rShader);
+ if (!DEBUG_EXIT_ANIMATION_BLEND) {
+ // 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);
+ }
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ canvas.drawRect(0, 0, mView.getWidth(), mView.getHeight(), mVanishPaint);
+ }
+ }
+
+ /**
+ * Shifts up the main window.
+ * @hide
+ */
+ public static 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();
+ private final SurfaceControl mFirstWindowSurface;
+ private final ViewGroup mSplashScreenView;
+ private final TransactionPool mTransactionPool;
+ private final Rect mFirstWindowFrame;
+ private final int mMainWindowShiftLength;
+
+ public ShiftUpAnimation(float fromYDelta, float toYDelta, View occludeHoleView,
+ SurfaceControl firstWindowSurface, ViewGroup splashScreenView,
+ TransactionPool transactionPool, Rect firstWindowFrame,
+ int mainWindowShiftLength) {
+ mFromYDelta = fromYDelta;
+ mToYDelta = toYDelta;
+ mOccludeHoleView = occludeHoleView;
+ mApplier = new SyncRtSurfaceTransactionApplier(occludeHoleView);
+ mFirstWindowSurface = firstWindowSurface;
+ mSplashScreenView = splashScreenView;
+ mTransactionPool = transactionPool;
+ mFirstWindowFrame = firstWindowFrame;
+ mMainWindowShiftLength = mainWindowShiftLength;
+ }
+
+ void onAnimationProgress(float linearProgress) {
+ if (mFirstWindowSurface == null || !mFirstWindowSurface.isValid()
+ || !mSplashScreenView.isAttachedToWindow()) {
+ return;
+ }
+
+ 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();
+ tx.setFrameTimelineVsync(Choreographer.getSfInstance().getVsyncId());
+ mTmpTransform.postTranslate(mFirstWindowFrame.left,
+ mFirstWindowFrame.top + mMainWindowShiftLength);
+
+ SyncRtSurfaceTransactionApplier.SurfaceParams
+ params = new SyncRtSurfaceTransactionApplier.SurfaceParams
+ .Builder(mFirstWindowSurface)
+ .withMatrix(mTmpTransform)
+ .withMergeTransaction(tx)
+ .build();
+ mApplier.scheduleApply(params);
+
+ mTransactionPool.release(tx);
+ }
+
+ void finish() {
+ if (mFirstWindowSurface == null || !mFirstWindowSurface.isValid()) {
+ return;
+ }
+ final SurfaceControl.Transaction tx = mTransactionPool.acquire();
+ if (mSplashScreenView.isAttachedToWindow()) {
+ tx.setFrameTimelineVsync(Choreographer.getSfInstance().getVsyncId());
+
+ SyncRtSurfaceTransactionApplier.SurfaceParams
+ params = new SyncRtSurfaceTransactionApplier.SurfaceParams
+ .Builder(mFirstWindowSurface)
+ .withWindowCrop(null)
+ .withMergeTransaction(tx)
+ .build();
+ mApplier.scheduleApply(params);
+ } else {
+ tx.setWindowCrop(mFirstWindowSurface, null);
+ tx.apply();
+ }
+ mTransactionPool.release(tx);
+
+ Choreographer.getSfInstance().postCallback(CALLBACK_COMMIT,
+ mFirstWindowSurface::release, null);
+ }
+ }
+}