diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java index afbf9f2a2d..3184d6d43f 100644 --- a/src/com/android/launcher3/folder/Folder.java +++ b/src/com/android/launcher3/folder/Folder.java @@ -791,12 +791,14 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC 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 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC mSuppressFolderDeletion = false; clearDragInfo(); mState = STATE_SMALL; + mContent.setCurrentPage(0); } public boolean acceptDrop(DragObject d) { @@ -1516,18 +1519,17 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC return itemsInReadingOrder; } - public List getItemsOnCurrentPage() { + public List getItemsOnPage(int page) { ArrayList 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 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 7e8d0c7cd5..26a2c8913a 100644 --- a/src/com/android/launcher3/folder/FolderAnimationManager.java +++ b/src/com/android/launcher3/folder/FolderAnimationManager.java @@ -119,7 +119,7 @@ public class FolderAnimationManager { public AnimatorSet getAnimator() { final DragLayer.LayoutParams lp = (DragLayer.LayoutParams) mFolder.getLayoutParams(); FolderIcon.PreviewLayoutRule rule = mFolderIcon.getLayoutRule(); - final List itemsInPreview = mFolderIcon.getItemsToDisplay(); + final List itemsInPreview = mFolderIcon.getPreviewItems(); // Match position of the FolderIcon final Rect folderIconPos = new Rect(); @@ -186,7 +186,7 @@ public class FolderAnimationManager { 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 @@ public class FolderAnimationManager { } /** - * 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 itemsInPreview = mFolderIcon.getItemsToDisplay(); + boolean isOnFirstPage = mFolder.mContent.getCurrentPage() == 0; + final List 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 @@ public class FolderAnimationManager { 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 @@ public class FolderAnimationManager { 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 7fdacc094c..ae6a0e86d6 100644 --- a/src/com/android/launcher3/folder/FolderIcon.java +++ b/src/com/android/launcher3/folder/FolderIcon.java @@ -471,15 +471,25 @@ public class FolderIcon extends FrameLayout implements FolderListener { return mFolderName.getVisibility() == VISIBLE; } - public List getItemsToDisplay() { + /** + * Returns the list of preview items displayed in the icon. + */ + public List getPreviewItems() { + return getPreviewItemsOnPage(0); + } + + /** + * Returns the list of "preview items" on {@param page}. + */ + public List getPreviewItemsOnPage(int page) { mPreviewVerifier.setFolderInfo(mFolder.getInfo()); List itemsToDisplay = new ArrayList<>(); - List allItems = mFolder.getItemsInRankOrder(); - int numItems = allItems.size(); + List 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 class FolderIcon extends FrameLayout implements FolderListener { } } + 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 d0d8e79d53..eb415b9456 100644 --- a/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java +++ b/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java @@ -25,15 +25,45 @@ import com.android.launcher3.config.FeatureFlags; */ 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 2524a6d82b..74c210254b 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 @@ public class PreviewItemManager { // These hold the first page preview items private ArrayList mFirstPageParams = new ArrayList<>(); + // These hold the current page preview items. It is empty if the current page is the first page. + private ArrayList 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 @@ public class PreviewItemManager { return params; } - public void draw(Canvas canvas) { - computePreviewDrawingParams(mReferenceDrawable); + public void drawParams(Canvas canvas, ArrayList 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 @@ public class PreviewItemManager { } } - void updateItemDrawingParams(boolean animate) { - List items = mIcon.getItemsToDisplay(); - int numItemsInPreview = items.size(); - int prevNumItems = mFirstPageParams.size(); + void buildParamsForPage(int page, ArrayList params, boolean animate) { + List 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 @@ public class PreviewItemManager { } 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 @@ public class PreviewItemManager { } } + 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) {