Animate AbstractSlideInView translation shift with PendingAnimation.

The PendingAnimation gives us more flexibility than the AnimatorSet.

Test: Manual
Bug: 289290185
Flag: No
Change-Id: I3156a659098c44ce9e6b20e5e79e88742add629a
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
index 5691ecf..db225be 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
@@ -158,11 +158,11 @@
     }
 
     private void animateOpen() {
-        if (mIsOpen || mOpenCloseAnimator.isRunning()) {
+        if (mIsOpen || mOpenCloseAnimation.getAnimationPlayer().isRunning()) {
             return;
         }
         mIsOpen = true;
-        setUpDefaultOpenAnimator().start();
+        setUpDefaultOpenAnimation().start();
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
index 3fe7359..9ee7b0e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
@@ -17,6 +17,7 @@
 
 import static com.android.app.animation.Interpolators.EMPHASIZED;
 
+import android.animation.Animator;
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Rect;
@@ -58,7 +59,7 @@
 
     /** Opens the all apps view. */
     void show(boolean animate) {
-        if (mIsOpen || mOpenCloseAnimator.isRunning()) {
+        if (mIsOpen || mOpenCloseAnimation.getAnimationPlayer().isRunning()) {
             return;
         }
         mIsOpen = true;
@@ -66,10 +67,12 @@
         mAllAppsCallbacks.onAllAppsTransitionStart(true);
 
         if (animate) {
-            setUpOpenCloseAnimator(TRANSLATION_SHIFT_OPENED, EMPHASIZED);
-            mOpenCloseAnimator.addListener(AnimatorListeners.forEndCallback(
+            setUpOpenAnimation(mAllAppsCallbacks.getOpenDuration());
+            Animator animator = mOpenCloseAnimation.getAnimationPlayer();
+            animator.setInterpolator(EMPHASIZED);
+            animator.addListener(AnimatorListeners.forEndCallback(
                     () -> mAllAppsCallbacks.onAllAppsTransitionEnd(true)));
-            mOpenCloseAnimator.setDuration(mAllAppsCallbacks.getOpenDuration()).start();
+            animator.start();
         } else {
             mTranslationShift = TRANSLATION_SHIFT_OPENED;
             mAllAppsCallbacks.onAllAppsTransitionEnd(true);
diff --git a/src/com/android/launcher3/views/AbstractSlideInView.java b/src/com/android/launcher3/views/AbstractSlideInView.java
index f69d299..30e0971 100644
--- a/src/com/android/launcher3/views/AbstractSlideInView.java
+++ b/src/com/android/launcher3/views/AbstractSlideInView.java
@@ -17,6 +17,7 @@
 
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 
+import static com.android.app.animation.Interpolators.LINEAR;
 import static com.android.app.animation.Interpolators.scrollInterpolatorForVelocity;
 import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
 import static com.android.launcher3.LauncherAnimUtils.SUCCESS_TRANSITION_PROGRESS;
@@ -24,16 +25,14 @@
 import static com.android.launcher3.allapps.AllAppsTransitionController.REVERT_SWIPE_ALL_APPS_TO_HOME_ANIMATION_DURATION_MS;
 import static com.android.launcher3.util.ScrollableLayoutManager.PREDICTIVE_BACK_MIN_SCALE;
 
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
+import android.animation.ValueAnimator;
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Outline;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.util.AttributeSet;
-import android.util.Property;
+import android.util.FloatProperty;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
@@ -51,6 +50,8 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.AnimatedFloat;
 import com.android.launcher3.anim.AnimatorListeners;
+import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.touch.BaseSwipeDetector;
 import com.android.launcher3.touch.SingleAxisSwipeDetector;
 
@@ -66,8 +67,8 @@
 public abstract class AbstractSlideInView<T extends Context & ActivityContext>
         extends AbstractFloatingView implements SingleAxisSwipeDetector.Listener {
 
-    protected static final Property<AbstractSlideInView, Float> TRANSLATION_SHIFT =
-            new Property<AbstractSlideInView, Float>(Float.class, "translationShift") {
+    protected static final FloatProperty<AbstractSlideInView<?>> TRANSLATION_SHIFT =
+            new FloatProperty<>("translationShift") {
 
                 @Override
                 public Float get(AbstractSlideInView view) {
@@ -75,31 +76,54 @@
                 }
 
                 @Override
-                public void set(AbstractSlideInView view, Float value) {
+                public void setValue(AbstractSlideInView view, float value) {
                     view.setTranslationShift(value);
                 }
             };
     protected static final float TRANSLATION_SHIFT_CLOSED = 1f;
     protected static final float TRANSLATION_SHIFT_OPENED = 0f;
     private static final float VIEW_NO_SCALE = 1f;
-    private static final int NO_DURATION = -1;
+    private static final int DEFAULT_DURATION = 300;
 
     protected final T mActivityContext;
 
     protected final SingleAxisSwipeDetector mSwipeDetector;
-    protected @NonNull AnimatorSet mOpenCloseAnimator;
-    private final ObjectAnimator mTranslationShiftAnimator;
+    protected @NonNull AnimatorPlaybackController mOpenCloseAnimation;
 
     protected ViewGroup mContent;
     protected final View mColorScrim;
 
+    /**
+     * Interpolator for {@link #mOpenCloseAnimation} when we are closing due to dragging downwards.
+     */
     private Interpolator mScrollInterpolator;
     private long mScrollDuration;
+    /**
+     * End progress for {@link #mOpenCloseAnimation} when we are closing due to dragging downloads.
+     * <p>
+     * There are two cases that determine this value:
+     * <ol>
+     *     <li>
+     *         If the drag interrupts the opening transition (i.e. {@link #mToTranslationShift}
+     *         is {@link #TRANSLATION_SHIFT_OPENED}), we need to animate back to {@code 0} to
+     *         reverse the animation that was paused at {@link #onDragStart(boolean, float)}.
+     *     </li>
+     *     <li>
+     *         If the drag started after the view is fully opened (i.e.
+     *         {@link #mToTranslationShift} is {@link #TRANSLATION_SHIFT_CLOSED}), the animation
+     *         that was set up at {@link #onDragStart(boolean, float)} for closing the view
+     *         should go forward to {@code 1}.
+     *     </li>
+     * </ol>
+     */
+    private float mScrollEndProgress;
 
     // range [0, 1], 0=> completely open, 1=> completely closed
     protected float mTranslationShift = TRANSLATION_SHIFT_CLOSED;
-    /** {@link #mTranslationShift} at the invocation of {@link #onDragStart(boolean, float)}. */
-    protected float mDragStartTranslationShift;
+    protected float mFromTranslationShift;
+    protected float mToTranslationShift;
+    /** {@link #mOpenCloseAnimation} progress at {@link #onDragStart(boolean, float)}. */
+    private float mDragStartProgress;
 
     protected boolean mNoIntercept;
     protected @Nullable OnCloseListener mOnCloseBeginListener;
@@ -128,52 +152,78 @@
         mActivityContext = ActivityContext.lookupContext(context);
 
         mScrollInterpolator = Interpolators.SCROLL_CUBIC;
-        mScrollDuration = NO_DURATION;
+        mScrollDuration = DEFAULT_DURATION;
         mSwipeDetector = new SingleAxisSwipeDetector(context, this,
                 SingleAxisSwipeDetector.VERTICAL);
 
-        mOpenCloseAnimator = new AnimatorSet();
-        mTranslationShiftAnimator = ObjectAnimator.ofPropertyValuesHolder(this);
+        mOpenCloseAnimation = new PendingAnimation(0).createPlaybackController();
 
         int scrimColor = getScrimColor(context);
         mColorScrim = scrimColor != -1 ? createColorScrim(context, scrimColor) : null;
     }
 
     /**
-     * Sets up a {@link #mOpenCloseAnimator} for opening with default parameters.
+     * Sets up a {@link #mOpenCloseAnimation} for opening with default parameters.
      *
-     * @see #setUpOpenCloseAnimator(float, Interpolator)
+     * @see #setUpOpenCloseAnimation(float, float, long)
      */
-    protected final AnimatorSet setUpDefaultOpenAnimator() {
-        return setUpOpenCloseAnimator(TRANSLATION_SHIFT_OPENED, Interpolators.FAST_OUT_SLOW_IN);
+    protected final AnimatorPlaybackController setUpDefaultOpenAnimation() {
+        AnimatorPlaybackController animation = setUpOpenCloseAnimation(
+                TRANSLATION_SHIFT_CLOSED, TRANSLATION_SHIFT_OPENED, DEFAULT_DURATION);
+        animation.getAnimationPlayer().setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+        return animation;
     }
 
     /**
-     * Initializes a new {@link #mOpenCloseAnimator}.
-     * <p>
-     * Subclasses should override this method if they want to add more {@code Animator} instances
-     * to the set.
+     * Sets up a {@link #mOpenCloseAnimation} for opening with a given duration.
      *
-     * @param translationShift             translation shift to animate to.
-     * @param translationShiftInterpolator interpolator for {@link #mTranslationShiftAnimator}.
-     * @return {@link #mOpenCloseAnimator}
+     * @see #setUpOpenCloseAnimation(float, float, long)
      */
-    protected AnimatorSet setUpOpenCloseAnimator(
-            float translationShift, Interpolator translationShiftInterpolator) {
-        mOpenCloseAnimator = new AnimatorSet();
-        mOpenCloseAnimator.addListener(AnimatorListeners.forEndCallback(() -> {
+    protected final AnimatorPlaybackController setUpOpenAnimation(long duration) {
+        return setUpOpenCloseAnimation(
+                TRANSLATION_SHIFT_CLOSED, TRANSLATION_SHIFT_OPENED, duration);
+    }
+
+    private AnimatorPlaybackController setUpCloseAnimation(long duration) {
+        return setUpOpenCloseAnimation(
+                TRANSLATION_SHIFT_OPENED, TRANSLATION_SHIFT_CLOSED, duration);
+    }
+
+    /**
+     * Initializes a new {@link #mOpenCloseAnimation}.
+     *
+     * @param fromTranslationShift translation shift to animate from.
+     * @param toTranslationShift   translation shift to animate to.
+     * @param duration             animation duration.
+     * @return {@link #mOpenCloseAnimation}
+     */
+    private AnimatorPlaybackController setUpOpenCloseAnimation(
+            float fromTranslationShift, float toTranslationShift, long duration) {
+        mFromTranslationShift = fromTranslationShift;
+        mToTranslationShift = toTranslationShift;
+
+        PendingAnimation animation = new PendingAnimation(duration);
+        animation.addEndListener(b -> {
             mSwipeDetector.finishedScrolling();
             announceAccessibilityChanges();
-        }));
+        });
 
-        mTranslationShiftAnimator.setValues(PropertyValuesHolder.ofFloat(
-                TRANSLATION_SHIFT, translationShift));
-        mTranslationShiftAnimator.setInterpolator(translationShiftInterpolator);
-        mOpenCloseAnimator.play(mTranslationShiftAnimator);
+        animation.addFloat(
+                this, TRANSLATION_SHIFT, fromTranslationShift, toTranslationShift, LINEAR);
+        onOpenCloseAnimationPending(animation);
 
-        return mOpenCloseAnimator;
+        mOpenCloseAnimation = animation.createPlaybackController();
+        return mOpenCloseAnimation;
     }
 
+    /**
+     * Invoked when a {@link #mOpenCloseAnimation} is being set up.
+     * <p>
+     * Subclasses can override this method to modify the animation before it's used to create a
+     * {@link AnimatorPlaybackController}.
+     */
+    protected void onOpenCloseAnimationPending(PendingAnimation animation) {}
+
     protected void attachToContainer() {
         if (mColorScrim != null) {
             getPopupContainer().addView(mColorScrim);
@@ -316,29 +366,33 @@
     }
 
     private boolean isOpeningAnimationRunning() {
-        return mIsOpen && mOpenCloseAnimator.isRunning();
+        return mIsOpen && mOpenCloseAnimation.getAnimationPlayer().isRunning();
     }
 
     /* SingleAxisSwipeDetector.Listener */
 
     @Override
     public void onDragStart(boolean start, float startDisplacement) {
-        mOpenCloseAnimator.cancel();
-        mDragStartTranslationShift = mTranslationShift;
+        if (mOpenCloseAnimation.getAnimationPlayer().isRunning()) {
+            mOpenCloseAnimation.pause();
+            mDragStartProgress = mOpenCloseAnimation.getProgressFraction();
+        } else {
+            setUpCloseAnimation(DEFAULT_DURATION);
+            mDragStartProgress = 0;
+        }
     }
 
     @Override
     public boolean onDrag(float displacement) {
-        setTranslationShift(Utilities.boundToRange(
-                mDragStartTranslationShift + displacement / getShiftRange(),
-                TRANSLATION_SHIFT_OPENED,
-                TRANSLATION_SHIFT_CLOSED));
+        float progress = mDragStartProgress
+                + Math.signum(mToTranslationShift - mFromTranslationShift)
+                * (displacement / getShiftRange());
+        mOpenCloseAnimation.setPlayFraction(Utilities.boundToRange(progress, 0, 1));
         return true;
     }
 
     @Override
     public void onDragEnd(float velocity) {
-        mDragStartTranslationShift = 0;
         float successfulShiftThreshold = mActivityContext.getDeviceProfile().isTablet
                 ? TABLET_BOTTOM_SHEET_SUCCESS_TRANSITION_PROGRESS : SUCCESS_TRANSITION_PROGRESS;
         if ((mSwipeDetector.isFling(velocity) && velocity > 0)
@@ -346,10 +400,15 @@
             mScrollInterpolator = scrollInterpolatorForVelocity(velocity);
             mScrollDuration = BaseSwipeDetector.calculateDuration(
                     velocity, TRANSLATION_SHIFT_CLOSED - mTranslationShift);
+            mScrollEndProgress = mToTranslationShift == TRANSLATION_SHIFT_OPENED ? 0 : 1;
             close(true);
         } else {
-            setUpOpenCloseAnimator(TRANSLATION_SHIFT_OPENED, Interpolators.DECELERATE)
-                    .setDuration(BaseSwipeDetector.calculateDuration(velocity, mTranslationShift))
+            ValueAnimator animator = mOpenCloseAnimation.getAnimationPlayer();
+            animator.setInterpolator(Interpolators.DECELERATE);
+            animator.setFloatValues(
+                    mOpenCloseAnimation.getProgressFraction(),
+                    mToTranslationShift == TRANSLATION_SHIFT_OPENED ? 1 : 0);
+            animator.setDuration(BaseSwipeDetector.calculateDuration(velocity, mTranslationShift))
                     .start();
         }
     }
@@ -371,24 +430,27 @@
         Optional.ofNullable(mOnCloseBeginListener).ifPresent(OnCloseListener::onSlideInViewClosed);
 
         if (!animate) {
-            mOpenCloseAnimator.cancel();
+            mOpenCloseAnimation.pause();
             setTranslationShift(TRANSLATION_SHIFT_CLOSED);
             onCloseComplete();
             return;
         }
 
-        final Interpolator interpolator;
-        final long duration;
+        final ValueAnimator animator;
         if (mSwipeDetector.isIdleState()) {
-            interpolator = getIdleInterpolator();
-            duration = defaultDuration;
+            setUpCloseAnimation(defaultDuration);
+            animator = mOpenCloseAnimation.getAnimationPlayer();
+            animator.setInterpolator(getIdleInterpolator());
         } else {
-            interpolator = mScrollInterpolator;
-            duration = mScrollDuration > NO_DURATION ? mScrollDuration : defaultDuration;
+            animator = mOpenCloseAnimation.getAnimationPlayer();
+            animator.setInterpolator(mScrollInterpolator);
+            animator.setDuration(mScrollDuration);
+            mOpenCloseAnimation.getAnimationPlayer().setFloatValues(
+                    mOpenCloseAnimation.getProgressFraction(), mScrollEndProgress);
         }
-        setUpOpenCloseAnimator(TRANSLATION_SHIFT_CLOSED, interpolator)
-                .addListener(AnimatorListeners.forEndCallback(this::onCloseComplete));
-        mOpenCloseAnimator.setDuration(duration).start();
+
+        animator.addListener(AnimatorListeners.forEndCallback(this::onCloseComplete));
+        animator.start();
     }
 
     protected Interpolator getIdleInterpolator() {
diff --git a/src/com/android/launcher3/views/WidgetsEduView.java b/src/com/android/launcher3/views/WidgetsEduView.java
index 92e048b..e70b1cb 100644
--- a/src/com/android/launcher3/views/WidgetsEduView.java
+++ b/src/com/android/launcher3/views/WidgetsEduView.java
@@ -116,11 +116,11 @@
     }
 
     private void animateOpen() {
-        if (mIsOpen || mOpenCloseAnimator.isRunning()) {
+        if (mIsOpen || mOpenCloseAnimation.getAnimationPlayer().isRunning()) {
             return;
         }
         mIsOpen = true;
-        setUpDefaultOpenAnimator().start();
+        setUpDefaultOpenAnimation().start();
     }
 
     /** Shows widget education dialog. */
diff --git a/src/com/android/launcher3/widget/AddItemWidgetsBottomSheet.java b/src/com/android/launcher3/widget/AddItemWidgetsBottomSheet.java
index c6fc5fe..80b1cdd 100644
--- a/src/com/android/launcher3/widget/AddItemWidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/AddItemWidgetsBottomSheet.java
@@ -128,11 +128,11 @@
     }
 
     private void animateOpen() {
-        if (mIsOpen || mOpenCloseAnimator.isRunning()) {
+        if (mIsOpen || mOpenCloseAnimation.getAnimationPlayer().isRunning()) {
             return;
         }
         mIsOpen = true;
-        setUpDefaultOpenAnimator().start();
+        setUpDefaultOpenAnimation().start();
     }
 
     @Override
diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
index 82394f1..c347939 100644
--- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
@@ -224,12 +224,12 @@
     }
 
     private void animateOpen() {
-        if (mIsOpen || mOpenCloseAnimator.isRunning()) {
+        if (mIsOpen || mOpenCloseAnimation.getAnimationPlayer().isRunning()) {
             return;
         }
         mIsOpen = true;
         setupNavBarColor();
-        setUpDefaultOpenAnimator().start();
+        setUpDefaultOpenAnimation().start();
     }
 
     @Override
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index 43f1846..abca1f8 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -22,6 +22,7 @@
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGETSTRAY_SEARCHED;
 import static com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL;
 
+import android.animation.Animator;
 import android.content.Context;
 import android.content.pm.LauncherApps;
 import android.content.res.Configuration;
@@ -624,13 +625,12 @@
                 mContent.setAlpha(0);
                 setTranslationShift(VERTICAL_START_POSITION);
             }
-            setUpOpenCloseAnimator(
-                    TRANSLATION_SHIFT_OPENED,
-                    AnimationUtils.loadInterpolator(
-                            getContext(), android.R.interpolator.linear_out_slow_in));
+            setUpOpenAnimation(mActivityContext.getDeviceProfile().bottomSheetOpenDuration);
+            Animator animator = mOpenCloseAnimation.getAnimationPlayer();
+            animator.setInterpolator(AnimationUtils.loadInterpolator(
+                    getContext(), android.R.interpolator.linear_out_slow_in));
             post(() -> {
-                mOpenCloseAnimator
-                        .setDuration(mActivityContext.getDeviceProfile().bottomSheetOpenDuration)
+                animator.setDuration(mActivityContext.getDeviceProfile().bottomSheetOpenDuration)
                         .start();
                 mContent.animate().alpha(1).setDuration(FADE_IN_DURATION);
             });