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);
}
}