Merge "Back open-close animation with an AnimatorSet." into udc-qpr-dev
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
index bd47923..5691ecf 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
@@ -19,7 +19,6 @@
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_EDU_DENY;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_EDU_SEEN;
 
-import android.animation.PropertyValuesHolder;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.Rect;
@@ -30,7 +29,6 @@
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
-import com.android.app.animation.Interpolators;
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.CellLayout;
 import com.android.launcher3.DeviceProfile;
@@ -164,10 +162,7 @@
             return;
         }
         mIsOpen = true;
-        mOpenCloseAnimator.setValues(
-                PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
-        mOpenCloseAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
-        mOpenCloseAnimator.start();
+        setUpDefaultOpenAnimator().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 4f75ef5..3fe7359 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
@@ -17,9 +17,6 @@
 
 import static com.android.app.animation.Interpolators.EMPHASIZED;
 
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.PropertyValuesHolder;
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Rect;
@@ -31,6 +28,7 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Insettable;
 import com.android.launcher3.R;
+import com.android.launcher3.anim.AnimatorListeners;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.taskbar.allapps.TaskbarAllAppsViewController.TaskbarAllAppsCallbacks;
 import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
@@ -68,16 +66,9 @@
         mAllAppsCallbacks.onAllAppsTransitionStart(true);
 
         if (animate) {
-            mOpenCloseAnimator.setValues(
-                    PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
-            mOpenCloseAnimator.setInterpolator(EMPHASIZED);
-            mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    mOpenCloseAnimator.removeListener(this);
-                    mAllAppsCallbacks.onAllAppsTransitionEnd(true);
-                }
-            });
+            setUpOpenCloseAnimator(TRANSLATION_SHIFT_OPENED, EMPHASIZED);
+            mOpenCloseAnimator.addListener(AnimatorListeners.forEndCallback(
+                    () -> mAllAppsCallbacks.onAllAppsTransitionEnd(true)));
             mOpenCloseAnimator.setDuration(mAllAppsCallbacks.getOpenDuration()).start();
         } else {
             mTranslationShift = TRANSLATION_SHIFT_OPENED;
diff --git a/src/com/android/launcher3/views/AbstractSlideInView.java b/src/com/android/launcher3/views/AbstractSlideInView.java
index de10fc5..f69d299 100644
--- a/src/com/android/launcher3/views/AbstractSlideInView.java
+++ b/src/com/android/launcher3/views/AbstractSlideInView.java
@@ -24,8 +24,7 @@
 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.Animator;
-import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
 import android.content.Context;
@@ -51,6 +50,7 @@
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.AnimatedFloat;
+import com.android.launcher3.anim.AnimatorListeners;
 import com.android.launcher3.touch.BaseSwipeDetector;
 import com.android.launcher3.touch.SingleAxisSwipeDetector;
 
@@ -82,15 +82,19 @@
     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;
 
     protected final T mActivityContext;
 
     protected final SingleAxisSwipeDetector mSwipeDetector;
-    protected final ObjectAnimator mOpenCloseAnimator;
+    protected @NonNull AnimatorSet mOpenCloseAnimator;
+    private final ObjectAnimator mTranslationShiftAnimator;
 
     protected ViewGroup mContent;
     protected final View mColorScrim;
-    protected Interpolator mScrollInterpolator;
+
+    private Interpolator mScrollInterpolator;
+    private long mScrollDuration;
 
     // range [0, 1], 0=> completely open, 1=> completely closed
     protected float mTranslationShift = TRANSLATION_SHIFT_CLOSED;
@@ -104,8 +108,8 @@
     protected final AnimatedFloat mSlideInViewScale =
             new AnimatedFloat(this::onScaleProgressChanged, VIEW_NO_SCALE);
     protected boolean mIsBackProgressing;
-    @Nullable private Drawable mContentBackground;
-    @Nullable private View mContentBackgroundParentView;
+    private @Nullable Drawable mContentBackground;
+    private @Nullable View mContentBackgroundParentView;
 
     protected final ViewOutlineProvider mViewOutlineProvider = new ViewOutlineProvider() {
         @Override
@@ -124,21 +128,52 @@
         mActivityContext = ActivityContext.lookupContext(context);
 
         mScrollInterpolator = Interpolators.SCROLL_CUBIC;
+        mScrollDuration = NO_DURATION;
         mSwipeDetector = new SingleAxisSwipeDetector(context, this,
                 SingleAxisSwipeDetector.VERTICAL);
 
-        mOpenCloseAnimator = ObjectAnimator.ofPropertyValuesHolder(this);
-        mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                mSwipeDetector.finishedScrolling();
-                announceAccessibilityChanges();
-            }
-        });
+        mOpenCloseAnimator = new AnimatorSet();
+        mTranslationShiftAnimator = ObjectAnimator.ofPropertyValuesHolder(this);
+
         int scrimColor = getScrimColor(context);
         mColorScrim = scrimColor != -1 ? createColorScrim(context, scrimColor) : null;
     }
 
+    /**
+     * Sets up a {@link #mOpenCloseAnimator} for opening with default parameters.
+     *
+     * @see #setUpOpenCloseAnimator(float, Interpolator)
+     */
+    protected final AnimatorSet setUpDefaultOpenAnimator() {
+        return setUpOpenCloseAnimator(TRANSLATION_SHIFT_OPENED, Interpolators.FAST_OUT_SLOW_IN);
+    }
+
+    /**
+     * Initializes a new {@link #mOpenCloseAnimator}.
+     * <p>
+     * Subclasses should override this method if they want to add more {@code Animator} instances
+     * to the set.
+     *
+     * @param translationShift             translation shift to animate to.
+     * @param translationShiftInterpolator interpolator for {@link #mTranslationShiftAnimator}.
+     * @return {@link #mOpenCloseAnimator}
+     */
+    protected AnimatorSet setUpOpenCloseAnimator(
+            float translationShift, Interpolator translationShiftInterpolator) {
+        mOpenCloseAnimator = new AnimatorSet();
+        mOpenCloseAnimator.addListener(AnimatorListeners.forEndCallback(() -> {
+            mSwipeDetector.finishedScrolling();
+            announceAccessibilityChanges();
+        }));
+
+        mTranslationShiftAnimator.setValues(PropertyValuesHolder.ofFloat(
+                TRANSLATION_SHIFT, translationShift));
+        mTranslationShiftAnimator.setInterpolator(translationShiftInterpolator);
+        mOpenCloseAnimator.play(mTranslationShiftAnimator);
+
+        return mOpenCloseAnimator;
+    }
+
     protected void attachToContainer() {
         if (mColorScrim != null) {
             getPopupContainer().addView(mColorScrim);
@@ -309,16 +344,13 @@
         if ((mSwipeDetector.isFling(velocity) && velocity > 0)
                 || mTranslationShift > successfulShiftThreshold) {
             mScrollInterpolator = scrollInterpolatorForVelocity(velocity);
-            mOpenCloseAnimator.setDuration(BaseSwipeDetector.calculateDuration(
-                    velocity, TRANSLATION_SHIFT_CLOSED - mTranslationShift));
+            mScrollDuration = BaseSwipeDetector.calculateDuration(
+                    velocity, TRANSLATION_SHIFT_CLOSED - mTranslationShift);
             close(true);
         } else {
-            mOpenCloseAnimator.setValues(PropertyValuesHolder.ofFloat(
-                    TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
-            mOpenCloseAnimator.setDuration(
-                    BaseSwipeDetector.calculateDuration(velocity, mTranslationShift))
-                    .setInterpolator(Interpolators.DECELERATE);
-            mOpenCloseAnimator.start();
+            setUpOpenCloseAnimator(TRANSLATION_SHIFT_OPENED, Interpolators.DECELERATE)
+                    .setDuration(BaseSwipeDetector.calculateDuration(velocity, mTranslationShift))
+                    .start();
         }
     }
 
@@ -344,23 +376,19 @@
             onCloseComplete();
             return;
         }
-        mOpenCloseAnimator.setValues(
-                PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_CLOSED));
-        mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                mOpenCloseAnimator.removeListener(this);
-                onCloseComplete();
-            }
-        });
+
+        final Interpolator interpolator;
+        final long duration;
         if (mSwipeDetector.isIdleState()) {
-            mOpenCloseAnimator
-                    .setDuration(defaultDuration)
-                    .setInterpolator(getIdleInterpolator());
+            interpolator = getIdleInterpolator();
+            duration = defaultDuration;
         } else {
-            mOpenCloseAnimator.setInterpolator(mScrollInterpolator);
+            interpolator = mScrollInterpolator;
+            duration = mScrollDuration > NO_DURATION ? mScrollDuration : defaultDuration;
         }
-        mOpenCloseAnimator.start();
+        setUpOpenCloseAnimator(TRANSLATION_SHIFT_CLOSED, interpolator)
+                .addListener(AnimatorListeners.forEndCallback(this::onCloseComplete));
+        mOpenCloseAnimator.setDuration(duration).start();
     }
 
     protected Interpolator getIdleInterpolator() {
diff --git a/src/com/android/launcher3/views/WidgetsEduView.java b/src/com/android/launcher3/views/WidgetsEduView.java
index 9180781..92e048b 100644
--- a/src/com/android/launcher3/views/WidgetsEduView.java
+++ b/src/com/android/launcher3/views/WidgetsEduView.java
@@ -15,9 +15,6 @@
  */
 package com.android.launcher3.views;
 
-import static com.android.app.animation.Interpolators.FAST_OUT_SLOW_IN;
-
-import android.animation.PropertyValuesHolder;
 import android.content.Context;
 import android.graphics.Rect;
 import android.util.AttributeSet;
@@ -123,10 +120,7 @@
             return;
         }
         mIsOpen = true;
-        mOpenCloseAnimator.setValues(
-                PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
-        mOpenCloseAnimator.setInterpolator(FAST_OUT_SLOW_IN);
-        mOpenCloseAnimator.start();
+        setUpDefaultOpenAnimator().start();
     }
 
     /** Shows widget education dialog. */
diff --git a/src/com/android/launcher3/widget/AddItemWidgetsBottomSheet.java b/src/com/android/launcher3/widget/AddItemWidgetsBottomSheet.java
index 473abf1..c6fc5fe 100644
--- a/src/com/android/launcher3/widget/AddItemWidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/AddItemWidgetsBottomSheet.java
@@ -16,10 +16,8 @@
 
 package com.android.launcher3.widget;
 
-import static com.android.app.animation.Interpolators.FAST_OUT_SLOW_IN;
 import static com.android.launcher3.Utilities.ATLEAST_R;
 
-import android.animation.PropertyValuesHolder;
 import android.annotation.SuppressLint;
 import android.content.Context;
 import android.graphics.Insets;
@@ -134,10 +132,7 @@
             return;
         }
         mIsOpen = true;
-        mOpenCloseAnimator.setValues(
-                PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
-        mOpenCloseAnimator.setInterpolator(FAST_OUT_SLOW_IN);
-        mOpenCloseAnimator.start();
+        setUpDefaultOpenAnimator().start();
     }
 
     @Override
diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
index 93f7cb3..82394f1 100644
--- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
@@ -16,10 +16,8 @@
 
 package com.android.launcher3.widget;
 
-import static com.android.app.animation.Interpolators.FAST_OUT_SLOW_IN;
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_BOTTOM_WIDGETS_TRAY;
 
-import android.animation.PropertyValuesHolder;
 import android.content.Context;
 import android.graphics.Rect;
 import android.util.AttributeSet;
@@ -231,10 +229,7 @@
         }
         mIsOpen = true;
         setupNavBarColor();
-        mOpenCloseAnimator.setValues(
-                PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
-        mOpenCloseAnimator.setInterpolator(FAST_OUT_SLOW_IN);
-        mOpenCloseAnimator.start();
+        setUpDefaultOpenAnimator().start();
     }
 
     @Override
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index c9cd4b6..43f1846 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -22,9 +22,6 @@
 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.animation.AnimatorListenerAdapter;
-import android.animation.PropertyValuesHolder;
 import android.content.Context;
 import android.content.pm.LauncherApps;
 import android.content.res.Configuration;
@@ -627,20 +624,14 @@
                 mContent.setAlpha(0);
                 setTranslationShift(VERTICAL_START_POSITION);
             }
-            mOpenCloseAnimator.setValues(
-                    PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
-            mOpenCloseAnimator
-                    .setDuration(mActivityContext.getDeviceProfile().bottomSheetOpenDuration)
-                    .setInterpolator(AnimationUtils.loadInterpolator(
+            setUpOpenCloseAnimator(
+                    TRANSLATION_SHIFT_OPENED,
+                    AnimationUtils.loadInterpolator(
                             getContext(), android.R.interpolator.linear_out_slow_in));
-            mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    mOpenCloseAnimator.removeListener(this);
-                }
-            });
             post(() -> {
-                mOpenCloseAnimator.start();
+                mOpenCloseAnimator
+                        .setDuration(mActivityContext.getDeviceProfile().bottomSheetOpenDuration)
+                        .start();
                 mContent.animate().alpha(1).setDuration(FADE_IN_DURATION);
             });
         } else {