mirror of
https://github.com/LawnchairLauncher/lawnchair.git
synced 2026-02-28 07:46:55 +00:00
Merge "Polish the back to home gesture animation to match the spec." into tm-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
cf80645111
@@ -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.
|
||||
|
||||
@@ -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();
|
||||
|
||||
24
res/interpolator/back_cancel.xml
Normal file
24
res/interpolator/back_cancel.xml
Normal 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"/>
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user