Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e018711a authored by Jon Miranda's avatar Jon Miranda
Browse files

Add spring to shelf for home <-> overview <-> all apps state transitions.

Added new SpringObjectAnimator class that wraps an ObjectAnimator so the
Object can be controlled via the Animator or via a SpringAnimation. It extends
ValueAnimator so that it remains compatible with AnimatorPlaybackController.

Code is behind feature flag toggle QUICKSTEP_SPRINGS.

Bug: 111698021
Change-Id: I1b20179ede37e89a6a6bb2a45d407cc74c99ac4e
parent 1c8db791
Loading
Loading
Loading
Loading
+3 −4
Original line number Diff line number Diff line
@@ -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<T extends BaseDraggingActivity> {

            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);
+8 −3
Original line number Diff line number Diff line
@@ -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,8 +145,13 @@ public class LongSwipeHelper {
        ValueAnimator animator = mAnimator.getAnimationPlayer();
        animator.setDuration(duration).setInterpolator(interpolator);
        animator.setFloatValues(currentFraction, endProgress);

        if (QUICKSTEP_SPRINGS.get()) {
            mAnimator.dispatchOnStartWithVelocity(endProgress, velocityPxPerMs);
        } else {
            animator.start();
        }
    }

    private void onSwipeAnimationComplete(boolean toAllApps, boolean isFling, Runnable callback) {
        RecentsView rv = mLauncher.getOverviewPanel();
+14 −7
Original line number Diff line number Diff line
@@ -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<T extends BaseDraggingActivity> {
            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<T extends BaseDraggingActivity> {
    }

    /** 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,8 +856,13 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
                mLauncherTransitionController.dispatchSetInterpolator(Interpolators.mapToProgress(
                        interpolator, adjustedStart, end));
                mLauncherTransitionController.getAnimationPlayer().setDuration(adjustedDuration);

                if (QUICKSTEP_SPRINGS.get()) {
                    mLauncherTransitionController.dispatchOnStartWithVelocity(end, velocityPxPerMs);
                } else {
                    mLauncherTransitionController.getAnimationPlayer().start();
                }
            }
        });
    }

@@ -999,7 +1006,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
        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() {
+54 −5
Original line number Diff line number Diff line
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<AllAppsTransitionController> ALL_APPS_PROGRESS_SPRING
            = new FloatPropertyCompat<AllAppsTransitionController>("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<AllAppsTransitionController, Float> {

        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());
+66 −4
Original line number Diff line number Diff line
@@ -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<SpringAnimation> 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,11 +350,14 @@ public abstract class AnimatorPlaybackController implements ValueAnimator.Animat

        @Override
        public void onAnimationSuccess(Animator animator) {
            // 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();
                }
            }
        }

        private void dispatchOnEndRecursively(Animator animator) {
            for (AnimatorListener l : nonNullList(animator.getListeners())) {
Loading