Refactor Folder/FolderAnimationManager code.

* FolderAnimationManager is now lazy loaded.
* Extract methods to simplify code.

Bug: 35064148

Change-Id: I08a72ef19a1f21637f033059d5e228ef67135ccd
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 6262231..94b17c3 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -135,7 +135,7 @@
 
     @Thunk final ArrayList<View> mItemsInReadingOrder = new ArrayList<View>();
 
-    private FolderAnimationManager mFolderAnimationManager;
+    private AnimatorSet mCurrentAnimator;
 
     private final int mExpandDuration;
     public final int mMaterialExpandDuration;
@@ -479,8 +479,6 @@
                 }
             }
         });
-
-        mFolderAnimationManager = new FolderAnimationManager(this);
     }
 
     /**
@@ -516,7 +514,30 @@
         mState = STATE_SMALL;
     }
 
-    private AnimatorSet getOpeningAnimatorSet() {
+    private void startAnimation(final AnimatorSet a) {
+        long startTime = 0;
+        if (mCurrentAnimator != null && mCurrentAnimator.isRunning()) {
+            // This allows a nice transition when closing a Folder while it is still animating open.
+            startTime = mCurrentAnimator.getDuration() - mCurrentAnimator.getCurrentPlayTime();
+            mCurrentAnimator.cancel();
+        }
+        a.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                mState = STATE_ANIMATING;
+                mCurrentAnimator = a;
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mCurrentAnimator = null;
+            }
+        });
+        a.setCurrentPlayTime(startTime);
+        a.start();
+    }
+
+    private AnimatorSet getOpeningAnimator() {
         prepareReveal();
         mFolderIcon.growAndFadeOut();
 
@@ -613,8 +634,8 @@
         centerAboutIcon();
 
         AnimatorSet anim = FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION
-                ? mFolderAnimationManager.getOpeningAnimator()
-                : getOpeningAnimatorSet();
+                ? new FolderAnimationManager(this, true /* isOpening */).getAnimator()
+                : getOpeningAnimator();
         onCompleteRunnable = new Runnable() {
             @Override
             public void run() {
@@ -624,11 +645,14 @@
         anim.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationStart(Animator animation) {
+                if (FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION) {
+                    mFolderIcon.setVisibility(INVISIBLE);
+                }
+
                 Utilities.sendCustomAccessibilityEvent(
                         Folder.this,
                         AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
                         mContent.getAccessibilityDescription());
-                mState = STATE_ANIMATING;
             }
             @Override
             public void onAnimationEnd(Animator animation) {
@@ -680,7 +704,7 @@
         }
 
         mPageIndicator.stopAllAnimations();
-        anim.start();
+        startAnimation(anim);
 
         // Make sure the folder picks up the last drag move even if the finger doesn't move.
         if (mDragController.isDragging()) {
@@ -736,7 +760,7 @@
         parent.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
     }
 
-    private AnimatorSet getClosingAnimatorSet() {
+    private AnimatorSet getClosingAnimator() {
         AnimatorSet animatorSet = LauncherAnimUtils.createAnimatorSet();
         animatorSet.play(LauncherAnimUtils.ofViewAlphaAndScale(this, 0, 0.9f, 0.9f));
 
@@ -749,8 +773,8 @@
 
     private void animateClosed() {
         AnimatorSet a = FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION
-                ? mFolderAnimationManager.getClosingAnimator()
-                : getClosingAnimatorSet();
+                ? new FolderAnimationManager(this, false /* isOpening */).getAnimator()
+                : getClosingAnimator();
         a.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
@@ -762,10 +786,9 @@
                         Folder.this,
                         AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
                         getContext().getString(R.string.folder_closed));
-                mState = STATE_ANIMATING;
             }
         });
-        a.start();
+        startAnimation(a);
     }
 
     private void closeComplete(boolean wasAnimated) {
diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java
index 6ce572d..c1c974c 100644
--- a/src/com/android/launcher3/folder/FolderAnimationManager.java
+++ b/src/com/android/launcher3/folder/FolderAnimationManager.java
@@ -62,7 +62,8 @@
     private Context mContext;
     private Launcher mLauncher;
 
-    private Animator mRevealAnimator;
+    private final boolean mIsOpening;
+
     private final TimeInterpolator mOpeningInterpolator;
     private final TimeInterpolator mClosingInterpolator;
     private final TimeInterpolator mPreviewItemOpeningInterpolator;
@@ -102,7 +103,7 @@
                 }
             };
 
-    public FolderAnimationManager(Folder folder) {
+    public FolderAnimationManager(Folder folder, boolean isOpening) {
         mFolder = folder;
         mContent = folder.mContent;
         mFolderBackground = (GradientDrawable) mFolder.getBackground();
@@ -113,6 +114,8 @@
         mContext = folder.getContext();
         mLauncher = folder.mLauncher;
 
+        mIsOpening = isOpening;
+
         mOpeningInterpolator = AnimationUtils.loadInterpolator(mContext,
                 R.interpolator.folder_opening_interpolator);
         mClosingInterpolator = AnimationUtils.loadInterpolator(mContext,
@@ -123,31 +126,11 @@
                 R.interpolator.folder_preview_item_closing_interpolator);
     }
 
-    public AnimatorSet getOpeningAnimator() {
-        mFolder.setPivotX(0);
-        mFolder.setPivotY(0);
-
-        AnimatorSet a = getAnimatorSet(true /* isOpening */);
-        a.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationStart(Animator animation) {
-                mFolderIcon.setVisibility(View.INVISIBLE);
-            }
-        });
-        return a;
-    }
-
-    public AnimatorSet getClosingAnimator() {
-        AnimatorSet a = getAnimatorSet(false /* isOpening */);
-        return a;
-    }
 
     /**
      * Prepares the Folder for animating between open / closed states.
-     *
-     * @param isOpening If true, return the animator set for the opening animation.
      */
-    private AnimatorSet getAnimatorSet(final boolean isOpening) {
+    public AnimatorSet getAnimator() {
         final DragLayer.LayoutParams lp = (DragLayer.LayoutParams) mFolder.getLayoutParams();
         FolderIcon.PreviewLayoutRule rule = mFolderIcon.getLayoutRule();
         final List<BubbleTextView> itemsInPreview = mFolderIcon.getItemsToDisplay();
@@ -159,9 +142,11 @@
 
         final float initialScale = folderScale;
         final float finalScale = 1f;
-        float scale = isOpening ? initialScale : finalScale;
+        float scale = mIsOpening ? initialScale : finalScale;
         mFolder.setScaleX(scale);
         mFolder.setScaleY(scale);
+        mFolder.setPivotX(0);
+        mFolder.setPivotY(0);
 
         // Match position of the FolderIcon
         final Rect folderIconPos = new Rect();
@@ -189,45 +174,14 @@
         final int finalColor = Themes.getAttrColor(mContext, android.R.attr.colorPrimary);
         final int initialColor =
                 ColorUtils.setAlphaComponent(finalColor, mPreviewBackground.getBackgroundAlpha());
-        mFolderBackground.setColor(isOpening ? initialColor : finalColor);
+        mFolderBackground.setColor(mIsOpening ? initialColor : finalColor);
 
         // Initialize the Folder items' text.
-        final List<BubbleTextView> itemsOnCurrentPage = mFolder.getItemsOnCurrentPage();
+        final List<BubbleTextView> items = mFolder.getItemsOnCurrentPage();
         final int finalTextColor = Themes.getAttrColor(mContext, android.R.attr.textColorSecondary);
-        ITEMS_TEXT_COLOR_PROPERTY.set(itemsOnCurrentPage, isOpening ? Color.TRANSPARENT
+        ITEMS_TEXT_COLOR_PROPERTY.set(items, mIsOpening ? Color.TRANSPARENT
                 : finalTextColor);
 
-        // Create the animators.
-        AnimatorSet a = LauncherAnimUtils.createAnimatorSet();
-        a.setDuration(mFolder.mMaterialExpandDuration);
-
-        ObjectAnimator translationX = isOpening
-                ? ObjectAnimator.ofFloat(mFolder, View.TRANSLATION_X, xDistance, 0)
-                : ObjectAnimator.ofFloat(mFolder, View.TRANSLATION_X, 0, xDistance);
-        a.play(translationX);
-
-        ObjectAnimator translationY = isOpening
-                ? ObjectAnimator.ofFloat(mFolder, View.TRANSLATION_Y, yDistance, 0)
-                : ObjectAnimator.ofFloat(mFolder, View.TRANSLATION_Y, 0, yDistance);
-        a.play(translationY);
-
-        ObjectAnimator scaleAnimator = isOpening
-                ? ObjectAnimator.ofFloat(mFolder, SCALE_PROPERTY, initialScale, finalScale)
-                : ObjectAnimator.ofFloat(mFolder, SCALE_PROPERTY, finalScale, initialScale);
-        a.play(scaleAnimator);
-
-        ObjectAnimator itemsTextColor = isOpening
-                ? ObjectAnimator.ofArgb(itemsOnCurrentPage, ITEMS_TEXT_COLOR_PROPERTY,
-                        Color.TRANSPARENT, finalTextColor)
-                : ObjectAnimator.ofArgb(itemsOnCurrentPage, ITEMS_TEXT_COLOR_PROPERTY,
-                        finalTextColor, Color.TRANSPARENT);
-        a.play(itemsTextColor);
-
-        ObjectAnimator backgroundColor = isOpening
-                ? ObjectAnimator.ofArgb(mFolderBackground, "color", initialColor, finalColor)
-                : ObjectAnimator.ofArgb(mFolderBackground, "color", finalColor, initialColor);
-        a.play(backgroundColor);
-
         // Set up the reveal animation that clips the Folder.
         float stroke = mPreviewBackground.getStrokeWidth();
         int initialSize = (int) ((mFolderIcon.mBackground.getRadius() * 2 + stroke) / folderScale);
@@ -236,50 +190,52 @@
         Rect startRect = new Rect(totalOffsetX + unscaledStroke, unscaledStroke,
                 totalOffsetX + initialSize, initialSize);
         Rect endRect = new Rect(0, 0, lp.width, lp.height);
-        a.play(getRevealAnimator(isOpening, initialSize / 2f, startRect, endRect));
+        float finalRadius = Utilities.pxFromDp(2, mContext.getResources().getDisplayMetrics());
+
+        // Create the animators.
+        AnimatorSet a = LauncherAnimUtils.createAnimatorSet();
+        a.setDuration(mFolder.mMaterialExpandDuration);
+
+        a.play(getAnimator(mFolder, View.TRANSLATION_X, xDistance, 0f));
+        a.play(getAnimator(mFolder, View.TRANSLATION_Y, yDistance, 0f));
+        a.play(getAnimator(mFolder, SCALE_PROPERTY, initialScale, finalScale));
+        a.play(getAnimator(items, ITEMS_TEXT_COLOR_PROPERTY, Color.TRANSPARENT, finalTextColor));
+        a.play(getAnimator(mFolderBackground, "color", initialColor, finalColor));
+        a.play(new RoundedRectRevealOutlineProvider(initialSize / 2f, finalRadius, startRect,
+                endRect).createRevealAnimator(mFolder, !mIsOpening));
 
         a.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
                 super.onAnimationEnd(animation);
-                ITEMS_TEXT_COLOR_PROPERTY.set(itemsOnCurrentPage, finalTextColor);
+                ITEMS_TEXT_COLOR_PROPERTY.set(items, finalTextColor);
+                mFolder.setTranslationX(0.0f);
+                mFolder.setTranslationY(0.0f);
+                mFolder.setScaleX(1f);
+                mFolder.setScaleY(1f);
             }
         });
 
         // We set the interpolator on all current child animators here, because the preview item
         // animators may use a different interpolator.
         for (Animator animator : a.getChildAnimations()) {
-            animator.setInterpolator(isOpening ? mOpeningInterpolator : mClosingInterpolator);
+            animator.setInterpolator(mIsOpening ? mOpeningInterpolator : mClosingInterpolator);
         }
 
-        addPreviewItemAnimatorsToSet(a, isOpening, folderScale, nudgeOffsetX);
+        addPreviewItemAnimatorsToSet(a, folderScale, nudgeOffsetX);
         return a;
     }
 
-    private Animator getRevealAnimator(boolean isOpening, float circleRadius, Rect start,
-            Rect end) {
-        boolean revealIsRunning = mRevealAnimator != null && mRevealAnimator.isRunning();
-        final float finalRadius = revealIsRunning
-                ? ((RoundedRectRevealOutlineProvider) mFolder.getOutlineProvider()).getRadius()
-                : Utilities.pxFromDp(2, mContext.getResources().getDisplayMetrics());
-        if (revealIsRunning) {
-            mRevealAnimator.cancel();
-        }
-        mRevealAnimator = new RoundedRectRevealOutlineProvider(circleRadius, finalRadius,
-                start, end).createRevealAnimator(mFolder, !isOpening);
-        return mRevealAnimator;
-    }
-
     /**
      * Animate the items that are displayed in the preview.
      */
-    private void addPreviewItemAnimatorsToSet(AnimatorSet animatorSet, boolean isOpening,
-            final float folderScale, int nudgeOffsetX) {
+    private void addPreviewItemAnimatorsToSet(AnimatorSet animatorSet, final float folderScale,
+            int nudgeOffsetX) {
         FolderIcon.PreviewLayoutRule rule = mFolderIcon.getLayoutRule();
         final List<BubbleTextView> itemsInPreview = mFolderIcon.getItemsToDisplay();
         final int numItemsInPreview = itemsInPreview.size();
 
-        TimeInterpolator previewItemInterpolator = getPreviewItemInterpolator(isOpening);
+        TimeInterpolator previewItemInterpolator = getPreviewItemInterpolator();
 
         ShortcutAndWidgetContainer cwc = mContent.getPageAt(0).getShortcutsAndWidgets();
         for (int i = 0; i < numItemsInPreview; ++i) {
@@ -297,7 +253,7 @@
 
             final float initialScale = iconScale / folderScale;
             final float finalScale = 1f;
-            float scale = isOpening ? initialScale : finalScale;
+            float scale = mIsOpening ? initialScale : finalScale;
             btv.setScaleX(scale);
             btv.setScaleY(scale);
 
@@ -314,21 +270,15 @@
             final float xDistance = previewPosX - btvLp.x;
             final float yDistance = previewPosY - btvLp.y;
 
-            ObjectAnimator translationX = isOpening
-                    ? ObjectAnimator.ofFloat(btv, View.TRANSLATION_X, xDistance, 0)
-                    : ObjectAnimator.ofFloat(btv, View.TRANSLATION_X, 0, xDistance);
+            Animator translationX = getAnimator(btv, View.TRANSLATION_X, xDistance, 0f);
             translationX.setInterpolator(previewItemInterpolator);
             animatorSet.play(translationX);
 
-            ObjectAnimator translationY = isOpening
-                    ? ObjectAnimator.ofFloat(btv, View.TRANSLATION_Y, yDistance, 0)
-                    : ObjectAnimator.ofFloat(btv, View.TRANSLATION_Y, 0, yDistance);
+            Animator translationY = getAnimator(btv, View.TRANSLATION_Y, yDistance, 0f);
             translationY.setInterpolator(previewItemInterpolator);
             animatorSet.play(translationY);
 
-            ObjectAnimator scaleAnimator = isOpening
-                    ? ObjectAnimator.ofFloat(btv, SCALE_PROPERTY, initialScale, finalScale)
-                    : ObjectAnimator.ofFloat(btv, SCALE_PROPERTY, finalScale, initialScale);
+            Animator scaleAnimator = getAnimator(btv, SCALE_PROPERTY, initialScale, finalScale);
             scaleAnimator.setInterpolator(previewItemInterpolator);
             animatorSet.play(scaleAnimator);
 
@@ -345,13 +295,31 @@
         }
     }
 
-    private TimeInterpolator getPreviewItemInterpolator(boolean isOpening) {
+    private TimeInterpolator getPreviewItemInterpolator() {
         if (mFolder.getItemCount() > FolderIcon.NUM_ITEMS_IN_PREVIEW) {
             // With larger folders, we want the preview items to reach their final positions faster
             // (when opening) and later (when closing) so that they appear aligned with the rest of
             // the folder items when they are both visible.
-            return isOpening ? mPreviewItemOpeningInterpolator : mPreviewItemClosingInterpolator;
+            return mIsOpening ? mPreviewItemOpeningInterpolator : mPreviewItemClosingInterpolator;
         }
-        return isOpening ? mOpeningInterpolator : mClosingInterpolator;
+        return mIsOpening ? mOpeningInterpolator : mClosingInterpolator;
+    }
+
+    private Animator getAnimator(View view, Property property, float v1, float v2) {
+        return mIsOpening
+                ? ObjectAnimator.ofFloat(view, property, v1, v2)
+                : ObjectAnimator.ofFloat(view, property, v2, v1);
+    }
+
+    private Animator getAnimator(List<BubbleTextView> items, Property property, int v1, int v2) {
+        return mIsOpening
+                ? ObjectAnimator.ofArgb(items, property, v1, v2)
+                : ObjectAnimator.ofArgb(items, property, v2, v1);
+    }
+
+    private Animator getAnimator(GradientDrawable drawable, String property, int v1, int v2) {
+        return mIsOpening
+                ? ObjectAnimator.ofArgb(drawable, property, v1, v2)
+                : ObjectAnimator.ofArgb(drawable, property, v2, v1);
     }
 }