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
This commit is contained in:
+2
-2
@@ -79,8 +79,8 @@ final class AppToOverviewAnimationProvider<T extends StatefulActivity<?>> 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);
|
||||
|
||||
@@ -365,8 +365,7 @@ public abstract class BaseSwipeUpHandler<T extends StatefulActivity<?>, 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) {
|
||||
|
||||
+11
-44
@@ -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<T extends StatefulActivity<?>, 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<T extends StatefulActivity<?>, 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<T extends StatefulActivity<?>, 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<T extends StatefulActivity<?>, 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<T extends StatefulActivity<?>, 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<T extends StatefulActivity<?>, 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<T extends StatefulActivity<?>, 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;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -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<AnimatorPlaybackController> callback) {
|
||||
boolean activityVisible, Consumer<AnimatorControllerWithResistance> callback) {
|
||||
DefaultAnimationFactory factory = new DefaultAnimationFactory(callback);
|
||||
factory.initUI();
|
||||
return factory;
|
||||
|
||||
+2
-2
@@ -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<AnimatorPlaybackController> callback) {
|
||||
boolean activityVisible, Consumer<AnimatorControllerWithResistance> callback) {
|
||||
notifyRecentsOfOrientation(deviceState.getRotationTouchHelper());
|
||||
DefaultAnimationFactory factory = new DefaultAnimationFactory(callback) {
|
||||
@Override
|
||||
|
||||
+10
-25
@@ -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());
|
||||
|
||||
|
||||
@@ -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<STATE_TYPE extends BaseState<STATE_T
|
||||
public abstract void onAssistantVisibilityChanged(float visibility);
|
||||
|
||||
public abstract AnimationFactory prepareRecentsUI(RecentsAnimationDeviceState deviceState,
|
||||
boolean activityVisible, Consumer<AnimatorPlaybackController> callback);
|
||||
boolean activityVisible, Consumer<AnimatorControllerWithResistance> callback);
|
||||
|
||||
public abstract ActivityInitListener createActivityInitListener(
|
||||
Predicate<Boolean> onInitListener);
|
||||
@@ -319,11 +320,11 @@ public abstract class BaseActivityInterface<STATE_TYPE extends BaseState<STATE_T
|
||||
|
||||
protected final ACTIVITY_TYPE mActivity;
|
||||
private final STATE_TYPE mStartState;
|
||||
private final Consumer<AnimatorPlaybackController> mCallback;
|
||||
private final Consumer<AnimatorControllerWithResistance> mCallback;
|
||||
|
||||
private boolean mIsAttachedToWindow;
|
||||
|
||||
DefaultAnimationFactory(Consumer<AnimatorPlaybackController> callback) {
|
||||
DefaultAnimationFactory(Consumer<AnimatorControllerWithResistance> callback) {
|
||||
mCallback = callback;
|
||||
|
||||
mActivity = getCreatedActivity();
|
||||
@@ -351,7 +352,13 @@ public abstract class BaseActivityInterface<STATE_TYPE extends BaseState<STATE_T
|
||||
controller.setEndAction(() -> 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
|
||||
|
||||
+1
-2
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 <SCALE> AnimatorControllerWithResistance createForRecents(
|
||||
AnimatorPlaybackController normalController, Context context,
|
||||
RecentsOrientedState recentsOrientedState, DeviceProfile dp, SCALE scaleTarget,
|
||||
FloatProperty<SCALE> 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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user