Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4ce2b40f authored by Shan Huang's avatar Shan Huang
Browse files

Polish the back to home gesture animation to match the spec.

Bug:228533817
Test: Enable back animation from FlagFlipper (Predicitve Back Animation)
and swipe back on Flag Flipper. Try in both portrait and landscape mode.

Change-Id: I7792dc5079698c1b5a7dd3cd06344ba46c4a2da6
parent d34d3b48
Loading
Loading
Loading
Loading
+5 −2
Original line number Original line Diff line number Diff line
@@ -244,8 +244,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
        mDragLayerAlpha = mDragLayer.getAlphaProperty(ALPHA_INDEX_TRANSITIONS);
        mDragLayerAlpha = mDragLayer.getAlphaProperty(ALPHA_INDEX_TRANSITIONS);
        mHandler = new Handler(Looper.getMainLooper());
        mHandler = new Handler(Looper.getMainLooper());
        mDeviceProfile = mLauncher.getDeviceProfile();
        mDeviceProfile = mLauncher.getDeviceProfile();
        mBackAnimationController = new LauncherBackAnimationController(
        mBackAnimationController = new LauncherBackAnimationController(mLauncher, this);
                mDeviceProfile, mLauncher, this);


        Resources res = mLauncher.getResources();
        Resources res = mLauncher.getResources();
        mContentScale = res.getFloat(R.dimen.content_scale);
        mContentScale = res.getFloat(R.dimen.content_scale);
@@ -1441,6 +1440,10 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
                }
                }
            };
            };
            anim.addOnUpdateListener(runner);
            anim.addOnUpdateListener(runner);
        } else {
            // If no floating icon or widget is present, animate the to the default window
            // target rect.
            anim.addOnUpdateListener(new SpringAnimRunner(targets, targetRect, windowTargetBounds));
        }
        }


        // Use a fixed velocity to start the animation.
        // Use a fixed velocity to start the animation.
+77 −49
Original line number Original line Diff line number Diff line
@@ -25,6 +25,7 @@ import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator;
import android.graphics.Matrix;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.RectF;
import android.os.Handler;
import android.os.Handler;
@@ -33,11 +34,12 @@ import android.util.MathUtils;
import android.util.Pair;
import android.util.Pair;
import android.view.RemoteAnimationTarget;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.view.SurfaceControl;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.window.BackEvent;
import android.window.BackEvent;
import android.window.IOnBackInvokedCallback;
import android.window.IOnBackInvokedCallback;


import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.QuickstepTransitionManager;
import com.android.launcher3.QuickstepTransitionManager;
import com.android.launcher3.R;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.Utilities;
@@ -62,34 +64,36 @@ import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
 *
 *
 */
 */
public class LauncherBackAnimationController {
public class LauncherBackAnimationController {
    private static final int CANCEL_TRANSITION_DURATION = 150;
    private static final int CANCEL_TRANSITION_DURATION = 233;
    private static final float MIN_WINDOW_SCALE = 0.7f;
    private static final String TAG = "LauncherBackAnimationController";
    private static final String TAG = "LauncherBackAnimationController";
    private final DeviceProfile mDeviceProfile;
    private final QuickstepTransitionManager mQuickstepTransitionManager;
    private final QuickstepTransitionManager mQuickstepTransitionManager;
    private final Matrix mTransformMatrix = new Matrix();
    private final Matrix mTransformMatrix = new Matrix();
    private final RectF mTargetRectF = new RectF();
    /** The window position at the beginning of the back animation. */
    private final RectF mStartRectF = new RectF();
    private final Rect mStartRect = new Rect();
    /** The window position when the back gesture is cancelled. */
    private final RectF mCancelRect = new RectF();
    /** The current window position. */
    private final RectF mCurrentRect = new RectF();
    private final RectF mCurrentRect = new RectF();
    private final BaseQuickstepLauncher mLauncher;
    private final BaseQuickstepLauncher mLauncher;
    private final int mWindowScaleMarginX;
    private final int mWindowScaleMarginX;
    private final int mWindowScaleMarginY;
    /** Max window translation in the Y axis. */
    private final int mWindowMaxDeltaY;
    private final float mWindowScaleEndCornerRadius;
    private final float mWindowScaleEndCornerRadius;
    private final float mWindowScaleStartCornerRadius;
    private final float mWindowScaleStartCornerRadius;
    private final Interpolator mCancelInterpolator;
    private final PointF mInitialTouchPos = new PointF();


    private RemoteAnimationTargetCompat mBackTarget;
    private RemoteAnimationTargetCompat mBackTarget;
    private SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
    private SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
    private boolean mSpringAnimationInProgress = false;
    private boolean mSpringAnimationInProgress = false;
    private boolean mAnimatorSetInProgress = false;
    private boolean mAnimatorSetInProgress = false;
    @BackEvent.SwipeEdge
    private int mSwipeEdge;
    private float mBackProgress = 0;
    private float mBackProgress = 0;
    private boolean mBackInProgress = false;
    private boolean mBackInProgress = false;


    public LauncherBackAnimationController(
    public LauncherBackAnimationController(
            DeviceProfile deviceProfile,
            BaseQuickstepLauncher launcher,
            BaseQuickstepLauncher launcher,
            QuickstepTransitionManager quickstepTransitionManager) {
            QuickstepTransitionManager quickstepTransitionManager) {
        mDeviceProfile = deviceProfile;
        mLauncher = launcher;
        mLauncher = launcher;
        mQuickstepTransitionManager = quickstepTransitionManager;
        mQuickstepTransitionManager = quickstepTransitionManager;
        mWindowScaleEndCornerRadius = QuickStepContract.supportsRoundedCornersOnWindows(
        mWindowScaleEndCornerRadius = QuickStepContract.supportsRoundedCornersOnWindows(
@@ -100,8 +104,10 @@ public class LauncherBackAnimationController {
        mWindowScaleStartCornerRadius = QuickStepContract.getWindowCornerRadius(mLauncher);
        mWindowScaleStartCornerRadius = QuickStepContract.getWindowCornerRadius(mLauncher);
        mWindowScaleMarginX = mLauncher.getResources().getDimensionPixelSize(
        mWindowScaleMarginX = mLauncher.getResources().getDimensionPixelSize(
                R.dimen.swipe_back_window_scale_x_margin);
                R.dimen.swipe_back_window_scale_x_margin);
        mWindowScaleMarginY = mLauncher.getResources().getDimensionPixelSize(
        mWindowMaxDeltaY = mLauncher.getResources().getDimensionPixelSize(
                R.dimen.swipe_back_window_scale_y_margin);
                R.dimen.swipe_back_window_max_delta_y);
        mCancelInterpolator =
                AnimationUtils.loadInterpolator(mLauncher, R.interpolator.back_cancel);
    }
    }


    /**
    /**
@@ -136,7 +142,7 @@ public class LauncherBackAnimationController {
                        if (!mBackInProgress) {
                        if (!mBackInProgress) {
                            startBack(backEvent);
                            startBack(backEvent);
                        } else {
                        } else {
                            updateBackProgress(mBackProgress);
                            updateBackProgress(mBackProgress, backEvent);
                        }
                        }
                    }
                    }


@@ -145,11 +151,13 @@ public class LauncherBackAnimationController {
    }
    }


    private void resetPositionAnimated() {
    private void resetPositionAnimated() {
        ValueAnimator cancelAnimator = ValueAnimator.ofFloat(mBackProgress, 0);
        ValueAnimator cancelAnimator = ValueAnimator.ofFloat(0, 1);
        mCancelRect.set(mCurrentRect);
        cancelAnimator.setDuration(CANCEL_TRANSITION_DURATION);
        cancelAnimator.setDuration(CANCEL_TRANSITION_DURATION);
        cancelAnimator.setInterpolator(mCancelInterpolator);
        cancelAnimator.addUpdateListener(
        cancelAnimator.addUpdateListener(
                animation -> {
                animation -> {
                    updateBackProgress((float) animation.getAnimatedValue());
                    updateCancelProgress((float) animation.getAnimatedValue());
                });
                });
        cancelAnimator.addListener(new AnimatorListenerAdapter() {
        cancelAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            @Override
@@ -179,50 +187,70 @@ public class LauncherBackAnimationController {
        mTransaction.show(appTarget.leash).apply();
        mTransaction.show(appTarget.leash).apply();
        mTransaction.setAnimationTransaction();
        mTransaction.setAnimationTransaction();
        mBackTarget = new RemoteAnimationTargetCompat(appTarget);
        mBackTarget = new RemoteAnimationTargetCompat(appTarget);
        mSwipeEdge = backEvent.getSwipeEdge();
        mInitialTouchPos.set(backEvent.getTouchX(), backEvent.getTouchY());
        float screenWidth = mDeviceProfile.widthPx;

        float screenHeight = mDeviceProfile.heightPx;
        float targetHeight = screenHeight - 2 * mWindowScaleMarginY;
        float targetWidth = targetHeight * screenWidth / screenHeight;
        float left;
        if (mSwipeEdge == BackEvent.EDGE_LEFT) {
            left = screenWidth - targetWidth - mWindowScaleMarginX;
        } else {
            left = mWindowScaleMarginX;
        }
        float top = mWindowScaleMarginY;
        // TODO(b/218916755): Offset start rectangle in multiwindow mode.
        // TODO(b/218916755): Offset start rectangle in multiwindow mode.
        mStartRectF.set(0, 0, screenWidth, screenHeight);
        mStartRect.set(mBackTarget.windowConfiguration.getMaxBounds());
        mTargetRectF.set(left, top, targetWidth + left, targetHeight + top);
    }
    }


    private void updateBackProgress(float progress) {
    private void updateBackProgress(float progress, BackEvent event) {
        if (mBackTarget == null) {
        if (mBackTarget == null) {
            return;
            return;
        }
        }
        float screenWidth = mStartRect.width();
        float screenHeight = mStartRect.height();
        float dX = Math.abs(event.getTouchX() - mInitialTouchPos.x);
        // The 'follow width' is the width of the window if it completely matches
        // the gesture displacement.
        float followWidth = screenWidth - dX;
        // The 'progress width' is the width of the window if it strictly linearly interpolates
        // to minimum scale base on progress.
        float progressWidth = MathUtils.lerp(1, MIN_WINDOW_SCALE, progress) * screenWidth;
        // The final width is derived from interpolating between the follow with and progress width
        // using gesture progress.
        float width = MathUtils.lerp(followWidth, progressWidth, progress);
        float height = screenHeight / screenWidth * width;
        float deltaYRatio = (event.getTouchY() - mInitialTouchPos.y) / screenHeight;
        // 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) * mWindowMaxDeltaY;
        // Move the window along the Y axis.
        float top = (screenHeight - height) * 0.5f + deltaY;
        // Move the window along the X axis.
        float left = event.getSwipeEdge() == BackEvent.EDGE_RIGHT
                ? progress * mWindowScaleMarginX
                : screenWidth - progress * mWindowScaleMarginX - width;


        mCurrentRect.set(left, top, left + width, top + height);
        float cornerRadius = Utilities.mapRange(
                progress, mWindowScaleStartCornerRadius, mWindowScaleEndCornerRadius);
        applyTransform(mCurrentRect, cornerRadius);
    }

    private void updateCancelProgress(float progress) {
        if (mBackTarget == null) {
            return;
        }
        mCurrentRect.set(
        mCurrentRect.set(
                MathUtils.lerp(mStartRectF.left, mTargetRectF.left, progress),
                MathUtils.lerp(mCancelRect.left, mStartRect.left, progress),
                MathUtils.lerp(mStartRectF.top, mTargetRectF.top, progress),
                MathUtils.lerp(mCancelRect.top, mStartRect.top, progress),
                MathUtils.lerp(mStartRectF.right, mTargetRectF.right, progress),
                MathUtils.lerp(mCancelRect.right, mStartRect.right, progress),
                MathUtils.lerp(mStartRectF.bottom, mTargetRectF.bottom, progress));
                MathUtils.lerp(mCancelRect.bottom, mStartRect.bottom, progress));
        SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder builder =
                new SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder(mBackTarget.leash);


        Rect currentRect = new Rect();
        float cornerRadius = Utilities.mapRange(
        mCurrentRect.round(currentRect);
                progress, mWindowScaleEndCornerRadius, mWindowScaleStartCornerRadius);
        applyTransform(mCurrentRect, cornerRadius);
    }


        // Scale the target window to match the currentRectF.
    /** Transform the target window to match the target rect. */
        final float scale = mCurrentRect.width() / mStartRectF.width();
    private void applyTransform(RectF targetRect, float cornerRadius) {
        SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder builder =
                new SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder(mBackTarget.leash);
        final float scale = targetRect.width() / mStartRect.width();
        mTransformMatrix.reset();
        mTransformMatrix.reset();
        mTransformMatrix.setScale(scale, scale);
        mTransformMatrix.setScale(scale, scale);
        mTransformMatrix.postTranslate(mCurrentRect.left, mCurrentRect.top);
        mTransformMatrix.postTranslate(targetRect.left, targetRect.top);
        Rect startRect = new Rect();
        mStartRectF.round(startRect);
        float cornerRadius = Utilities.mapRange(
                progress, mWindowScaleStartCornerRadius, mWindowScaleEndCornerRadius);
        builder.withMatrix(mTransformMatrix)
        builder.withMatrix(mTransformMatrix)
                .withWindowCrop(startRect)
                .withWindowCrop(mStartRect)
                .withCornerRadius(cornerRadius);
                .withCornerRadius(cornerRadius);
        SyncRtSurfaceTransactionApplierCompat.SurfaceParams surfaceParams = builder.build();
        SyncRtSurfaceTransactionApplierCompat.SurfaceParams surfaceParams = builder.build();


@@ -263,11 +291,11 @@ public class LauncherBackAnimationController {
        mBackTarget = null;
        mBackTarget = null;
        mBackInProgress = false;
        mBackInProgress = false;
        mBackProgress = 0;
        mBackProgress = 0;
        mSwipeEdge = BackEvent.EDGE_LEFT;
        mTransformMatrix.reset();
        mTransformMatrix.reset();
        mTargetRectF.setEmpty();
        mCancelRect.setEmpty();
        mCurrentRect.setEmpty();
        mCurrentRect.setEmpty();
        mStartRectF.setEmpty();
        mStartRect.setEmpty();
        mInitialTouchPos.set(0, 0);
        mAnimatorSetInProgress = false;
        mAnimatorSetInProgress = false;
        mSpringAnimationInProgress = false;
        mSpringAnimationInProgress = false;
        SystemUiProxy systemUiProxy = SystemUiProxy.INSTANCE.getNoCreate();
        SystemUiProxy systemUiProxy = SystemUiProxy.INSTANCE.getNoCreate();
+24 −0
Original line number Original line Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
** Copyright 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.
*/
-->

<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
    android:controlX1="0.2"
    android:controlY1="0"
    android:controlX2="0"
    android:controlY2="1"/>
 No newline at end of file
+1 −1
Original line number Original line Diff line number Diff line
@@ -166,6 +166,6 @@


    <!-- Swipe back to home related -->
    <!-- Swipe back to home related -->
    <dimen name="swipe_back_window_scale_x_margin">10dp</dimen>
    <dimen name="swipe_back_window_scale_x_margin">10dp</dimen>
    <dimen name="swipe_back_window_scale_y_margin">80dp</dimen>
    <dimen name="swipe_back_window_max_delta_y">160dp</dimen>
    <dimen name="swipe_back_window_corner_radius">40dp</dimen>
    <dimen name="swipe_back_window_corner_radius">40dp</dimen>
</resources>
</resources>