diff options
| author | 2022-10-12 11:53:21 +0000 | |
|---|---|---|
| committer | 2022-10-12 11:53:21 +0000 | |
| commit | c14774d7b3c423374d0ade358be0ddbb0c69660c (patch) | |
| tree | 41c482d2d72eb27b252c5999f9c660f82df7495f | |
| parent | 10c358b746e11cad2cf2fecedf2b7ba9fd67dac5 (diff) | |
| parent | 934606c0e8c0161b19e9672cf748fcec78fe1103 (diff) | |
Merge "Refactor SplashScreenAnimation so it can be reused in other parts of the code." into tm-qpr-dev am: 934606c0e8
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/19886504
Change-Id: I2a8cab7381118a00734363204338a149a68d2b4f
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
3 files changed, 365 insertions, 247 deletions
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp index 82573b2b9acc..f615ad6e671b 100644 --- a/libs/WindowManager/Shell/Android.bp +++ b/libs/WindowManager/Shell/Android.bp @@ -45,6 +45,9 @@ filegroup { "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 014f02bcf8b7..8bba44049c88 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 @@ import com.android.wm.shell.common.TransactionPool; */ 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 @@ public class SplashScreenExitAnimation implements Animator.AnimatorListener { 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 @@ public class SplashScreenExitAnimation implements Animator.AnimatorListener { } 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 @@ public class SplashScreenExitAnimation implements Animator.AnimatorListener { mFinishCallback = null; } } - if (mShiftUpAnimation != null) { - mShiftUpAnimation.finish(); - } } @Override @@ -342,40 +135,4 @@ public class SplashScreenExitAnimation implements Animator.AnimatorListener { 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 000000000000..3098e55ec78b --- /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); + } + } +} |