From bef6a44e7b8869a4f311db778b9ebfe2f3d40428 Mon Sep 17 00:00:00 2001 From: Tony Date: Mon, 20 May 2019 21:35:59 -0400 Subject: [PATCH] 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 --- .../LauncherActivityControllerHelper.java | 38 ++++++++++++++++--- .../WindowTransformSwipeHandler.java | 33 ++++++++++++++-- .../quickstep/util/ClipAnimationHelper.java | 9 +++-- .../com/android/quickstep/views/TaskView.java | 1 + .../quickstep/ActivityControlHelper.java | 2 + 5 files changed, 70 insertions(+), 13 deletions(-) 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 4b2e487da9..00e4a9d5a8 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.Animator; 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 @@ import java.util.function.Consumer; */ public final class LauncherActivityControllerHelper implements ActivityControlHelper { + private Runnable mAdjustInterpolatorsRunnable; + @Override public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect) { LayoutUtils.calculateLauncherTaskSize(context, dp, outRect); @@ -193,6 +196,13 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe } } + @Override + public void adjustActivityControllerInterpolators() { + if (mAdjustInterpolatorsRunnable != null) { + mAdjustInterpolatorsRunnable.run(); + } + } + @Override public void onTransitionCancelled() { activity.getStateManager().goToState(startState, false /* animate */); @@ -275,6 +285,7 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe 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 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe 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 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe = 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 ffef1cf325..16beb79527 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 @@ public class WindowTransformSwipeHandler 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 @@ public class WindowTransformSwipeHandler 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 @@ public class WindowTransformSwipeHandler 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 class WindowTransformSwipeHandler 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 @@ public class WindowTransformSwipeHandler private void onAnimatorPlaybackControllerCreated(AnimatorPlaybackController anim) { mLauncherTransitionController = anim; + mLauncherTransitionController.dispatchSetInterpolator(t -> t * mDragLengthFactor); + mAnimationFactory.adjustActivityControllerInterpolators(); mLauncherTransitionController.dispatchOnStart(); updateLauncherTransitionProgress(); } @@ -690,7 +712,9 @@ public class WindowTransformSwipeHandler 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 @@ public class WindowTransformSwipeHandler } 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 @@ public class WindowTransformSwipeHandler 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 c164a2450f..e2fb602d92 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 static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MOD 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 class ClipAnimationHelper { 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 @@ public class ClipAnimationHelper { 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 @@ public class ClipAnimationHelper { 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 053b7389cc..3364377e9d 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 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { * @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 17f88c980e..b0acd9b1b6 100644 --- a/quickstep/src/com/android/quickstep/ActivityControlHelper.java +++ b/quickstep/src/com/android/quickstep/ActivityControlHelper.java @@ -118,6 +118,8 @@ public interface ActivityControlHelper { void createActivityController(long transitionLength); + default void adjustActivityControllerInterpolators() { } + default void onTransitionCancelled() { } default void setShelfState(ShelfAnimState animState, Interpolator interpolator,