Continue scaling down recents past final position in 0 button mode
- Previously, we clamped the progress to 1 when reaching mTransitionDragLength.
Now, we allow dragging all the way to the top of the screen, and store this
new top progress in mDragLengthFactor (> 1f).
- Because the launcher animation controller is inherently bound to a progress
between 0 and 1, we have to do a bit of trickery involving interpolators.
Specifically, we normalize the progress to 0 to 1 by dividing by
mDragLengthFactor, but then we set the interpolators to multiply their
progress by mDragLengthFactor. The result is that the animation progress
appears to go from 0 to mDragLengthFactor, just like the window progress.
- To avoid scaling too small, we start interpolating the progress at a certain
point, ending at a specified max progress when reaching the top of the screen.
Bug: 131741395
Change-Id: Ie8b4b56d37249cd1456f93c110c26c78fe052dc0
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
index 4b2e487..00e4a9d 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
@@ -30,6 +30,7 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
+import android.animation.TimeInterpolator;
import android.content.Context;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -68,6 +69,8 @@
*/
public final class LauncherActivityControllerHelper implements ActivityControlHelper<Launcher> {
+ private Runnable mAdjustInterpolatorsRunnable;
+
@Override
public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect) {
LayoutUtils.calculateLauncherTaskSize(context, dp, outRect);
@@ -194,6 +197,13 @@
}
@Override
+ public void adjustActivityControllerInterpolators() {
+ if (mAdjustInterpolatorsRunnable != null) {
+ mAdjustInterpolatorsRunnable.run();
+ }
+ }
+
+ @Override
public void onTransitionCancelled() {
activity.getStateManager().goToState(startState, false /* animate */);
}
@@ -275,6 +285,7 @@
playScaleDownAnim(anim, activity, fromState, endState);
anim.setDuration(transitionLength * 2);
+ anim.setInterpolator(LINEAR);
AnimatorPlaybackController controller =
AnimatorPlaybackController.wrap(anim, transitionLength * 2);
activity.getStateManager().setCurrentUserControlledAnimation(controller);
@@ -291,7 +302,6 @@
Animator shiftAnim = new SpringObjectAnimator<>(activity.getAllAppsController(),
"allAppsSpringFromACH", activity.getAllAppsController().getShiftRange(),
SPRING_DAMPING_RATIO, SPRING_STIFFNESS, progressValues);
- shiftAnim.setInterpolator(LINEAR);
return shiftAnim;
}
@@ -310,19 +320,37 @@
= fromState.getOverviewScaleAndTranslation(launcher);
LauncherState.ScaleAndTranslation endScaleAndTranslation
= endState.getOverviewScaleAndTranslation(launcher);
+ float fromTranslationY = fromScaleAndTranslation.translationY;
+ float endTranslationY = endScaleAndTranslation.translationY;
float fromFullscreenProgress = fromState.getOverviewFullscreenProgress();
float endFullscreenProgress = endState.getOverviewFullscreenProgress();
Animator scale = ObjectAnimator.ofFloat(recentsView, SCALE_PROPERTY,
fromScaleAndTranslation.scale, endScaleAndTranslation.scale);
Animator translateY = ObjectAnimator.ofFloat(recentsView, TRANSLATION_Y,
- fromScaleAndTranslation.translationY, endScaleAndTranslation.translationY);
+ fromTranslationY, endTranslationY);
Animator applyFullscreenProgress = ObjectAnimator.ofFloat(recentsView,
RecentsView.FULLSCREEN_PROGRESS, fromFullscreenProgress, endFullscreenProgress);
- scale.setInterpolator(LINEAR);
- translateY.setInterpolator(LINEAR);
- applyFullscreenProgress.setInterpolator(LINEAR);
anim.playTogether(scale, translateY, applyFullscreenProgress);
+
+ mAdjustInterpolatorsRunnable = () -> {
+ // Adjust the translateY interpolator to account for the running task's top inset.
+ // When progress <= 1, this is handled by each task view as they set their fullscreen
+ // progress. However, once we go to progress > 1, fullscreen progress stays at 0, so
+ // recents as a whole needs to translate further to keep up with the app window.
+ TaskView runningTaskView = recentsView.getRunningTaskView();
+ if (runningTaskView == null) {
+ runningTaskView = recentsView.getTaskViewAt(recentsView.getCurrentPage());
+ }
+ TimeInterpolator oldInterpolator = translateY.getInterpolator();
+ Rect fallbackInsets = launcher.getDeviceProfile().getInsets();
+ float extraTranslationY = runningTaskView.getThumbnail().getInsets(fallbackInsets).top;
+ float normalizedTranslationY = extraTranslationY / (fromTranslationY - endTranslationY);
+ translateY.setInterpolator(t -> {
+ float newT = oldInterpolator.getInterpolation(t);
+ return newT <= 1f ? newT : newT + normalizedTranslationY * (newT - 1);
+ });
+ };
}
@Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
index ffef1cf..16beb79 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -217,6 +217,12 @@
private static final long SHELF_ANIM_DURATION = 120;
public static final long RECENTS_ATTACH_DURATION = 300;
+ // Start resisting when swiping past this factor of mTransitionDragLength.
+ private static final float DRAG_LENGTH_FACTOR_START_PULLBACK = 1.4f;
+ // This is how far down we can scale down, where 0f is full screen and 1f is recents.
+ private static final float DRAG_LENGTH_FACTOR_MAX_PULLBACK = 1.8f;
+ private static final Interpolator PULLBACK_INTERPOLATOR = DEACCEL;
+
/**
* Used as the page index for logging when we return to the last task at the end of the gesture.
*/
@@ -231,7 +237,10 @@
private RunningWindowAnim mRunningWindowAnim;
private boolean mIsShelfPeeking;
private DeviceProfile mDp;
+ // The distance needed to drag to reach the task size in recents.
private int mTransitionDragLength;
+ // How much further we can drag past recents, as a factor of mTransitionDragLength.
+ private float mDragLengthFactor = 1;
// Shift in the range of [0, 1].
// 0 => preview snapShot is completely visible, and hotseat is completely translated down
@@ -375,6 +384,10 @@
mTransitionDragLength = mActivityControlHelper.getSwipeUpDestinationAndLength(
dp, mContext, tempRect);
mClipAnimationHelper.updateTargetRect(tempRect);
+ if (mMode == Mode.NO_BUTTON) {
+ // We can drag all the way to the top of the screen.
+ mDragLengthFactor = (float) dp.heightPx / mTransitionDragLength;
+ }
}
private long getFadeInDuration() {
@@ -546,11 +559,18 @@
public void updateDisplacement(float displacement) {
// We are moving in the negative x/y direction
displacement = -displacement;
- if (displacement > mTransitionDragLength && mTransitionDragLength > 0) {
- mCurrentShift.updateValue(1);
+ if (displacement > mTransitionDragLength * mDragLengthFactor && mTransitionDragLength > 0) {
+ mCurrentShift.updateValue(mDragLengthFactor);
} else {
float translation = Math.max(displacement, 0);
float shift = mTransitionDragLength == 0 ? 0 : translation / mTransitionDragLength;
+ if (shift > DRAG_LENGTH_FACTOR_START_PULLBACK) {
+ float pullbackProgress = Utilities.getProgress(shift,
+ DRAG_LENGTH_FACTOR_START_PULLBACK, mDragLengthFactor);
+ pullbackProgress = PULLBACK_INTERPOLATOR.getInterpolation(pullbackProgress);
+ shift = DRAG_LENGTH_FACTOR_START_PULLBACK + pullbackProgress
+ * (DRAG_LENGTH_FACTOR_MAX_PULLBACK - DRAG_LENGTH_FACTOR_START_PULLBACK);
+ }
mCurrentShift.updateValue(shift);
}
}
@@ -638,6 +658,8 @@
private void onAnimatorPlaybackControllerCreated(AnimatorPlaybackController anim) {
mLauncherTransitionController = anim;
+ mLauncherTransitionController.dispatchSetInterpolator(t -> t * mDragLengthFactor);
+ mAnimationFactory.adjustActivityControllerInterpolators();
mLauncherTransitionController.dispatchOnStart();
updateLauncherTransitionProgress();
}
@@ -690,7 +712,9 @@
if (mGestureEndTarget == HOME) {
return;
}
- float progress = mCurrentShift.value;
+ // Normalize the progress to 0 to 1, as the animation controller will clamp it to that
+ // anyway. The controller mimics the drag length factor by applying it to its interpolators.
+ float progress = mCurrentShift.value / mDragLengthFactor;
mLauncherTransitionController.setPlayFraction(
progress <= mShiftAtGestureStart || mShiftAtGestureStart >= 1
? 0 : (progress - mShiftAtGestureStart) / (1 - mShiftAtGestureStart));
@@ -898,7 +922,7 @@
}
endShift = endTarget.endShift;
startShift = Utilities.boundToRange(currentShift - velocityPxPerMs.y
- * SINGLE_FRAME_MS / mTransitionDragLength, 0, 1);
+ * SINGLE_FRAME_MS / mTransitionDragLength, 0, mDragLengthFactor);
float minFlingVelocity = mContext.getResources()
.getDimension(R.dimen.quickstep_fling_min_velocity);
if (Math.abs(endVelocity) > minFlingVelocity && mTransitionDragLength > 0) {
@@ -1057,6 +1081,7 @@
mLauncherTransitionController.getAnimationPlayer().end();
} else {
mLauncherTransitionController.dispatchSetInterpolator(adjustedInterpolator);
+ mAnimationFactory.adjustActivityControllerInterpolators();
mLauncherTransitionController.getAnimationPlayer().setDuration(duration);
if (QUICKSTEP_SPRINGS.get()) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java
index c164a24..e2fb602 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java
@@ -23,7 +23,6 @@
import android.annotation.TargetApi;
import android.content.Context;
-import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Matrix.ScaleToFit;
import android.graphics.Rect;
@@ -160,14 +159,16 @@
public RectF applyTransform(RemoteAnimationTargetSet targetSet, TransformParams params,
boolean launcherOnTop) {
+ float progress = params.progress;
if (params.currentRect == null) {
RectF currentRect;
mTmpRectF.set(mTargetRect);
Utilities.scaleRectFAboutCenter(mTmpRectF, params.offsetScale);
- float progress = params.progress;
currentRect = mRectFEvaluator.evaluate(progress, mSourceRect, mTmpRectF);
currentRect.offset(params.offsetX, 0);
+ // Don't clip past progress > 1.
+ progress = Math.min(1, progress);
final RectF sourceWindowClipInsets = params.forLiveTile
? mSourceWindowClipInsetsForLiveTile : mSourceWindowClipInsets;
mClipRectF.left = sourceWindowClipInsets.left * progress;
@@ -189,7 +190,7 @@
float alpha = 1f;
int layer = RemoteAnimationProvider.getLayer(app, mBoostModeTargetLayers);
float cornerRadius = 0f;
- float scale = params.currentRect.width() / crop.width();
+ float scale = Math.max(params.currentRect.width(), mTargetRect.width()) / crop.width();
if (app.mode == targetSet.targetMode) {
if (app.activityType != RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
mTmpMatrix.setRectToRect(mSourceRect, params.currentRect, ScaleToFit.FILL);
@@ -198,7 +199,7 @@
if (mSupportsRoundedCornersOnWindows) {
float windowCornerRadius = mUseRoundedCornersOnWindows
? mWindowCornerRadius : 0;
- cornerRadius = Utilities.mapRange(params.progress, windowCornerRadius,
+ cornerRadius = Utilities.mapRange(progress, windowCornerRadius,
mTaskCornerRadius);
mCurrentCornerRadius = cornerRadius;
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
index 053b738..3364377 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
@@ -633,6 +633,7 @@
* @param progress: 0 = show icon and no insets; 1 = don't show icon and show full insets.
*/
public void setFullscreenProgress(float progress) {
+ progress = Utilities.boundToRange(progress, 0, 1);
if (progress == mFullscreenProgress) {
return;
}
diff --git a/quickstep/src/com/android/quickstep/ActivityControlHelper.java b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
index 17f88c9..b0acd9b 100644
--- a/quickstep/src/com/android/quickstep/ActivityControlHelper.java
+++ b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
@@ -118,6 +118,8 @@
void createActivityController(long transitionLength);
+ default void adjustActivityControllerInterpolators() { }
+
default void onTransitionCancelled() { }
default void setShelfState(ShelfAnimState animState, Interpolator interpolator,