diff --git a/quickstep/src/com/android/quickstep/ActivityControlHelper.java b/quickstep/src/com/android/quickstep/ActivityControlHelper.java index 4646fd7f42..952b6899c9 100644 --- a/quickstep/src/com/android/quickstep/ActivityControlHelper.java +++ b/quickstep/src/com/android/quickstep/ActivityControlHelper.java @@ -21,7 +21,6 @@ import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; import static com.android.launcher3.LauncherState.BACKGROUND_APP; import static com.android.launcher3.LauncherState.FAST_OVERVIEW; import static com.android.launcher3.LauncherState.OVERVIEW; -import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS; import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.states.RotationHelper.REQUEST_LOCK; import static com.android.quickstep.TouchConsumer.INTERACTION_NORMAL; @@ -53,9 +52,9 @@ import com.android.launcher3.LauncherInitListener; import com.android.launcher3.LauncherState; import com.android.launcher3.R; import com.android.launcher3.TestProtocol; -import com.android.launcher3.allapps.AllAppsTransitionController; import com.android.launcher3.allapps.DiscoveryBounce; import com.android.launcher3.anim.AnimatorPlaybackController; +import com.android.launcher3.anim.SpringObjectAnimator; import com.android.launcher3.compat.AccessibilityManagerCompat; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.dragndrop.DragLayer; @@ -295,8 +294,8 @@ public interface ActivityControlHelper { AnimatorSet anim = new AnimatorSet(); if (!activity.getDeviceProfile().isVerticalBarLayout()) { - AllAppsTransitionController controller = activity.getAllAppsController(); - ObjectAnimator shiftAnim = ObjectAnimator.ofFloat(controller, ALL_APPS_PROGRESS, + Animator shiftAnim = new SpringObjectAnimator(activity.getAllAppsController(), + activity.getAllAppsController().getShiftRange(), fromState.getVerticalProgress(activity), endState.getVerticalProgress(activity)); shiftAnim.setInterpolator(LINEAR); diff --git a/quickstep/src/com/android/quickstep/LongSwipeHelper.java b/quickstep/src/com/android/quickstep/LongSwipeHelper.java index 16214dd6ea..80d37aeaf2 100644 --- a/quickstep/src/com/android/quickstep/LongSwipeHelper.java +++ b/quickstep/src/com/android/quickstep/LongSwipeHelper.java @@ -19,6 +19,7 @@ import static com.android.launcher3.LauncherAnimUtils.MIN_PROGRESS_TO_ALL_APPS; import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.anim.Interpolators.DEACCEL; +import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS; import static com.android.quickstep.WindowTransformSwipeHandler.MAX_SWIPE_DURATION; import static com.android.quickstep.WindowTransformSwipeHandler.MIN_OVERSHOOT_DURATION; @@ -41,7 +42,6 @@ import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import com.android.launcher3.util.FlingBlockCheck; import com.android.quickstep.views.RecentsView; -import com.android.systemui.shared.system.RemoteAnimationTargetCompat; /** * Utility class to handle long swipe from an app. @@ -113,7 +113,7 @@ public class LongSwipeHelper { * MAX_SWIPE_DURATION * SWIPE_DURATION_MULTIPLIER)); duration = Math.min(MAX_SWIPE_DURATION, expectedDuration); - if (blockedFling && !toAllApps) { + if (blockedFling && !toAllApps && !QUICKSTEP_SPRINGS.get()) { Interpolators.OvershootParams overshoot = new OvershootParams(currentFraction, currentFraction, endProgress, velocityPxPerMs, (int) mMaxSwipeDistance); duration = (overshoot.duration + duration); @@ -145,7 +145,12 @@ public class LongSwipeHelper { ValueAnimator animator = mAnimator.getAnimationPlayer(); animator.setDuration(duration).setInterpolator(interpolator); animator.setFloatValues(currentFraction, endProgress); - animator.start(); + + if (QUICKSTEP_SPRINGS.get()) { + mAnimator.dispatchOnStartWithVelocity(endProgress, velocityPxPerMs); + } else { + animator.start(); + } } private void onSwipeAnimationComplete(boolean toAllApps, boolean isFling, Runnable callback) { diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java index 686b52b087..7317178c7e 100644 --- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java +++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java @@ -22,6 +22,7 @@ import static com.android.launcher3.Utilities.postAsyncCallback; 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.QUICKSTEP_SPRINGS; import static com.android.quickstep.QuickScrubController.QUICK_SCRUB_FROM_APP_START_DURATION; import static com.android.quickstep.QuickScrubController.QUICK_SWITCH_FROM_APP_START_DURATION; import static com.android.quickstep.TouchConsumer.INTERACTION_NORMAL; @@ -788,7 +789,8 @@ public class WindowTransformSwipeHandler { mRecentsAnimationWrapper.enableTouchProxy(); } - animateToProgress(startShift, endShift, duration, interpolator, goingToHome); + animateToProgress(startShift, endShift, duration, interpolator, goingToHome, + velocityPxPerMs); } private void doLogGesture(boolean toLauncher) { @@ -813,14 +815,14 @@ public class WindowTransformSwipeHandler { } /** Animates to the given progress, where 0 is the current app and 1 is overview. */ - private void animateToProgress(float start, float end, long duration, - Interpolator interpolator, boolean goingToHome) { + private void animateToProgress(float start, float end, long duration, Interpolator interpolator, + boolean goingToHome, float velocityPxPerMs) { mRecentsAnimationWrapper.runOnInit(() -> animateToProgressInternal(start, end, duration, - interpolator, goingToHome)); + interpolator, goingToHome, velocityPxPerMs)); } private void animateToProgressInternal(float start, float end, long duration, - Interpolator interpolator, boolean goingToHome) { + Interpolator interpolator, boolean goingToHome, float velocityPxPerMs) { mIsGoingToHome = goingToHome; ObjectAnimator anim = mCurrentShift.animateToValue(start, end).setDuration(duration); anim.setInterpolator(interpolator); @@ -854,7 +856,12 @@ public class WindowTransformSwipeHandler { mLauncherTransitionController.dispatchSetInterpolator(Interpolators.mapToProgress( interpolator, adjustedStart, end)); mLauncherTransitionController.getAnimationPlayer().setDuration(adjustedDuration); - mLauncherTransitionController.getAnimationPlayer().start(); + + if (QUICKSTEP_SPRINGS.get()) { + mLauncherTransitionController.dispatchOnStartWithVelocity(end, velocityPxPerMs); + } else { + mLauncherTransitionController.getAnimationPlayer().start(); + } } }); } @@ -999,7 +1006,7 @@ public class WindowTransformSwipeHandler { long duration = FeatureFlags.QUICK_SWITCH.get() ? QUICK_SWITCH_FROM_APP_START_DURATION : QUICK_SCRUB_FROM_APP_START_DURATION; - animateToProgress(mCurrentShift.value, 1f, duration, LINEAR, true /* goingToHome */); + animateToProgress(mCurrentShift.value, 1f, duration, LINEAR, true /* goingToHome */, 1f); } private void onQuickScrubStartUi() { diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java index ffbf34c7b3..962c25bf5d 100644 --- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -1,7 +1,6 @@ package com.android.launcher3.allapps; import static com.android.launcher3.LauncherState.ALL_APPS_CONTENT; -import static com.android.launcher3.LauncherState.ALL_APPS_HEADER; import static com.android.launcher3.LauncherState.ALL_APPS_HEADER_EXTRA; import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.LauncherState.VERTICAL_SWIPE_INDICATOR; @@ -15,9 +14,7 @@ import static com.android.launcher3.util.SystemUiController.UI_STATE_ALL_APPS; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; -import android.animation.ObjectAnimator; import android.util.Property; -import android.view.View; import android.view.animation.Interpolator; import com.android.launcher3.DeviceProfile; @@ -29,10 +26,15 @@ import com.android.launcher3.LauncherStateManager.StateHandler; import com.android.launcher3.R; import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimatorSetBuilder; +import com.android.launcher3.anim.SpringObjectAnimator; import com.android.launcher3.anim.PropertySetter; +import com.android.launcher3.anim.SpringObjectAnimator.SpringProperty; import com.android.launcher3.util.Themes; import com.android.launcher3.views.ScrimView; +import androidx.dynamicanimation.animation.FloatPropertyCompat; +import androidx.dynamicanimation.animation.SpringAnimation; + /** * Handles AllApps view transition. * 1) Slides all apps view using direct manipulation @@ -59,6 +61,53 @@ public class AllAppsTransitionController implements StateHandler, OnDeviceProfil } }; + public static final FloatPropertyCompat ALL_APPS_PROGRESS_SPRING + = new FloatPropertyCompat("allAppsProgressSpring") { + @Override + public float getValue(AllAppsTransitionController controller) { + return controller.mProgress; + } + + @Override + public void setValue(AllAppsTransitionController controller, float progress) { + controller.setProgress(progress); + } + }; + + /** + * Property that either sets the progress directly or animates the progress via a spring. + */ + public static class AllAppsSpringProperty extends + SpringProperty { + + SpringAnimation mSpring; + boolean useSpring = false; + + public AllAppsSpringProperty(SpringAnimation spring) { + super(Float.class, "allAppsSpringProperty"); + mSpring = spring; + } + + @Override + public Float get(AllAppsTransitionController controller) { + return controller.getProgress(); + } + + @Override + public void set(AllAppsTransitionController controller, Float progress) { + if (useSpring) { + mSpring.animateToFinalPosition(progress); + } else { + controller.setProgress(progress); + } + } + + @Override + public void switchToSpring() { + useSpring = true; + } + } + private AllAppsContainerView mAppsView; private ScrimView mScrimView; @@ -174,8 +223,8 @@ public class AllAppsTransitionController implements StateHandler, OnDeviceProfil Interpolator interpolator = config.userControlled ? LINEAR : toState == OVERVIEW ? builder.getInterpolator(ANIM_OVERVIEW_SCALE, FAST_OUT_SLOW_IN) : FAST_OUT_SLOW_IN; - ObjectAnimator anim = - ObjectAnimator.ofFloat(this, ALL_APPS_PROGRESS, mProgress, targetProgress); + Animator anim = new SpringObjectAnimator(this, 1f / mShiftRange, mProgress, + targetProgress); anim.setDuration(config.duration); anim.setInterpolator(builder.getInterpolator(ANIM_VERTICAL_PROGRESS, interpolator)); anim.addListener(getProgressAnimatorListener()); diff --git a/src/com/android/launcher3/anim/AnimatorPlaybackController.java b/src/com/android/launcher3/anim/AnimatorPlaybackController.java index 819c8439b4..62f59e4026 100644 --- a/src/com/android/launcher3/anim/AnimatorPlaybackController.java +++ b/src/com/android/launcher3/anim/AnimatorPlaybackController.java @@ -16,6 +16,7 @@ package com.android.launcher3.anim; import static com.android.launcher3.anim.Interpolators.LINEAR; +import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS; import android.animation.Animator; import android.animation.Animator.AnimatorListener; @@ -23,10 +24,16 @@ import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.TimeInterpolator; import android.animation.ValueAnimator; +import android.util.Log; import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Set; + +import androidx.dynamicanimation.animation.DynamicAnimation; +import androidx.dynamicanimation.animation.SpringAnimation; /** * Helper class to control the playback of an {@link AnimatorSet}, with custom interpolators @@ -37,6 +44,9 @@ import java.util.List; */ public abstract class AnimatorPlaybackController implements ValueAnimator.AnimatorUpdateListener { + private static final String TAG = "AnimatorPlaybackCtrler"; + private static boolean DEBUG = false; + public static AnimatorPlaybackController wrap(AnimatorSet anim, long duration) { return wrap(anim, duration, null); } @@ -60,6 +70,7 @@ public abstract class AnimatorPlaybackController implements ValueAnimator.Animat private final long mDuration; protected final AnimatorSet mAnim; + private Set mSprings; protected float mCurrentFraction; private Runnable mEndAction; @@ -67,6 +78,9 @@ public abstract class AnimatorPlaybackController implements ValueAnimator.Animat protected boolean mTargetCancelled = false; protected Runnable mOnCancelRunnable; + private OnAnimationEndDispatcher mEndListener; + private DynamicAnimation.OnAnimationEndListener mSpringEndListener; + protected AnimatorPlaybackController(AnimatorSet anim, long duration, Runnable onCancelRunnable) { mAnim = anim; @@ -75,7 +89,8 @@ public abstract class AnimatorPlaybackController implements ValueAnimator.Animat mAnimationPlayer = ValueAnimator.ofFloat(0, 1); mAnimationPlayer.setInterpolator(LINEAR); - mAnimationPlayer.addListener(new OnAnimationEndDispatcher()); + mEndListener = new OnAnimationEndDispatcher(); + mAnimationPlayer.addListener(mEndListener); mAnimationPlayer.addUpdateListener(this); mAnim.addListener(new AnimatorListenerAdapter() { @@ -99,6 +114,15 @@ public abstract class AnimatorPlaybackController implements ValueAnimator.Animat mTargetCancelled = false; } }); + + mSprings = new HashSet<>(); + mSpringEndListener = (animation, canceled, value, velocity1) -> { + if (canceled) { + mEndListener.onAnimationCancel(mAnimationPlayer); + } else { + mEndListener.onAnimationEnd(mAnimationPlayer); + } + }; } public AnimatorSet getTarget() { @@ -180,6 +204,29 @@ public abstract class AnimatorPlaybackController implements ValueAnimator.Animat } } + /** + * Starts playback and sets the spring. + */ + public void dispatchOnStartWithVelocity(float end, float velocity) { + if (!QUICKSTEP_SPRINGS.get()) { + dispatchOnStart(); + return; + } + + if (DEBUG) Log.d(TAG, "dispatchOnStartWithVelocity#end=" + end + ", velocity=" + velocity); + + for (Animator a : mAnim.getChildAnimations()) { + if (a instanceof SpringObjectAnimator) { + if (DEBUG) Log.d(TAG, "Found springAnimator=" + a); + SpringObjectAnimator springAnimator = (SpringObjectAnimator) a; + mSprings.add(springAnimator.getSpring()); + springAnimator.startSpring(end, velocity, mSpringEndListener); + } + } + + dispatchOnStart(); + } + public void dispatchOnStart() { dispatchOnStartRecursively(mAnim); } @@ -282,6 +329,18 @@ public abstract class AnimatorPlaybackController implements ValueAnimator.Animat } } + private boolean isAnySpringRunning() { + for (SpringAnimation spring : mSprings) { + if (spring.isRunning()) { + return true; + } + } + return false; + } + + /** + * Only dispatches the on end actions once the animator and all springs have completed running. + */ private class OnAnimationEndDispatcher extends AnimationSuccessListener { @Override @@ -291,9 +350,12 @@ public abstract class AnimatorPlaybackController implements ValueAnimator.Animat @Override public void onAnimationSuccess(Animator animator) { - dispatchOnEndRecursively(mAnim); - if (mEndAction != null) { - mEndAction.run(); + // We wait for the spring (if any) to finish running before completing the end callback. + if (mSprings.isEmpty() || !isAnySpringRunning()) { + dispatchOnEndRecursively(mAnim); + if (mEndAction != null) { + mEndAction.run(); + } } } diff --git a/src/com/android/launcher3/anim/SpringObjectAnimator.java b/src/com/android/launcher3/anim/SpringObjectAnimator.java new file mode 100644 index 0000000000..1e365707c1 --- /dev/null +++ b/src/com/android/launcher3/anim/SpringObjectAnimator.java @@ -0,0 +1,270 @@ +/* + * Copyright (C) 2019 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.launcher3.anim; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ObjectAnimator; +import android.animation.TimeInterpolator; +import android.animation.ValueAnimator; +import android.util.Log; +import android.util.Property; + +import com.android.launcher3.allapps.AllAppsTransitionController; +import com.android.launcher3.allapps.AllAppsTransitionController.AllAppsSpringProperty; + +import java.util.ArrayList; + +import androidx.dynamicanimation.animation.DynamicAnimation.OnAnimationEndListener; +import androidx.dynamicanimation.animation.SpringAnimation; +import androidx.dynamicanimation.animation.SpringForce; + +/** + * This animator allows for an object's property to be be controlled by an {@link ObjectAnimator} or + * a {@link SpringAnimation}. It extends ValueAnimator so it can be used in an AnimatorSet. + */ +public class SpringObjectAnimator extends ValueAnimator { + + private static final String TAG = "SpringObjectAnimator"; + private static boolean DEBUG = false; + + private AllAppsTransitionController mObject; + private ObjectAnimator mObjectAnimator; + private float[] mValues; + + private SpringAnimation mSpring; + private AllAppsSpringProperty mProperty; + + private ArrayList mListeners; + private boolean mSpringEnded = false; + private boolean mAnimatorEnded = false; + private boolean mEnded = false; + + private static final float SPRING_DAMPING_RATIO = 0.9f; + private static final float SPRING_STIFFNESS = 600f; + + public SpringObjectAnimator(AllAppsTransitionController object, float minimumVisibleChange, + float... values) { + mObject = object; + mSpring = new SpringAnimation(object, AllAppsTransitionController.ALL_APPS_PROGRESS_SPRING); + mSpring.setMinimumVisibleChange(minimumVisibleChange); + mSpring.setSpring(new SpringForce(0) + .setDampingRatio(SPRING_DAMPING_RATIO) + .setStiffness(SPRING_STIFFNESS)); + mSpring.setStartVelocity(0.01f); + mProperty = new AllAppsSpringProperty(mSpring); + mObjectAnimator = ObjectAnimator.ofFloat(object, mProperty, values); + mValues = values; + mListeners = new ArrayList<>(); + setFloatValues(values); + + mObjectAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + mAnimatorEnded = false; + mEnded = false; + for (AnimatorListener l : mListeners) { + l.onAnimationStart(animation); + } + } + + @Override + public void onAnimationEnd(Animator animation) { + mAnimatorEnded = true; + tryEnding(); + } + + @Override + public void onAnimationCancel(Animator animation) { + for (AnimatorListener l : mListeners) { + l.onAnimationCancel(animation); + } + mSpring.animateToFinalPosition(mObject.getProgress()); + } + }); + + mSpring.addUpdateListener((animation, value, velocity) -> mSpringEnded = false); + mSpring.addEndListener((animation, canceled, value, velocity) -> { + mSpringEnded = true; + tryEnding(); + }); + } + + private void tryEnding() { + if (DEBUG) { + Log.d(TAG, "tryEnding#mAnimatorEnded=" + mAnimatorEnded + ", mSpringEnded=" + + mSpringEnded + ", mEnded=" + mEnded); + } + + if (mAnimatorEnded && mSpringEnded && !mEnded) { + for (AnimatorListener l : mListeners) { + l.onAnimationEnd(mObjectAnimator); + } + mEnded = true; + } + } + + public SpringAnimation getSpring() { + return mSpring; + } + + /** + * Initializes and sets up the spring to take over controlling the object. + */ + void startSpring(float end, float velocity, OnAnimationEndListener endListener) { + // Cancel the spring so we can set new start velocity and final position. We need to remove + // the listener since the spring is not actually ending. + mSpring.removeEndListener(endListener); + mSpring.cancel(); + mSpring.addEndListener(endListener); + + mProperty.switchToSpring(); + + mSpring.setStartVelocity(velocity); + mSpring.animateToFinalPosition(end == 0 ? mValues[0] : mValues[1]); + } + + @Override + public void addListener(AnimatorListener listener) { + mListeners.add(listener); + } + + @Override + public void addPauseListener(AnimatorPauseListener listener) { + mObjectAnimator.addPauseListener(listener); + } + + @Override + public void cancel() { + mSpring.animateToFinalPosition(mObject.getProgress()); + mObjectAnimator.cancel(); + } + + @Override + public void end() { + mObjectAnimator.end(); + } + + @Override + public long getDuration() { + return mObjectAnimator.getDuration(); + } + + @Override + public TimeInterpolator getInterpolator() { + return mObjectAnimator.getInterpolator(); + } + + @Override + public ArrayList getListeners() { + return mObjectAnimator.getListeners(); + } + + @Override + public long getStartDelay() { + return mObjectAnimator.getStartDelay(); + } + + @Override + public long getTotalDuration() { + return mObjectAnimator.getTotalDuration(); + } + + @Override + public boolean isPaused() { + return mObjectAnimator.isPaused(); + } + + @Override + public boolean isRunning() { + return mObjectAnimator.isRunning(); + } + + @Override + public boolean isStarted() { + return mObjectAnimator.isStarted(); + } + + @Override + public void pause() { + mObjectAnimator.pause(); + } + + @Override + public void removeAllListeners() { + mObjectAnimator.removeAllListeners(); + } + + @Override + public void removeListener(AnimatorListener listener) { + mObjectAnimator.removeListener(listener); + } + + @Override + public void removePauseListener(AnimatorPauseListener listener) { + mObjectAnimator.removePauseListener(listener); + } + + @Override + public void resume() { + mObjectAnimator.resume(); + } + + @Override + public ValueAnimator setDuration(long duration) { + return mObjectAnimator.setDuration(duration); + } + + @Override + public void setInterpolator(TimeInterpolator value) { + mObjectAnimator.setInterpolator(value); + } + + @Override + public void setStartDelay(long startDelay) { + mObjectAnimator.setStartDelay(startDelay); + } + + @Override + public void setTarget(Object target) { + mObjectAnimator.setTarget(target); + } + + @Override + public void start() { + mObjectAnimator.start(); + } + + @Override + public void setCurrentFraction(float fraction) { + mObjectAnimator.setCurrentFraction(fraction); + } + + @Override + public void setCurrentPlayTime(long playTime) { + mObjectAnimator.setCurrentPlayTime(playTime); + } + + public static abstract class SpringProperty extends Property { + + public SpringProperty(Class type, String name) { + super(type, name); + } + + abstract public void switchToSpring(); + } + +} diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java index b01e41ea9c..3a7c949a62 100644 --- a/src/com/android/launcher3/config/BaseFlags.java +++ b/src/com/android/launcher3/config/BaseFlags.java @@ -98,6 +98,9 @@ abstract class BaseFlags { public static final TogglableFlag ENABLE_TASK_STABILIZER = new TogglableFlag( "ENABLE_TASK_STABILIZER", false, "Stable task list across fast task switches"); + public static final TogglableFlag QUICKSTEP_SPRINGS = new TogglableFlag("QUICKSTEP_SPRINGS", + false, "Enable springs for quickstep animations"); + public static void initialize(Context context) { // Avoid the disk read for user builds if (Utilities.IS_DEBUG_DEVICE) { diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java index a7bd243a6f..bb143288c4 100644 --- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java +++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java @@ -24,6 +24,7 @@ import static com.android.launcher3.LauncherStateManager.ATOMIC_COMPONENT; import static com.android.launcher3.LauncherStateManager.NON_ATOMIC_COMPONENT; import static com.android.launcher3.Utilities.SINGLE_FRAME_MS; import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity; +import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -45,6 +46,7 @@ import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.AnimatorSetBuilder; import com.android.launcher3.compat.AccessibilityManagerCompat; +import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; @@ -428,8 +430,8 @@ public abstract class AbstractStateChangeTouchController maybeUpdateAtomicAnim(mFromState, targetState, targetState == mToState ? 1f : 0f); updateSwipeCompleteAnimation(anim, Math.max(duration, getRemainingAtomicDuration()), targetState, velocity, fling); - mCurrentAnimation.dispatchOnStart(); - if (fling && targetState == LauncherState.ALL_APPS) { + mCurrentAnimation.dispatchOnStartWithVelocity(endProgress, velocity); + if (fling && targetState == LauncherState.ALL_APPS && !QUICKSTEP_SPRINGS.get()) { mLauncher.getAppsView().addSpringFromFlingUpdateListener(anim, velocity); } anim.start();