Merge "Polish the back to home gesture animation to match the spec." into tm-dev

This commit is contained in:
TreeHugger Robot
2022-04-16 01:51:10 +00:00
committed by Android (Google) Code Review
4 changed files with 110 additions and 55 deletions

View File

@@ -244,8 +244,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
mDragLayerAlpha = mDragLayer.getAlphaProperty(ALPHA_INDEX_TRANSITIONS);
mHandler = new Handler(Looper.getMainLooper());
mDeviceProfile = mLauncher.getDeviceProfile();
mBackAnimationController = new LauncherBackAnimationController(
mDeviceProfile, mLauncher, this);
mBackAnimationController = new LauncherBackAnimationController(mLauncher, this);
Resources res = mLauncher.getResources();
mContentScale = res.getFloat(R.dimen.content_scale);
@@ -1441,6 +1440,10 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
}
};
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.

View File

@@ -25,6 +25,7 @@ import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Handler;
@@ -33,11 +34,12 @@ import android.util.MathUtils;
import android.util.Pair;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.window.BackEvent;
import android.window.IOnBackInvokedCallback;
import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.QuickstepTransitionManager;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
@@ -62,34 +64,36 @@ import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
*
*/
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 final DeviceProfile mDeviceProfile;
private final QuickstepTransitionManager mQuickstepTransitionManager;
private final Matrix mTransformMatrix = new Matrix();
private final RectF mTargetRectF = new RectF();
private final RectF mStartRectF = new RectF();
/** The window position at the beginning of the back animation. */
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 BaseQuickstepLauncher mLauncher;
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 mWindowScaleStartCornerRadius;
private final Interpolator mCancelInterpolator;
private final PointF mInitialTouchPos = new PointF();
private RemoteAnimationTargetCompat mBackTarget;
private SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
private boolean mSpringAnimationInProgress = false;
private boolean mAnimatorSetInProgress = false;
@BackEvent.SwipeEdge
private int mSwipeEdge;
private float mBackProgress = 0;
private boolean mBackInProgress = false;
public LauncherBackAnimationController(
DeviceProfile deviceProfile,
BaseQuickstepLauncher launcher,
QuickstepTransitionManager quickstepTransitionManager) {
mDeviceProfile = deviceProfile;
mLauncher = launcher;
mQuickstepTransitionManager = quickstepTransitionManager;
mWindowScaleEndCornerRadius = QuickStepContract.supportsRoundedCornersOnWindows(
@@ -100,8 +104,10 @@ public class LauncherBackAnimationController {
mWindowScaleStartCornerRadius = QuickStepContract.getWindowCornerRadius(mLauncher);
mWindowScaleMarginX = mLauncher.getResources().getDimensionPixelSize(
R.dimen.swipe_back_window_scale_x_margin);
mWindowScaleMarginY = mLauncher.getResources().getDimensionPixelSize(
R.dimen.swipe_back_window_scale_y_margin);
mWindowMaxDeltaY = mLauncher.getResources().getDimensionPixelSize(
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) {
startBack(backEvent);
} else {
updateBackProgress(mBackProgress);
updateBackProgress(mBackProgress, backEvent);
}
}
@@ -145,11 +151,13 @@ public class LauncherBackAnimationController {
}
private void resetPositionAnimated() {
ValueAnimator cancelAnimator = ValueAnimator.ofFloat(mBackProgress, 0);
ValueAnimator cancelAnimator = ValueAnimator.ofFloat(0, 1);
mCancelRect.set(mCurrentRect);
cancelAnimator.setDuration(CANCEL_TRANSITION_DURATION);
cancelAnimator.setInterpolator(mCancelInterpolator);
cancelAnimator.addUpdateListener(
animation -> {
updateBackProgress((float) animation.getAnimatedValue());
updateCancelProgress((float) animation.getAnimatedValue());
});
cancelAnimator.addListener(new AnimatorListenerAdapter() {
@Override
@@ -179,50 +187,70 @@ public class LauncherBackAnimationController {
mTransaction.show(appTarget.leash).apply();
mTransaction.setAnimationTransaction();
mBackTarget = new RemoteAnimationTargetCompat(appTarget);
mSwipeEdge = backEvent.getSwipeEdge();
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;
mInitialTouchPos.set(backEvent.getTouchX(), backEvent.getTouchY());
// TODO(b/218916755): Offset start rectangle in multiwindow mode.
mStartRectF.set(0, 0, screenWidth, screenHeight);
mTargetRectF.set(left, top, targetWidth + left, targetHeight + top);
mStartRect.set(mBackTarget.windowConfiguration.getMaxBounds());
}
private void updateBackProgress(float progress) {
private void updateBackProgress(float progress, BackEvent event) {
if (mBackTarget == null) {
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(
MathUtils.lerp(mStartRectF.left, mTargetRectF.left, progress),
MathUtils.lerp(mStartRectF.top, mTargetRectF.top, progress),
MathUtils.lerp(mStartRectF.right, mTargetRectF.right, progress),
MathUtils.lerp(mStartRectF.bottom, mTargetRectF.bottom, progress));
SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder builder =
new SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder(mBackTarget.leash);
Rect currentRect = new Rect();
mCurrentRect.round(currentRect);
// Scale the target window to match the currentRectF.
final float scale = mCurrentRect.width() / mStartRectF.width();
mTransformMatrix.reset();
mTransformMatrix.setScale(scale, scale);
mTransformMatrix.postTranslate(mCurrentRect.left, mCurrentRect.top);
Rect startRect = new Rect();
mStartRectF.round(startRect);
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(
MathUtils.lerp(mCancelRect.left, mStartRect.left, progress),
MathUtils.lerp(mCancelRect.top, mStartRect.top, progress),
MathUtils.lerp(mCancelRect.right, mStartRect.right, progress),
MathUtils.lerp(mCancelRect.bottom, mStartRect.bottom, progress));
float cornerRadius = Utilities.mapRange(
progress, mWindowScaleEndCornerRadius, mWindowScaleStartCornerRadius);
applyTransform(mCurrentRect, cornerRadius);
}
/** Transform the target window to match the target rect. */
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.setScale(scale, scale);
mTransformMatrix.postTranslate(targetRect.left, targetRect.top);
builder.withMatrix(mTransformMatrix)
.withWindowCrop(startRect)
.withWindowCrop(mStartRect)
.withCornerRadius(cornerRadius);
SyncRtSurfaceTransactionApplierCompat.SurfaceParams surfaceParams = builder.build();
@@ -263,11 +291,11 @@ public class LauncherBackAnimationController {
mBackTarget = null;
mBackInProgress = false;
mBackProgress = 0;
mSwipeEdge = BackEvent.EDGE_LEFT;
mTransformMatrix.reset();
mTargetRectF.setEmpty();
mCancelRect.setEmpty();
mCurrentRect.setEmpty();
mStartRectF.setEmpty();
mStartRect.setEmpty();
mInitialTouchPos.set(0, 0);
mAnimatorSetInProgress = false;
mSpringAnimationInProgress = false;
SystemUiProxy systemUiProxy = SystemUiProxy.INSTANCE.getNoCreate();

View File

@@ -0,0 +1,24 @@
<?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"/>

View File

@@ -166,6 +166,6 @@
<!-- Swipe back to home related -->
<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>
</resources>