From 27726bf982412fa0f2e1e95bc8e123de440b6d17 Mon Sep 17 00:00:00 2001 From: Johannes Gallmann Date: Wed, 25 Oct 2023 17:25:23 +0200 Subject: Polish cross task back animation Bug: 259429314 Test: Manual, i.e. verifying cross task back animation on device Change-Id: I82ade4ea245672391d98f632c8ae1fb5c0149b59 --- libs/WindowManager/Shell/res/values/dimen.xml | 7 + .../android/wm/shell/animation/Interpolators.java | 2 + .../wm/shell/back/CrossTaskBackAnimation.java | 145 +++++++++------------ 3 files changed, 71 insertions(+), 83 deletions(-) (limited to 'libs') diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml index 1f6f7aeadd45..3ecdc8087013 100644 --- a/libs/WindowManager/Shell/res/values/dimen.xml +++ b/libs/WindowManager/Shell/res/values/dimen.xml @@ -473,4 +473,11 @@ 0.44 1.2 + + + 14dp + + 8dp + diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java index 2ec9e8b12fc6..880579d6a772 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java @@ -88,6 +88,8 @@ public class Interpolators { public static final PathInterpolator DIM_INTERPOLATOR = new PathInterpolator(.23f, .87f, .52f, -0.11f); + public static final Interpolator DECELERATE = new PathInterpolator(0f, 0f, 0.5f, 1f); + // Create the default emphasized interpolator private static PathInterpolator createEmphasizedInterpolator() { Path path = new Path(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java index fc5ff017ebe5..8ec297e1e79c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java @@ -18,7 +18,6 @@ package com.android.wm.shell.back; import static android.view.RemoteAnimationTarget.MODE_CLOSING; import static android.view.RemoteAnimationTarget.MODE_OPENING; -import static android.window.BackEvent.EDGE_RIGHT; import static com.android.internal.jank.InteractionJankMonitor.CUJ_PREDICTIVE_BACK_CROSS_TASK; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW; @@ -37,7 +36,7 @@ import android.view.IRemoteAnimationFinishedCallback; import android.view.IRemoteAnimationRunner; import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; -import android.view.animation.AccelerateDecelerateInterpolator; +import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; import android.window.BackEvent; import android.window.BackMotionEvent; @@ -46,6 +45,8 @@ import android.window.IOnBackInvokedCallback; import com.android.internal.policy.ScreenDecorationsUtils; import com.android.internal.protolog.common.ProtoLog; +import com.android.wm.shell.R; +import com.android.wm.shell.animation.Interpolators; import com.android.wm.shell.common.annotations.ShellMainThread; import javax.inject.Inject; @@ -68,32 +69,18 @@ public class CrossTaskBackAnimation extends ShellBackAnimation { private static final int BACKGROUNDCOLOR = 0x43433A; /** - * Minimum scale of the entering window. + * Minimum scale of the entering and closing window. */ - private static final float ENTERING_MIN_WINDOW_SCALE = 0.85f; + private static final float MIN_WINDOW_SCALE = 0.8f; - /** - * Minimum scale of the closing window. - */ - private static final float CLOSING_MIN_WINDOW_SCALE = 0.75f; - - /** - * Minimum color scale of the closing window. - */ - private static final float CLOSING_MIN_WINDOW_COLOR_SCALE = 0.1f; - - /** - * The margin between the entering window and the closing window - */ - private static final int WINDOW_MARGIN = 35; - - /** Max window translation in the Y axis. */ - private static final int WINDOW_MAX_DELTA_Y = 160; + /** Duration of post animation after gesture committed. */ + private static final int POST_ANIMATION_DURATION_MS = 500; private final Rect mStartTaskRect = new Rect(); private final float mCornerRadius; // The closing window properties. + private final Rect mClosingStartRect = new Rect(); private final RectF mClosingCurrentRect = new RectF(); // The entering window properties. @@ -101,44 +88,32 @@ public class CrossTaskBackAnimation extends ShellBackAnimation { private final RectF mEnteringCurrentRect = new RectF(); private final PointF mInitialTouchPos = new PointF(); - private final Interpolator mInterpolator = new AccelerateDecelerateInterpolator(); - + private final Interpolator mPostAnimationInterpolator = Interpolators.EMPHASIZED; + private final Interpolator mYMovementInterpolator = Interpolators.DECELERATE; + private final Interpolator mProgressInterpolator = new DecelerateInterpolator(); private final Matrix mTransformMatrix = new Matrix(); private final float[] mTmpFloat9 = new float[9]; - private final float[] mTmpTranslate = {0, 0, 0}; private final BackAnimationRunner mBackAnimationRunner; private final BackAnimationBackground mBackground; private final Context mContext; private RemoteAnimationTarget mEnteringTarget; private RemoteAnimationTarget mClosingTarget; - private SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction(); + private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction(); private boolean mBackInProgress = false; - private boolean mIsRightEdge; - private float mProgress = 0; - private PointF mTouchPos = new PointF(); + private final PointF mTouchPos = new PointF(); private IRemoteAnimationFinishedCallback mFinishCallback; - private BackProgressAnimator mProgressAnimator = new BackProgressAnimator(); + private final BackProgressAnimator mProgressAnimator = new BackProgressAnimator(); + private float mInterWindowMargin; + private float mVerticalMargin; @Inject public CrossTaskBackAnimation(Context context, BackAnimationBackground background) { - mContext = context; mCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context); mBackAnimationRunner = new BackAnimationRunner( new Callback(), new Runner(), context, CUJ_PREDICTIVE_BACK_CROSS_TASK); mBackground = background; - } - - private static void computeScaleTransformMatrix(float scale, float[] matrix) { - matrix[0] = scale; - matrix[1] = 0; - matrix[2] = 0; - matrix[3] = 0; - matrix[4] = scale; - matrix[5] = 0; - matrix[6] = 0; - matrix[7] = 0; - matrix[8] = scale; + mContext = context; } private static float mapRange(float value, float min, float max) { @@ -146,7 +121,7 @@ public class CrossTaskBackAnimation extends ShellBackAnimation { } private float getInterpolatedProgress(float backProgress) { - return 1 - (1 - backProgress) * (1 - backProgress) * (1 - backProgress); + return mProgressInterpolator.getInterpolation(backProgress); } private void startBackAnimation() { @@ -162,6 +137,10 @@ public class CrossTaskBackAnimation extends ShellBackAnimation { // Draw background. mBackground.ensureBackground(mClosingTarget.windowConfiguration.getBounds(), BACKGROUNDCOLOR, mTransaction); + mInterWindowMargin = mContext.getResources() + .getDimension(R.dimen.cross_task_back_inter_window_margin); + mVerticalMargin = mContext.getResources() + .getDimension(R.dimen.cross_task_back_vertical_margin); } private void updateGestureBackProgress(float progress, BackEvent event) { @@ -169,44 +148,38 @@ public class CrossTaskBackAnimation extends ShellBackAnimation { return; } - float touchX = event.getTouchX(); float touchY = event.getTouchY(); - float dX = Math.abs(touchX - mInitialTouchPos.x); // The 'follow width' is the width of the window if it completely matches // the gesture displacement. final int width = mStartTaskRect.width(); final int height = mStartTaskRect.height(); - // The 'progress width' is the width of the window if it strictly linearly interpolates - // to minimum scale base on progress. - float enteringScale = mapRange(progress, 1, ENTERING_MIN_WINDOW_SCALE); - float closingScale = mapRange(progress, 1, CLOSING_MIN_WINDOW_SCALE); - float closingColorScale = mapRange(progress, 1, CLOSING_MIN_WINDOW_COLOR_SCALE); - - // The final width is derived from interpolating between the follow with and progress width - // using gesture progress. - float enteringWidth = enteringScale * width; - float closingWidth = closingScale * width; - float enteringHeight = (float) height / width * enteringWidth; - float closingHeight = (float) height / width * closingWidth; + float scale = mapRange(progress, 1, MIN_WINDOW_SCALE); + float scaledWidth = scale * width; + float scaledHeight = scale * height; - float deltaYRatio = (touchY - mInitialTouchPos.y) / height; // Base the window movement in the Y axis on the touch movement in the Y axis. - float deltaY = (float) Math.sin(deltaYRatio * Math.PI * 0.5f) * WINDOW_MAX_DELTA_Y; + float rawYDelta = touchY - mInitialTouchPos.y; + float yDirection = rawYDelta < 0 ? -1 : 1; + // limit yDelta interpretation to 1/2 of screen height in either direction + float deltaYRatio = Math.min(height / 2f, Math.abs(rawYDelta)) / (height / 2f); + float interpolatedYRatio = mYMovementInterpolator.getInterpolation(deltaYRatio); + // limit y-shift so surface never passes 8dp screen margin + float deltaY = yDirection * interpolatedYRatio * Math.max(0f, + (height - scaledHeight) / 2f - mVerticalMargin); + // Move the window along the Y axis. - float closingTop = (height - closingHeight) * 0.5f + deltaY; - float enteringTop = (height - enteringHeight) * 0.5f + deltaY; + float scaledTop = (height - scaledHeight) * 0.5f + deltaY; // Move the window along the X axis. - float right = width - (progress * WINDOW_MARGIN); - float left = right - closingWidth; + float right = width - (progress * mVerticalMargin); + float left = right - scaledWidth; - mClosingCurrentRect.set(left, closingTop, right, closingTop + closingHeight); - mEnteringCurrentRect.set(left - enteringWidth - WINDOW_MARGIN, enteringTop, - left - WINDOW_MARGIN, enteringTop + enteringHeight); + mClosingCurrentRect.set(left, scaledTop, right, scaledTop + scaledHeight); + mEnteringCurrentRect.set(left - scaledWidth - mInterWindowMargin, scaledTop, + left - mInterWindowMargin, scaledTop + scaledHeight); applyTransform(mClosingTarget.leash, mClosingCurrentRect, mCornerRadius); - applyColorTransform(mClosingTarget.leash, closingColorScale); applyTransform(mEnteringTarget.leash, mEnteringCurrentRect, mCornerRadius); mTransaction.apply(); @@ -214,9 +187,21 @@ public class CrossTaskBackAnimation extends ShellBackAnimation { } private void updatePostCommitClosingAnimation(float progress) { + float targetLeft = + mStartTaskRect.left + (1 - MIN_WINDOW_SCALE) * mStartTaskRect.width() / 2; + float targetTop = + mStartTaskRect.top + (1 - MIN_WINDOW_SCALE) * mStartTaskRect.height() / 2; + float targetWidth = mStartTaskRect.width() * MIN_WINDOW_SCALE; + float targetHeight = mStartTaskRect.height() * MIN_WINDOW_SCALE; + + float left = mapRange(progress, mClosingStartRect.left, targetLeft); + float top = mapRange(progress, mClosingStartRect.top, targetTop); + float width = mapRange(progress, mClosingStartRect.width(), targetWidth); + float height = mapRange(progress, mClosingStartRect.height(), targetHeight); mTransaction.setLayer(mClosingTarget.leash, 0); - float alpha = mapRange(progress, 1, 0); - mTransaction.setAlpha(mClosingTarget.leash, alpha); + + mClosingCurrentRect.set(left, top, left + width, top + height); + applyTransform(mClosingTarget.leash, mClosingCurrentRect, mCornerRadius); } private void updatePostCommitEnteringAnimation(float progress) { @@ -244,14 +229,6 @@ public class CrossTaskBackAnimation extends ShellBackAnimation { .setCornerRadius(leash, cornerRadius); } - private void applyColorTransform(SurfaceControl leash, float colorScale) { - if (leash == null) { - return; - } - computeScaleTransformMatrix(colorScale, mTmpFloat9); - mTransaction.setColorTransform(leash, mTmpFloat9, mTmpTranslate); - } - private void finishAnimation() { if (mEnteringTarget != null) { mEnteringTarget.leash.release(); @@ -276,7 +253,8 @@ public class CrossTaskBackAnimation extends ShellBackAnimation { try { mFinishCallback.onAnimationFinished(); } catch (RemoteException e) { - e.printStackTrace(); + ProtoLog.e(WM_SHELL_BACK_PREVIEW, + "RemoteException when invoking onAnimationFinished callback"); } mFinishCallback = null; } @@ -285,12 +263,11 @@ public class CrossTaskBackAnimation extends ShellBackAnimation { private void onGestureProgress(@NonNull BackEvent backEvent) { if (!mBackInProgress) { mInitialTouchPos.set(backEvent.getTouchX(), backEvent.getTouchY()); - mIsRightEdge = backEvent.getSwipeEdge() == EDGE_RIGHT; mBackInProgress = true; } - mProgress = backEvent.getProgress(); + float progress = backEvent.getProgress(); mTouchPos.set(backEvent.getTouchX(), backEvent.getTouchY()); - updateGestureBackProgress(getInterpolatedProgress(mProgress), backEvent); + updateGestureBackProgress(getInterpolatedProgress(progress), backEvent); } private void onGestureCommitted() { @@ -302,9 +279,11 @@ public class CrossTaskBackAnimation extends ShellBackAnimation { // We enter phase 2 of the animation, the starting coordinates for phase 2 are the current // coordinate of the gesture driven phase. mEnteringCurrentRect.round(mEnteringStartRect); + mClosingCurrentRect.round(mClosingStartRect); - ValueAnimator valueAnimator = ValueAnimator.ofFloat(1f, 0f).setDuration(300); - valueAnimator.setInterpolator(mInterpolator); + ValueAnimator valueAnimator = + ValueAnimator.ofFloat(1f, 0f).setDuration(POST_ANIMATION_DURATION_MS); + valueAnimator.setInterpolator(mPostAnimationInterpolator); valueAnimator.addUpdateListener(animation -> { float progress = animation.getAnimatedFraction(); updatePostCommitEnteringAnimation(progress); -- cgit v1.2.3-59-g8ed1b