Slide in first page preview items in FolderIcon after Folder closes.
If the Folder is not closed on on the first page, we animate the
current page items as if they are in the preview.
When the Folder finishes closing, the first page preview items
slide in as the current page preview items slide out.
Bug: 36022592
Bug: 35064148
Change-Id: I7ede7ed9e091e2a0c3cbe11731744bfe9ce36595
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index afbf9f2..3184d6d 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -791,12 +791,14 @@
if (mFolderIcon != null) {
mFolderIcon.setVisibility(View.VISIBLE);
if (FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION) {
- mFolderIcon.mFolderName.setTextVisibility(true);
mFolderIcon.setBackgroundVisible(true);
- mFolderIcon.mBackground.fadeInBackgroundShadow();
}
if (wasAnimated) {
- mFolderIcon.mBackground.animateBackgroundStroke();
+ if (FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION) {
+ mFolderIcon.mBackground.fadeInBackgroundShadow();
+ mFolderIcon.mBackground.animateBackgroundStroke();
+ mFolderIcon.onFolderClose(mContent.getCurrentPage());
+ }
if (mFolderIcon.hasBadge()) {
mFolderIcon.createBadgeScaleAnimator(0f, 1f).start();
}
@@ -818,6 +820,7 @@
mSuppressFolderDeletion = false;
clearDragInfo();
mState = STATE_SMALL;
+ mContent.setCurrentPage(0);
}
public boolean acceptDrop(DragObject d) {
@@ -1516,18 +1519,17 @@
return itemsInReadingOrder;
}
- public List<BubbleTextView> getItemsOnCurrentPage() {
+ public List<BubbleTextView> getItemsOnPage(int page) {
ArrayList<View> allItems = getItemsInRankOrder();
- int currentPage = mContent.getCurrentPage();
int lastPage = mContent.getPageCount() - 1;
int totalItemsInFolder = allItems.size();
int itemsPerPage = mContent.itemsPerPage();
- int numItemsOnCurrentPage = currentPage == lastPage
- ? totalItemsInFolder - (itemsPerPage * currentPage)
+ int numItemsOnCurrentPage = page == lastPage
+ ? totalItemsInFolder - (itemsPerPage * page)
: itemsPerPage;
- int startIndex = currentPage * itemsPerPage;
- int endIndex = startIndex + numItemsOnCurrentPage;
+ int startIndex = page * itemsPerPage;
+ int endIndex = Math.min(startIndex + numItemsOnCurrentPage, allItems.size());
List<BubbleTextView> itemsOnCurrentPage = new ArrayList<>(numItemsOnCurrentPage);
for (int i = startIndex; i < endIndex; ++i) {
diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java
index 7e8d0c7..26a2c89 100644
--- a/src/com/android/launcher3/folder/FolderAnimationManager.java
+++ b/src/com/android/launcher3/folder/FolderAnimationManager.java
@@ -119,7 +119,7 @@
public AnimatorSet getAnimator() {
final DragLayer.LayoutParams lp = (DragLayer.LayoutParams) mFolder.getLayoutParams();
FolderIcon.PreviewLayoutRule rule = mFolderIcon.getLayoutRule();
- final List<BubbleTextView> itemsInPreview = mFolderIcon.getItemsToDisplay();
+ final List<BubbleTextView> itemsInPreview = mFolderIcon.getPreviewItems();
// Match position of the FolderIcon
final Rect folderIconPos = new Rect();
@@ -186,7 +186,7 @@
PropertyResetListener colorResetListener = new PropertyResetListener<>(
BubbleTextView.TEXT_ALPHA_PROPERTY,
Color.alpha(Themes.getAttrColor(mContext, android.R.attr.textColorSecondary)));
- for (BubbleTextView icon : mFolder.getItemsOnCurrentPage()) {
+ for (BubbleTextView icon : mFolder.getItemsOnPage(mFolder.mContent.getCurrentPage())) {
if (mIsOpening) {
icon.setTextVisibility(false);
}
@@ -237,13 +237,19 @@
}
/**
- * Animate the items that are displayed in the preview.
+ * Animate the items on the current page.
*/
private void addPreviewItemAnimators(AnimatorSet animatorSet, final float folderScale,
int previewItemOffsetX) {
FolderIcon.PreviewLayoutRule rule = mFolderIcon.getLayoutRule();
- final List<BubbleTextView> itemsInPreview = mFolderIcon.getItemsToDisplay();
+ boolean isOnFirstPage = mFolder.mContent.getCurrentPage() == 0;
+ final List<BubbleTextView> itemsInPreview = isOnFirstPage
+ ? mFolderIcon.getPreviewItems()
+ : mFolderIcon.getPreviewItemsOnPage(mFolder.mContent.getCurrentPage());
final int numItemsInPreview = itemsInPreview.size();
+ final int numItemsInFirstPagePreview = isOnFirstPage
+ ? numItemsInPreview
+ : FolderIcon.NUM_ITEMS_IN_PREVIEW;
TimeInterpolator previewItemInterpolator = getPreviewItemInterpolator();
@@ -256,8 +262,8 @@
btvLp.isLockedToGrid = true;
cwc.setupLp(btv);
- // Match scale of icons in the preview.
- float previewScale = rule.scaleForItem(i, numItemsInPreview);
+ // Match scale of icons in the preview of the items on the first page.
+ float previewScale = rule.scaleForItem(i, numItemsInFirstPagePreview);
float previewSize = rule.getIconSize() * previewScale;
float iconScale = previewSize / itemsInPreview.get(i).getIconSize();
@@ -268,7 +274,7 @@
btv.setScaleY(scale);
// Match positions of the icons in the folder with their positions in the preview
- rule.computePreviewItemDrawingParams(i, numItemsInPreview, mTmpParams);
+ rule.computePreviewItemDrawingParams(i, numItemsInFirstPagePreview, mTmpParams);
// The PreviewLayoutRule assumes that the icon size takes up the entire width so we
// offset by the actual size.
int iconOffsetX = (int) ((btvLp.width - btv.getIconSize()) * iconScale) / 2;
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 7fdacc0..ae6a0e8 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -471,15 +471,25 @@
return mFolderName.getVisibility() == VISIBLE;
}
- public List<BubbleTextView> getItemsToDisplay() {
+ /**
+ * Returns the list of preview items displayed in the icon.
+ */
+ public List<BubbleTextView> getPreviewItems() {
+ return getPreviewItemsOnPage(0);
+ }
+
+ /**
+ * Returns the list of "preview items" on {@param page}.
+ */
+ public List<BubbleTextView> getPreviewItemsOnPage(int page) {
mPreviewVerifier.setFolderInfo(mFolder.getInfo());
List<BubbleTextView> itemsToDisplay = new ArrayList<>();
- List<View> allItems = mFolder.getItemsInRankOrder();
- int numItems = allItems.size();
+ List<BubbleTextView> itemsOnPage = mFolder.getItemsOnPage(page);
+ int numItems = itemsOnPage.size();
for (int rank = 0; rank < numItems; ++rank) {
- if (mPreviewVerifier.isItemInPreview(rank)) {
- itemsToDisplay.add((BubbleTextView) allItems.get(rank));
+ if (mPreviewVerifier.isItemInPreview(page, rank)) {
+ itemsToDisplay.add(itemsOnPage.get(rank));
}
if (itemsToDisplay.size() == FolderIcon.NUM_ITEMS_IN_PREVIEW) {
@@ -639,6 +649,10 @@
}
}
+ public void onFolderClose(int currentPage) {
+ mPreviewItemManager.onFolderClose(currentPage);
+ }
+
interface PreviewLayoutRule {
PreviewItemDrawingParams computePreviewItemDrawingParams(int index, int curNumItems,
PreviewItemDrawingParams params);
diff --git a/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java b/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java
index d0d8e79..eb415b9 100644
--- a/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java
+++ b/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java
@@ -25,15 +25,45 @@
*/
public class FolderIconPreviewVerifier {
+ private final int mMaxGridCountX;
+ private final int mMaxGridCountY;
+ private final int mMaxItemsPerPage;
+ private final int[] mGridSize = new int[2];
+
+ private int mGridCountX;
+
public FolderIconPreviewVerifier(InvariantDeviceProfile profile) {
- // b/37570804
+ mMaxGridCountX = profile.numFolderColumns;
+ mMaxGridCountY = profile.numFolderRows;
+ mMaxItemsPerPage = mMaxGridCountX * mMaxGridCountY;
}
public void setFolderInfo(FolderInfo info) {
- // b/37570804
+ FolderPagedView.calculateGridSize(info.contents.size(), 0, 0, mMaxGridCountX,
+ mMaxGridCountY, mMaxItemsPerPage, mGridSize);
+ mGridCountX = mGridSize[0];
}
+ /**
+ * Returns whether the item with {@param rank} is in the default Folder icon preview.
+ */
public boolean isItemInPreview(int rank) {
+ return isItemInPreview(0, rank);
+ }
+
+ /**
+ * @param page The page the item is on.
+ * @param rank The rank of the item.
+ * @return True iff the icon is in the 2x2 upper left quadrant of the Folder.
+ */
+ public boolean isItemInPreview(int page, int rank) {
+ if (page > 0) {
+ // First page items are laid out such that the first 4 items are always in the upper
+ // left quadrant. For all other pages, we need to check the row and col.
+ int col = rank % mGridCountX;
+ int row = rank / mGridCountX;
+ return col < 2 && row < 2;
+ }
return rank < FolderIcon.NUM_ITEMS_IN_PREVIEW;
}
}
diff --git a/src/com/android/launcher3/folder/PreviewItemManager.java b/src/com/android/launcher3/folder/PreviewItemManager.java
index 2524a6d..74c2102 100644
--- a/src/com/android/launcher3/folder/PreviewItemManager.java
+++ b/src/com/android/launcher3/folder/PreviewItemManager.java
@@ -16,6 +16,9 @@
package com.android.launcher3.folder;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
@@ -48,10 +51,19 @@
// These hold the first page preview items
private ArrayList<PreviewItemDrawingParams> mFirstPageParams = new ArrayList<>();
+ // These hold the current page preview items. It is empty if the current page is the first page.
+ private ArrayList<PreviewItemDrawingParams> mCurrentPageParams = new ArrayList<>();
+
+ private float mCurrentPageItemsTransX = 0;
+ private boolean mShouldSlideInFirstPage;
static final int INITIAL_ITEM_ANIMATION_DURATION = 350;
private static final int FINAL_ITEM_ANIMATION_DURATION = 200;
+ private static final int SLIDE_IN_FIRST_PAGE_ANIMATION_DURATION_DELAY = 200;
+ private static final int SLIDE_IN_FIRST_PAGE_ANIMATION_DURATION = 300;
+ private static final int ITEM_SLIDE_IN_OUT_DISTANCE_PX = 200;
+
public PreviewItemManager(FolderIcon icon) {
mIcon = icon;
}
@@ -116,15 +128,30 @@
return params;
}
- public void draw(Canvas canvas) {
- computePreviewDrawingParams(mReferenceDrawable);
+ public void drawParams(Canvas canvas, ArrayList<PreviewItemDrawingParams> params,
+ float transX) {
+ canvas.translate(transX, 0);
// The first item should be drawn last (ie. on top of later items)
- for (int i = mFirstPageParams.size() - 1; i >= 0; i--) {
- PreviewItemDrawingParams p = mFirstPageParams.get(i);
+ for (int i = params.size() - 1; i >= 0; i--) {
+ PreviewItemDrawingParams p = params.get(i);
if (!p.hidden) {
drawPreviewItem(canvas, p);
}
}
+ canvas.translate(-transX, 0);
+ }
+
+ public void draw(Canvas canvas) {
+ computePreviewDrawingParams(mReferenceDrawable);
+
+ float firstPageItemsTransX = 0;
+ if (mShouldSlideInFirstPage) {
+ drawParams(canvas, mCurrentPageParams, mCurrentPageItemsTransX);
+
+ firstPageItemsTransX = -ITEM_SLIDE_IN_OUT_DISTANCE_PX + mCurrentPageItemsTransX;
+ }
+
+ drawParams(canvas, mFirstPageParams, firstPageItemsTransX);
}
public void onParamsChanged() {
@@ -156,21 +183,21 @@
}
}
- void updateItemDrawingParams(boolean animate) {
- List<BubbleTextView> items = mIcon.getItemsToDisplay();
- int numItemsInPreview = items.size();
- int prevNumItems = mFirstPageParams.size();
+ void buildParamsForPage(int page, ArrayList<PreviewItemDrawingParams> params, boolean animate) {
+ List<BubbleTextView> items = mIcon.getPreviewItemsOnPage(page);
+ int prevNumItems = params.size();
- // We adjust the size of the list to match the number of items in the preview
- while (numItemsInPreview < mFirstPageParams.size()) {
- mFirstPageParams.remove(mFirstPageParams.size() - 1);
+ // We adjust the size of the list to match the number of items in the preview.
+ while (items.size() < params.size()) {
+ params.remove(params.size() - 1);
}
- while (numItemsInPreview > mFirstPageParams.size()) {
- mFirstPageParams.add(new PreviewItemDrawingParams(0, 0, 0, 0));
+ while (items.size() > params.size()) {
+ params.add(new PreviewItemDrawingParams(0, 0, 0, 0));
}
- for (int i = 0; i < mFirstPageParams.size(); i++) {
- PreviewItemDrawingParams p = mFirstPageParams.get(i);
+ int numItemsInFirstPagePreview = page == 0 ? items.size() : FolderIcon.NUM_ITEMS_IN_PREVIEW;
+ for (int i = 0; i < params.size(); i++) {
+ PreviewItemDrawingParams p = params.get(i);
p.drawable = items.get(i).getCompoundDrawables()[1];
if (p.drawable != null && !mIcon.mFolder.isOpen()) {
@@ -180,13 +207,13 @@
}
if (!animate || FeatureFlags.LAUNCHER3_LEGACY_FOLDER_ICON) {
- computePreviewItemDrawingParams(i, numItemsInPreview, p);
+ computePreviewItemDrawingParams(i, numItemsInFirstPagePreview, p);
if (mReferenceDrawable == null) {
mReferenceDrawable = p.drawable;
}
} else {
FolderPreviewItemAnim anim = new FolderPreviewItemAnim(this, p, i, prevNumItems, i,
- numItemsInPreview, DROP_IN_ANIMATION_DURATION, null);
+ numItemsInFirstPagePreview, DROP_IN_ANIMATION_DURATION, null);
if (p.anim != null) {
if (p.anim.hasEqualFinalState(anim)) {
@@ -201,6 +228,39 @@
}
}
+ void onFolderClose(int currentPage) {
+ // If we are not closing on the first page, we animate the current page preview items
+ // out, and animate the first page preview items in.
+ mShouldSlideInFirstPage = currentPage != 0;
+ if (mShouldSlideInFirstPage) {
+ mCurrentPageItemsTransX = 0;
+ buildParamsForPage(currentPage, mCurrentPageParams, false);
+ onParamsChanged();
+
+ ValueAnimator slideAnimator = ValueAnimator.ofFloat(0, ITEM_SLIDE_IN_OUT_DISTANCE_PX);
+ slideAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator valueAnimator) {
+ mCurrentPageItemsTransX = (float) valueAnimator.getAnimatedValue();
+ onParamsChanged();
+ }
+ });
+ slideAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mCurrentPageParams.clear();
+ }
+ });
+ slideAnimator.setStartDelay(SLIDE_IN_FIRST_PAGE_ANIMATION_DURATION_DELAY);
+ slideAnimator.setDuration(SLIDE_IN_FIRST_PAGE_ANIMATION_DURATION);
+ slideAnimator.start();
+ }
+ }
+
+ void updateItemDrawingParams(boolean animate) {
+ buildParamsForPage(0, mFirstPageParams, animate);
+ }
+
boolean verifyDrawable(@NonNull Drawable who) {
for (int i = 0; i < mFirstPageParams.size(); i++) {
if (mFirstPageParams.get(i).drawable == who) {