From 354a436f4e96d2ae12740cb1af16b97c37166321 Mon Sep 17 00:00:00 2001 From: Tony Wickham Date: Thu, 25 Jun 2020 15:51:33 -0700 Subject: [PATCH] Rewrite long swipe resistance ("pullback") logic - Rename "pullback" to "resistance" to reduce confusion. - Remove mDragLengthFactorStartPullback & mDragLengthFactorMaxPullback - Add AnimatorControllerWithResistance, which has 2 controllers, one for the normal shift to overview, then one to apply the resistance when swiping beyond that. - Don't hack animator interpolators/progress; insteaad, allow progress to go > 1 (which will run the separate resistance animator). - Don't start launcher controller separately from window controller; instead, both are controlled by mCurrentShift in updateFinalShift(). - The resistance animation logic is shared by both the active window and launcher (RecentsView). Bug: 149934536 Change-Id: Ib0f9da18e10cc9ddf1a2f82ed767f237c89d3a41 Merged-In: Ib0f9da18e10cc9ddf1a2f82ed767f237c89d3a41 --- .../AppToOverviewAnimationProvider.java | 4 +- .../android/quickstep/BaseSwipeUpHandler.java | 3 +- .../quickstep/BaseSwipeUpHandlerV2.java | 55 ++---- .../quickstep/FallbackActivityInterface.java | 4 +- .../quickstep/LauncherActivityInterface.java | 4 +- .../quickstep/SwipeUpAnimationLogic.java | 35 ++-- .../quickstep/BaseActivityInterface.java | 15 +- .../SwipeUpGestureTutorialController.java | 3 +- .../AnimatorControllerWithResistance.java | 157 ++++++++++++++++++ .../android/launcher3/anim/Interpolators.java | 1 + 10 files changed, 198 insertions(+), 83 deletions(-) create mode 100644 quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java index 9310685e30..55f542461c 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java @@ -79,8 +79,8 @@ final class AppToOverviewAnimationProvider> extend BaseActivityInterface.AnimationFactory factory = mActivityInterface.prepareRecentsUI( mDeviceState, wasVisible, (controller) -> { - controller.dispatchOnStart(); - controller.getAnimationPlayer().end(); + controller.getNormalController().dispatchOnStart(); + controller.getNormalController().getAnimationPlayer().end(); }); factory.createActivityInterface(RECENTS_LAUNCH_DURATION); factory.setRecentsAttachedToAppWindow(true, false); diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java index cbef67b84b..c90f70125a 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java @@ -365,8 +365,7 @@ public abstract class BaseSwipeUpHandler, Q extend */ protected void applyWindowTransform() { if (mWindowTransitionController != null) { - float progress = mCurrentShift.value / mDragLengthFactor; - mWindowTransitionController.setPlayFraction(progress); + mWindowTransitionController.setProgress(mCurrentShift.value, mDragLengthFactor); } if (mRecentsAnimationTargets != null) { if (mRecentsViewScrollLinked) { diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java index 758066f8c7..929b11ad66 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java @@ -21,7 +21,6 @@ import static com.android.launcher3.anim.Interpolators.DEACCEL; import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2; import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE; -import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS; import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.IGNORE; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_GESTURE; @@ -45,7 +44,6 @@ import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHO import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME; import android.animation.Animator; -import android.animation.TimeInterpolator; import android.animation.ValueAnimator; import android.annotation.TargetApi; import android.app.ActivityManager; @@ -67,7 +65,6 @@ import com.android.launcher3.DeviceProfile; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimationSuccessListener; -import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.logging.UserEventDispatcher; @@ -80,6 +77,7 @@ import com.android.quickstep.BaseActivityInterface.AnimationFactory; import com.android.quickstep.GestureState.GestureEndTarget; import com.android.quickstep.inputconsumers.OverviewInputConsumer; import com.android.quickstep.util.ActiveGestureLog; +import com.android.quickstep.util.AnimatorControllerWithResistance; import com.android.quickstep.util.RectFSpringAnim; import com.android.quickstep.util.ShelfPeekAnim; import com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState; @@ -181,8 +179,7 @@ public abstract class BaseSwipeUpHandlerV2, Q exte private ThumbnailData mTaskSnapshot; // Used to control launcher components throughout the swipe gesture. - private AnimatorPlaybackController mLauncherTransitionController; - private boolean mHasLauncherTransitionControllerStarted; + private AnimatorControllerWithResistance mLauncherTransitionController; private AnimationFactory mAnimationFactory = (t) -> { }; @@ -528,11 +525,11 @@ public abstract class BaseSwipeUpHandlerV2, Q exte /** * We don't want to change mLauncherTransitionController if mGestureState.getEndTarget() == HOME - * (it has its own animation) or if we're already animating the current controller. + * (it has its own animation). * @return Whether we can create the launcher controller or update its progress. */ private boolean canCreateNewOrUpdateExistingLauncherTransitionController() { - return mGestureState.getEndTarget() != HOME && !mHasLauncherTransitionControllerStarted; + return mGestureState.getEndTarget() != HOME; } @Override @@ -542,10 +539,9 @@ public abstract class BaseSwipeUpHandlerV2, Q exte return result; } - private void onAnimatorPlaybackControllerCreated(AnimatorPlaybackController anim) { + private void onAnimatorPlaybackControllerCreated(AnimatorControllerWithResistance anim) { mLauncherTransitionController = anim; - mLauncherTransitionController.dispatchSetInterpolator(t -> t * mDragLengthFactor); - mLauncherTransitionController.dispatchOnStart(); + mLauncherTransitionController.getNormalController().dispatchOnStart(); updateLauncherTransitionProgress(); } @@ -582,10 +578,7 @@ public abstract class BaseSwipeUpHandlerV2, Q exte || !canCreateNewOrUpdateExistingLauncherTransitionController()) { return; } - // 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); + mLauncherTransitionController.setProgress(mCurrentShift.value, mDragLengthFactor); } /** @@ -1028,31 +1021,6 @@ public abstract class BaseSwipeUpHandlerV2, Q exte windowAnim.start(); mRunningWindowAnim = RunningWindowAnim.wrap(windowAnim); } - // Always play the entire launcher animation when going home, since it is separate from - // the animation that has been controlled thus far. - if (mGestureState.getEndTarget() == HOME) { - start = 0; - } - - // We want to use the same interpolator as the window, but need to adjust it to - // interpolate over the remaining progress (end - start). - TimeInterpolator adjustedInterpolator = Interpolators.mapToProgress( - interpolator, start, end); - if (mLauncherTransitionController == null) { - return; - } - if (start == end || duration <= 0) { - mLauncherTransitionController.dispatchSetInterpolator(t -> end); - } else { - mLauncherTransitionController.dispatchSetInterpolator(adjustedInterpolator); - } - mLauncherTransitionController.getAnimationPlayer().setDuration(Math.max(0, duration)); - - if (UNSTABLE_SPRINGS.get()) { - mLauncherTransitionController.dispatchOnStart(); - } - mLauncherTransitionController.getAnimationPlayer().start(); - mHasLauncherTransitionControllerStarted = true; } private void computeRecentsScrollIfInvisible() { @@ -1173,10 +1141,6 @@ public abstract class BaseSwipeUpHandlerV2, Q exte private void cancelCurrentAnimation() { mCanceled = true; mCurrentShift.cancelAnimation(); - if (mLauncherTransitionController != null && mLauncherTransitionController - .getAnimationPlayer().isStarted()) { - mLauncherTransitionController.getAnimationPlayer().cancel(); - } } private void invalidateHandler() { @@ -1202,7 +1166,10 @@ public abstract class BaseSwipeUpHandlerV2, Q exte private void endLauncherTransitionController() { setShelfState(ShelfAnimState.CANCEL, LINEAR, 0); if (mLauncherTransitionController != null) { - mLauncherTransitionController.getAnimationPlayer().end(); + // End the animation, but stay at the same visual progress. + mLauncherTransitionController.getNormalController().dispatchSetInterpolator( + t -> Utilities.boundToRange(mCurrentShift.value, 0, 1)); + mLauncherTransitionController.getNormalController().getAnimationPlayer().end(); mLauncherTransitionController = null; } } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java index d1da0c1c10..3898f0b0ca 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java @@ -27,11 +27,11 @@ import androidx.annotation.Nullable; import com.android.launcher3.DeviceProfile; import com.android.launcher3.R; -import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.touch.PagedOrientationHandler; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.quickstep.fallback.RecentsState; import com.android.quickstep.util.ActivityInitListener; +import com.android.quickstep.util.AnimatorControllerWithResistance; import com.android.quickstep.views.RecentsView; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; @@ -84,7 +84,7 @@ public final class FallbackActivityInterface extends /** 6 */ @Override public AnimationFactory prepareRecentsUI(RecentsAnimationDeviceState deviceState, - boolean activityVisible, Consumer callback) { + boolean activityVisible, Consumer callback) { DefaultAnimationFactory factory = new DefaultAnimationFactory(callback); factory.initUI(); return factory; diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java index 7cd49fe18d..cd4be1895b 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java @@ -39,7 +39,6 @@ import com.android.launcher3.LauncherInitListener; import com.android.launcher3.LauncherState; import com.android.launcher3.R; import com.android.launcher3.allapps.DiscoveryBounce; -import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.appprediction.PredictionUiStateManager; import com.android.launcher3.statehandlers.DepthController; @@ -50,6 +49,7 @@ import com.android.launcher3.touch.PagedOrientationHandler; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.quickstep.SysUINavigationMode.Mode; import com.android.quickstep.util.ActivityInitListener; +import com.android.quickstep.util.AnimatorControllerWithResistance; import com.android.quickstep.util.LayoutUtils; import com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState; import com.android.quickstep.views.RecentsView; @@ -119,7 +119,7 @@ public final class LauncherActivityInterface extends @Override public AnimationFactory prepareRecentsUI(RecentsAnimationDeviceState deviceState, - boolean activityVisible, Consumer callback) { + boolean activityVisible, Consumer callback) { notifyRecentsOfOrientation(deviceState.getRotationTouchHelper()); DefaultAnimationFactory factory = new DefaultAnimationFactory(callback) { @Override diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeUpAnimationLogic.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeUpAnimationLogic.java index e54a21c073..137f8091ad 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeUpAnimationLogic.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeUpAnimationLogic.java @@ -16,7 +16,7 @@ package com.android.quickstep; import static com.android.launcher3.anim.Interpolators.ACCEL_1_5; -import static com.android.launcher3.anim.Interpolators.DEACCEL; +import static com.android.launcher3.anim.Interpolators.LINEAR; import android.animation.Animator; import android.content.Context; @@ -24,7 +24,6 @@ import android.graphics.Matrix; import android.graphics.Matrix.ScaleToFit; import android.graphics.Rect; import android.graphics.RectF; -import android.view.animation.Interpolator; import androidx.annotation.NonNull; import androidx.annotation.UiThread; @@ -35,6 +34,7 @@ import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.touch.PagedOrientationHandler; +import com.android.quickstep.util.AnimatorControllerWithResistance; import com.android.quickstep.util.RectFSpringAnim; import com.android.quickstep.util.TaskViewSimulator; import com.android.quickstep.util.TransformParams; @@ -45,7 +45,6 @@ import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat. public abstract class SwipeUpAnimationLogic { protected static final Rect TEMP_RECT = new Rect(); - private static final Interpolator PULLBACK_INTERPOLATOR = DEACCEL; protected DeviceProfile mDp; @@ -66,12 +65,8 @@ public abstract class SwipeUpAnimationLogic { protected int mTransitionDragLength; // How much further we can drag past recents, as a factor of mTransitionDragLength. protected float mDragLengthFactor = 1; - // Start resisting when swiping past this factor of mTransitionDragLength. - private float mDragLengthFactorStartPullback = 1f; - // This is how far down we can scale down, where 0f is full screen and 1f is recents. - private float mDragLengthFactorMaxPullback = 1f; - protected AnimatorPlaybackController mWindowTransitionController; + protected AnimatorControllerWithResistance mWindowTransitionController; public SwipeUpAnimationLogic(Context context, RecentsAnimationDeviceState deviceState, GestureState gestureState, TransformParams transformParams) { @@ -97,19 +92,16 @@ public abstract class SwipeUpAnimationLogic { if (mDeviceState.isFullyGesturalNavMode()) { // We can drag all the way to the top of the screen. mDragLengthFactor = (float) dp.heightPx / mTransitionDragLength; - - float startScale = mTaskViewSimulator.getFullScreenScale(); - // Start pulling back when RecentsView scale is 0.75f, and let it go down to 0.5f. - mDragLengthFactorStartPullback = (0.75f - startScale) / (1 - startScale); - mDragLengthFactorMaxPullback = (0.5f - startScale) / (1 - startScale); } else { - mDragLengthFactor = 1; - mDragLengthFactorStartPullback = mDragLengthFactorMaxPullback = 1; + mDragLengthFactor = 1 + AnimatorControllerWithResistance.TWO_BUTTON_EXTRA_DRAG_FACTOR; } PendingAnimation pa = new PendingAnimation(mTransitionDragLength * 2); - mTaskViewSimulator.addAppToOverviewAnim(pa, t -> t * mDragLengthFactor); - mWindowTransitionController = pa.createPlaybackController(); + mTaskViewSimulator.addAppToOverviewAnim(pa, LINEAR); + AnimatorPlaybackController normalController = pa.createPlaybackController(); + mWindowTransitionController = AnimatorControllerWithResistance.createForRecents( + normalController, mContext, mTaskViewSimulator.getOrientationState(), + mDp, mTaskViewSimulator.recentsViewScale, AnimatedFloat.VALUE); } @UiThread @@ -122,13 +114,6 @@ public abstract class SwipeUpAnimationLogic { } else { float translation = Math.max(displacement, 0); shift = mTransitionDragLength == 0 ? 0 : translation / mTransitionDragLength; - if (shift > mDragLengthFactorStartPullback) { - float pullbackProgress = Utilities.getProgress(shift, - mDragLengthFactorStartPullback, mDragLengthFactor); - pullbackProgress = PULLBACK_INTERPOLATOR.getInterpolation(pullbackProgress); - shift = mDragLengthFactorStartPullback + pullbackProgress - * (mDragLengthFactorMaxPullback - mDragLengthFactorStartPullback); - } } mCurrentShift.updateValue(shift); @@ -183,7 +168,7 @@ public abstract class SwipeUpAnimationLogic { HomeAnimationFactory homeAnimationFactory) { final RectF targetRect = homeAnimationFactory.getWindowTargetRect(); - mWindowTransitionController.setPlayFraction(startProgress / mDragLengthFactor); + mCurrentShift.updateValue(startProgress); mTaskViewSimulator.apply(mTransformParams.setProgress(startProgress)); RectF cropRectF = new RectF(mTaskViewSimulator.getCurrentCropRect()); diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java index df8f31d2c0..31630ff1bc 100644 --- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java +++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java @@ -52,6 +52,7 @@ import com.android.launcher3.touch.PagedOrientationHandler; import com.android.launcher3.util.WindowBounds; import com.android.quickstep.SysUINavigationMode.Mode; import com.android.quickstep.util.ActivityInitListener; +import com.android.quickstep.util.AnimatorControllerWithResistance; import com.android.quickstep.util.ShelfPeekAnim; import com.android.quickstep.util.SplitScreenBounds; import com.android.quickstep.views.RecentsView; @@ -106,7 +107,7 @@ public abstract class BaseActivityInterface callback); + boolean activityVisible, Consumer callback); public abstract ActivityInitListener createActivityInitListener( Predicate onInitListener); @@ -319,11 +320,11 @@ public abstract class BaseActivityInterface mCallback; + private final Consumer mCallback; private boolean mIsAttachedToWindow; - DefaultAnimationFactory(Consumer callback) { + DefaultAnimationFactory(Consumer callback) { mCallback = callback; mActivity = getCreatedActivity(); @@ -351,7 +352,13 @@ public abstract class BaseActivityInterface mActivity.getStateManager().goToState( controller.getInterpolatedProgress() > 0.5 ? mOverviewState : mBackgroundState, false)); - mCallback.accept(controller); + + RecentsView recentsView = mActivity.getOverviewPanel(); + AnimatorControllerWithResistance controllerWithResistance = + AnimatorControllerWithResistance.createForRecents(controller, mActivity, + recentsView.getPagedViewOrientedState(), mActivity.getDeviceProfile(), + recentsView, RECENTS_SCALE_PROPERTY); + mCallback.accept(controllerWithResistance); // Creating the activity controller animation sometimes reapplies the launcher state // (because we set the animation as the current state animation), so we reapply the diff --git a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java index 4110b33d24..a15731400e 100644 --- a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java +++ b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java @@ -184,8 +184,7 @@ abstract class SwipeUpGestureTutorialController extends TutorialController { @Override public void updateFinalShift() { - float progress = mCurrentShift.value / mDragLengthFactor; - mWindowTransitionController.setPlayFraction(progress); + mWindowTransitionController.setProgress(mCurrentShift.value, mDragLengthFactor); mTaskViewSimulator.apply(mTransformParams); } diff --git a/quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java b/quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java new file mode 100644 index 0000000000..d58329ab0b --- /dev/null +++ b/quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2020 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. + */ +package com.android.quickstep.util; + +import static com.android.launcher3.anim.Interpolators.DEACCEL; +import static com.android.launcher3.anim.Interpolators.LINEAR; +import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS; + +import android.animation.TimeInterpolator; +import android.content.Context; +import android.graphics.PointF; +import android.graphics.Rect; +import android.util.FloatProperty; + +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.Utilities; +import com.android.launcher3.anim.AnimatorPlaybackController; +import com.android.launcher3.anim.PendingAnimation; +import com.android.quickstep.LauncherActivityInterface; +import com.android.quickstep.SysUINavigationMode; + +/** + * Controls an animation that can go beyond progress = 1, at which point resistance should be + * applied. Internally, this is a wrapper around 2 {@link AnimatorPlaybackController}s, one that + * runs from progress 0 to 1 like normal, then one that seamlessly continues that animation but + * starts applying resistance as well. + */ +public class AnimatorControllerWithResistance { + + /** + * How much farther we can drag past overview in 2-button mode, as a factor of the distance + * it takes to drag from an app to overview. + */ + public static final float TWO_BUTTON_EXTRA_DRAG_FACTOR = 0.25f; + + /** + * Start slowing down the rate of scaling down when recents view is smaller than this scale. + */ + private static final float RECENTS_SCALE_START_RESIST = 0.75f; + + /** + * Recents view will reach this scale at the very end of the drag. + */ + private static final float RECENTS_SCALE_MAX_RESIST = 0.5f; + + private static final TimeInterpolator RECENTS_SCALE_RESIST_INTERPOLATOR = DEACCEL; + + private final AnimatorPlaybackController mNormalController; + private final AnimatorPlaybackController mResistanceController; + + // Initialize to -1 so the first 0 gets applied. + private float mLastNormalProgress = -1; + private float mLastResistProgress; + + public AnimatorControllerWithResistance(AnimatorPlaybackController normalController, + AnimatorPlaybackController resistanceController) { + mNormalController = normalController; + mResistanceController = resistanceController; + } + + public AnimatorPlaybackController getNormalController() { + return mNormalController; + } + + /** + * Applies the current progress of the animation. + * @param progress From 0 to maxProgress, where 1 is the target we are animating towards. + * @param maxProgress > 1, this is where the resistance will be applied. + */ + public void setProgress(float progress, float maxProgress) { + float normalProgress = Utilities.boundToRange(progress, 0, 1); + if (normalProgress != mLastNormalProgress) { + mLastNormalProgress = normalProgress; + mNormalController.setPlayFraction(normalProgress); + } + if (maxProgress <= 1) { + return; + } + float resistProgress = progress <= 1 ? 0 : Utilities.getProgress(progress, 1, maxProgress); + if (resistProgress != mLastResistProgress) { + mLastResistProgress = resistProgress; + mResistanceController.setPlayFraction(resistProgress); + } + } + + /** + * Applies resistance to recents when swiping up past its target position. + * @param normalController The controller to run from 0 to 1 before this resistance applies. + * @param context Used to compute start and end values. + * @param recentsOrientedState Used to compute start and end values. + * @param dp Used to compute start and end values. + * @param scaleTarget The target for the scaleProperty. + * @param scaleProperty Animate the value to change the scale of the window/recents view. + */ + public static AnimatorControllerWithResistance createForRecents( + AnimatorPlaybackController normalController, Context context, + RecentsOrientedState recentsOrientedState, DeviceProfile dp, SCALE scaleTarget, + FloatProperty scaleProperty) { + Rect startRect = new Rect(); + LauncherActivityInterface.INSTANCE.calculateTaskSize(context, dp, startRect, + recentsOrientedState.getOrientationHandler()); + long distanceToCover = startRect.bottom; + boolean isTwoButtonMode = SysUINavigationMode.getMode(context) == TWO_BUTTONS; + if (isTwoButtonMode) { + // We can only drag a small distance past overview, not to the top of the screen. + distanceToCover = (long) + ((dp.heightPx - startRect.bottom) * TWO_BUTTON_EXTRA_DRAG_FACTOR); + } + PendingAnimation resistAnim = new PendingAnimation(distanceToCover * 2); + + PointF pivot = new PointF(); + float fullscreenScale = recentsOrientedState.getFullScreenScaleAndPivot( + startRect, dp, pivot); + float startScale = 1; + float prevScaleRate = (fullscreenScale - startScale) / (dp.heightPx - startRect.bottom); + // This is what the scale would be at the end of the drag if we didn't apply resistance. + float endScale = startScale - prevScaleRate * distanceToCover; + final TimeInterpolator scaleInterpolator; + if (isTwoButtonMode) { + // We are bounded by the distance of the drag, so we don't need to apply resistance. + scaleInterpolator = LINEAR; + } else { + // Create an interpolator that resists the scale so the scale doesn't get smaller than + // RECENTS_SCALE_MAX_RESIST. + float startResist = Utilities.getProgress(RECENTS_SCALE_START_RESIST, startScale, + endScale); + float maxResist = Utilities.getProgress(RECENTS_SCALE_MAX_RESIST, startScale, endScale); + scaleInterpolator = t -> { + if (t < startResist) { + return t; + } + float resistProgress = Utilities.getProgress(t, startResist, 1); + resistProgress = RECENTS_SCALE_RESIST_INTERPOLATOR.getInterpolation(resistProgress); + return startResist + resistProgress * (maxResist - startResist); + }; + } + resistAnim.addFloat(scaleTarget, scaleProperty, startScale, endScale, + scaleInterpolator); + + AnimatorPlaybackController resistanceController = resistAnim.createPlaybackController(); + return new AnimatorControllerWithResistance(normalController, resistanceController); + } + +} diff --git a/src/com/android/launcher3/anim/Interpolators.java b/src/com/android/launcher3/anim/Interpolators.java index 860ccebef4..6ad43ea1e4 100644 --- a/src/com/android/launcher3/anim/Interpolators.java +++ b/src/com/android/launcher3/anim/Interpolators.java @@ -198,6 +198,7 @@ public class Interpolators { public OvershootParams(float startProgress, float overshootPastProgress, float endProgress, float velocityPxPerMs, int totalDistancePx, Context context) { velocityPxPerMs = Math.abs(velocityPxPerMs); + overshootPastProgress = Math.max(overshootPastProgress, startProgress); start = startProgress; int startPx = (int) (start * totalDistancePx); // Overshoot by about half a frame.