diff options
Diffstat (limited to 'src/com/android/launcher3/folder')
9 files changed, 703 insertions, 508 deletions
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java index f22b53338..0bd2c9af7 100644 --- a/src/com/android/launcher3/folder/Folder.java +++ b/src/com/android/launcher3/folder/Folder.java @@ -26,12 +26,12 @@ import android.animation.AnimatorSet; import android.annotation.SuppressLint; import android.appwidget.AppWidgetHostView; import android.content.Context; -import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Path; import android.graphics.Rect; import android.text.InputType; import android.text.Selection; +import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; import android.util.Pair; @@ -74,7 +74,10 @@ import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.dragndrop.DragOptions; import com.android.launcher3.logging.LoggerUtils; import com.android.launcher3.pageindicators.PageIndicatorDots; +import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction; +import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; +import com.android.launcher3.userevent.nano.LauncherLogProto.ItemType; import com.android.launcher3.userevent.nano.LauncherLogProto.Target; import com.android.launcher3.util.Thunk; import com.android.launcher3.views.ClipPathView; @@ -126,9 +129,6 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo private static final Rect sTempRect = new Rect(); private static final int MIN_FOLDERS_FOR_HARDWARE_OPTIMIZATION = 10; - private static String sDefaultFolderName; - private static String sHintText; - private final Alarm mReorderAlarm = new Alarm(); private final Alarm mOnExitAlarm = new Alarm(); private final Alarm mOnScrollHintAlarm = new Alarm(); @@ -148,7 +148,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo public ExtendedEditText mFolderName; private PageIndicatorDots mPageIndicator; - private View mFooter; + protected View mFooter; private int mFooterHeight; // Cell ranks used for drag and drop @@ -173,8 +173,6 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo private boolean mDeleteFolderOnDropCompleted = false; private boolean mSuppressFolderDeletion = false; private boolean mItemAddedBackToSelfViaIcon = false; - @Thunk float mFolderIconPivotX; - @Thunk float mFolderIconPivotY; private boolean mIsEditingName = false; @ViewDebug.ExportedProperty(category = "launcher") @@ -196,8 +194,6 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo super(context, attrs); setAlwaysDrawnWithCacheEnabled(false); - setLocaleDependentFields(getResources(), false /* force */); - mLauncher = Launcher.getLauncher(context); // We need this view to be focusable in touch mode so that when text editing of the folder // name is complete, we have something to focus on, thus hiding the cursor and giving @@ -315,10 +311,15 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo // Convert to a string here to ensure that no other state associated with the text field // gets saved. String newTitle = mFolderName.getText().toString(); - mInfo.setTitle(newTitle); + mInfo.title = newTitle; + mFolderIcon.onTitleChanged(newTitle); mLauncher.getModelWriter().updateItemInDatabase(mInfo); - mFolderName.setHint(sDefaultFolderName.contentEquals(newTitle) ? sHintText : null); + if (TextUtils.isEmpty(mInfo.title)) { + mFolderName.setHint(R.string.folder_hint_text); + } else { + mFolderName.setHint(null); + } sendCustomAccessibilityEvent( this, AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED, @@ -385,7 +386,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo mInfo = info; ArrayList<WorkspaceItemInfo> children = info.contents; Collections.sort(children, ITEM_POS_COMPARATOR); - mContent.bindItems(children); + updateItemLocationsInDatabaseBatch(); DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams(); if (lp == null) { @@ -393,30 +394,40 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo lp.customPosition = true; setLayoutParams(lp); } - centerAboutIcon(); - mItemsInvalidated = true; - updateTextViewFocus(); mInfo.addListener(this); - if (!sDefaultFolderName.contentEquals(mInfo.title)) { + if (!TextUtils.isEmpty(mInfo.title)) { mFolderName.setText(mInfo.title); mFolderName.setHint(null); } else { mFolderName.setText(""); - mFolderName.setHint(sHintText); + mFolderName.setHint(R.string.folder_hint_text); } - // In case any children didn't come across during loading, clean up the folder accordingly - mFolderIcon.post(new Runnable() { - public void run() { - if (getItemCount() <= 1) { - replaceFolderWithFinalItem(); - } + mFolderIcon.post(() -> { + if (getItemCount() <= 1) { + replaceFolderWithFinalItem(); } }); } + + /** + * Show suggested folder title. + */ + public void showSuggestedTitle(CharSequence suggestName) { + if (FeatureFlags.FOLDER_NAME_SUGGEST.get() && mInfo.contents.size() == 2) { + if (!TextUtils.isEmpty(suggestName)) { + mFolderName.setHint(suggestName); + mFolderName.setText(suggestName); + mFolderName.showKeyboard(); + mInfo.title = suggestName; + } + animateOpen(); + } + } + /** * Creates a new UserFolder, inflated from R.layout.user_folder. * @@ -473,17 +484,49 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo } /** + * Opens the folder as part of a drag operation + */ + public void beginExternalDrag() { + mIsExternalDrag = true; + mDragInProgress = true; + + // Since this folder opened by another controller, it might not get onDrop or + // onDropComplete. Perform cleanup once drag-n-drop ends. + mDragController.addDragListener(this); + + ArrayList<WorkspaceItemInfo> items = new ArrayList<>(mInfo.contents); + mEmptyCellRank = items.size(); + items.add(null); // Add an empty spot at the end + + animateOpen(items, mEmptyCellRank / mContent.itemsPerPage()); + } + + /** * Opens the user folder described by the specified tag. The opening of the folder * is animated relative to the specified View. If the View is null, no animation * is played. */ public void animateOpen() { + animateOpen(mInfo.contents, 0); + } + + /** + * Opens the user folder described by the specified tag. The opening of the folder + * is animated relative to the specified View. If the View is null, no animation + * is played. + */ + private void animateOpen(List<WorkspaceItemInfo> items, int pageNo) { Folder openFolder = getOpen(mLauncher); if (openFolder != null && openFolder != this) { // Close any open folder before opening a folder. openFolder.close(true); } + mContent.bindItems(items); + centerAboutIcon(); + mItemsInvalidated = true; + updateTextViewFocus(); + mIsOpen = true; DragLayer dragLayer = mLauncher.getDragLayer(); @@ -500,18 +543,13 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo } mContent.completePendingPageChanges(); - if (!mDragInProgress) { - // Open on the first page. - mContent.snapToPageImmediately(0); - } + mContent.snapToPageImmediately(pageNo); // This is set to true in close(), but isn't reset to false until onDropCompleted(). This // leads to an inconsistent state if you drag out of the folder and drag back in without // dropping. One resulting issue is that replaceFolderWithFinalItem() can be called twice. mDeleteFolderOnDropCompleted = false; - centerAboutIcon(); - AnimatorSet anim = new FolderAnimationManager(this, true /* isOpening */).getAnimator(); anim.addListener(new AnimatorListenerAdapter() { @Override @@ -524,7 +562,11 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo mState = STATE_OPEN; announceAccessibilityChanges(); - mLauncher.getUserEventDispatcher().resetElapsedContainerMillis("folder opened"); + mLauncher.getUserEventDispatcher().logActionOnItem( + Touch.TAP, + Direction.NONE, + ItemType.FOLDER_ICON, mInfo.cellX, mInfo.cellY); + mContent.setFocusOnFirstChild(); } }); @@ -570,20 +612,9 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo if (mDragController.isDragging()) { mDragController.forceTouchMove(); } - mContent.verifyVisibleHighResIcons(mContent.getNextPage()); } - public void beginExternalDrag() { - mEmptyCellRank = mContent.allocateRankForNewItem(); - mIsExternalDrag = true; - mDragInProgress = true; - - // Since this folder opened by another controller, it might not get onDrop or - // onDropComplete. Perform cleanup once drag-n-drop ends. - mDragController.addDragListener(this); - } - @Override protected boolean isOfType(int type) { return (type & TYPE_FOLDER) != 0; @@ -668,6 +699,8 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo } else if (mDragInProgress) { mDeleteFolderOnDropCompleted = true; } + } else if (!mDragInProgress) { + mContent.unbindItems(); } mSuppressFolderDeletion = false; clearDragInfo(); @@ -822,9 +855,9 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo } } + @Override public void onDropCompleted(final View target, final DragObject d, final boolean success) { - if (success) { if (mDeleteFolderOnDropCompleted && !mItemAddedBackToSelfViaIcon && target != this) { replaceFolderWithFinalItem(); @@ -834,9 +867,9 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo WorkspaceItemInfo info = (WorkspaceItemInfo) d.dragInfo; View icon = (mCurrentDragView != null && mCurrentDragView.getTag() == info) ? mCurrentDragView : mContent.createNewView(info); - ArrayList<View> views = getItemsInReadingOrder(); + ArrayList<View> views = getIconsInReadingOrder(); views.add(info.rank, icon); - mContent.arrangeChildren(views, views.size()); + mContent.arrangeChildren(views); mItemsInvalidated = true; try (SuppressInfoChanges s = new SuppressInfoChanges()) { @@ -863,7 +896,6 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo // Reordering may have occured, and we need to save the new item locations. We do this once // at the end to prevent unnecessary database operations. updateItemLocationsInDatabaseBatch(); - // Use the item count to check for multi-page as the folder UI may not have // been refreshed yet. if (getItemCount() <= mContent.itemsPerPage()) { @@ -874,16 +906,21 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo } private void updateItemLocationsInDatabaseBatch() { - ArrayList<View> list = getItemsInReadingOrder(); - ArrayList<ItemInfo> items = new ArrayList<ItemInfo>(); - for (int i = 0; i < list.size(); i++) { - View v = list.get(i); - ItemInfo info = (ItemInfo) v.getTag(); - info.rank = i; - items.add(info); + FolderGridOrganizer verifier = new FolderGridOrganizer( + mLauncher.getDeviceProfile().inv).setFolderInfo(mInfo); + + ArrayList<ItemInfo> items = new ArrayList<>(); + int total = mInfo.contents.size(); + for (int i = 0; i < total; i++) { + WorkspaceItemInfo itemInfo = mInfo.contents.get(i); + if (verifier.updateRankAndPos(itemInfo, i)) { + items.add(itemInfo); + } } - mLauncher.getModelWriter().moveItemsInDatabase(items, mInfo.id, 0); + if (!items.isEmpty()) { + mLauncher.getModelWriter().moveItemsInDatabase(items, mInfo.id, 0); + } } public void notifyDrop() { @@ -948,28 +985,16 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo setPivotX(folderPivotX); setPivotY(folderPivotY); - mFolderIconPivotX = (int) (mFolderIcon.getMeasuredWidth() * - (1.0f * folderPivotX / width)); - mFolderIconPivotY = (int) (mFolderIcon.getMeasuredHeight() * - (1.0f * folderPivotY / height)); - lp.width = width; lp.height = height; lp.x = left; lp.y = top; } - public float getPivotXForIconAnimation() { - return mFolderIconPivotX; - } - public float getPivotYForIconAnimation() { - return mFolderIconPivotY; - } - - private int getContentAreaHeight() { + protected int getContentAreaHeight() { DeviceProfile grid = mLauncher.getDeviceProfile(); - int maxContentAreaHeight = grid.availableHeightPx - - grid.getTotalWorkspacePadding().y - mFooterHeight; + int maxContentAreaHeight = grid.availableHeightPx - grid.getTotalWorkspacePadding().y + - mFooterHeight; int height = Math.min(maxContentAreaHeight, mContent.getDesiredHeight()); return Math.max(height, MIN_CONTENT_DIMEN); @@ -1021,22 +1046,12 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo * Rearranges the children based on their rank. */ public void rearrangeChildren() { - rearrangeChildren(-1); - } - - /** - * Rearranges the children based on their rank. - * @param itemCount if greater than the total children count, empty spaces are left at the end, - * otherwise it is ignored. - */ - public void rearrangeChildren(int itemCount) { - ArrayList<View> views = getItemsInReadingOrder(); - mContent.arrangeChildren(views, Math.max(itemCount, views.size())); + mContent.arrangeChildren(getIconsInReadingOrder()); mItemsInvalidated = true; } public int getItemCount() { - return mContent.getItemCount(); + return mInfo.contents.size(); } @Thunk void replaceFolderWithFinalItem() { @@ -1044,7 +1059,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo Runnable onCompleteRunnable = new Runnable() { @Override public void run() { - int itemCount = mInfo.contents.size(); + int itemCount = getItemCount(); if (itemCount <= 1) { View newIcon = null; @@ -1121,9 +1136,12 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo return false; } }); + } else { + setOnKeyListener(null); } } + @Override public void onDrop(DragObject d, DragOptions options) { // If the icon was dropped while the page was being scrolled, we need to compute // the target location again such that the icon is placed of the final page. @@ -1171,12 +1189,6 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo // before creating the view, so that WorkspaceItemInfo is updated appropriately. mLauncher.getModelWriter().addOrMoveItemInDatabase( si, mInfo.id, 0, si.cellX, si.cellY); - - // We only need to update the locations if it doesn't get handled in - // #onDropCompleted. - if (d.dragSource != this) { - updateItemLocationsInDatabaseBatch(); - } mIsExternalDrag = false; } else { currentDragView = mCurrentDragView; @@ -1203,7 +1215,13 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo // Temporarily suppress the listener, as we did all the work already here. try (SuppressInfoChanges s = new SuppressInfoChanges()) { - mInfo.add(si, false); + mInfo.add(si, mEmptyCellRank, false); + } + + // We only need to update the locations if it doesn't get handled in + // #onDropCompleted. + if (d.dragSource != this) { + updateItemLocationsInDatabaseBatch(); } } @@ -1226,22 +1244,29 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo // to correspond to the animation of the icon back into the folder. This is public void hideItem(WorkspaceItemInfo info) { View v = getViewForInfo(info); - v.setVisibility(INVISIBLE); + if (v != null) { + v.setVisibility(INVISIBLE); + } } public void showItem(WorkspaceItemInfo info) { View v = getViewForInfo(info); - v.setVisibility(VISIBLE); + if (v != null) { + v.setVisibility(VISIBLE); + } } @Override public void onAdd(WorkspaceItemInfo item, int rank) { - View view = mContent.createAndAddViewForRank(item, rank); + FolderGridOrganizer verifier = new FolderGridOrganizer( + mLauncher.getDeviceProfile().inv).setFolderInfo(mInfo); + verifier.updateRankAndPos(item, rank); mLauncher.getModelWriter().addOrMoveItemInDatabase(item, mInfo.id, 0, item.cellX, item.cellY); + updateItemLocationsInDatabaseBatch(); - ArrayList<View> items = new ArrayList<>(getItemsInReadingOrder()); - items.add(rank, view); - mContent.arrangeChildren(items, items.size()); + if (mContent.areViewsBound()) { + mContent.createAndAddViewForRank(item, rank); + } mItemsInvalidated = true; } @@ -1264,13 +1289,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo } private View getViewForInfo(final WorkspaceItemInfo item) { - return mContent.iterateOverItems(new ItemOperator() { - - @Override - public boolean evaluate(ItemInfo info, View view) { - return info == item; - } - }); + return mContent.iterateOverItems((info, view) -> info == item); } @Override @@ -1278,32 +1297,27 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo updateTextViewFocus(); } - @Override - public void prepareAutoUpdate() { - close(false); - } - - public void onTitleChanged(CharSequence title) { + /** + * Utility methods to iterate over items of the view + */ + public void iterateOverItems(ItemOperator op) { + mContent.iterateOverItems(op); } - public ArrayList<View> getItemsInReadingOrder() { + /** + * Returns the sorted list of all the icons in the folder + */ + public ArrayList<View> getIconsInReadingOrder() { if (mItemsInvalidated) { mItemsInReadingOrder.clear(); - mContent.iterateOverItems(new ItemOperator() { - - @Override - public boolean evaluate(ItemInfo info, View view) { - mItemsInReadingOrder.add(view); - return false; - } - }); + mContent.iterateOverItems((i, v) -> !mItemsInReadingOrder.add(v)); mItemsInvalidated = false; } return mItemsInReadingOrder; } public List<BubbleTextView> getItemsOnPage(int page) { - ArrayList<View> allItems = getItemsInReadingOrder(); + ArrayList<View> allItems = getIconsInReadingOrder(); int lastPage = mContent.getPageCount() - 1; int totalItemsInFolder = allItems.size(); int itemsPerPage = mContent.itemsPerPage(); @@ -1482,15 +1496,6 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo return false; } - public static void setLocaleDependentFields(Resources res, boolean force) { - if (sDefaultFolderName == null || force) { - sDefaultFolderName = res.getString(R.string.folder_name); - } - if (sHintText == null || force) { - sHintText = res.getString(R.string.folder_hint_text); - } - } - /** * Alternative to using {@link #getClipToOutline()} as it only works with derivatives of * rounded rect. diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java index 962f21560..1310d374e 100644 --- a/src/com/android/launcher3/folder/FolderAnimationManager.java +++ b/src/com/android/launcher3/folder/FolderAnimationManager.java @@ -59,6 +59,8 @@ import java.util.List; */ public class FolderAnimationManager { + private static final int FOLDER_NAME_ALPHA_DURATION = 32; + private Folder mFolder; private FolderPagedView mContent; private GradientDrawable mFolderBackground; @@ -79,7 +81,7 @@ public class FolderAnimationManager { private final TimeInterpolator mLargeFolderPreviewItemCloseInterpolator; private final PreviewItemDrawingParams mTmpParams = new PreviewItemDrawingParams(0, 0, 0, 0); - + private final FolderGridOrganizer mPreviewVerifier; public FolderAnimationManager(Folder folder, boolean isOpening) { mFolder = folder; @@ -91,6 +93,7 @@ public class FolderAnimationManager { mContext = folder.getContext(); mLauncher = folder.mLauncher; + mPreviewVerifier = new FolderGridOrganizer(mLauncher.getDeviceProfile().inv); mIsOpening = isOpening; @@ -113,7 +116,7 @@ public class FolderAnimationManager { public AnimatorSet getAnimator() { final DragLayer.LayoutParams lp = (DragLayer.LayoutParams) mFolder.getLayoutParams(); ClippedFolderIconLayoutRule rule = mFolderIcon.getLayoutRule(); - final List<BubbleTextView> itemsInPreview = mFolderIcon.getPreviewItems(); + final List<BubbleTextView> itemsInPreview = getPreviewIconsOnPage(0); // Match position of the FolderIcon final Rect folderIconPos = new Rect(); @@ -129,11 +132,19 @@ public class FolderAnimationManager { * scaleRelativeToDragLayer; final float finalScale = 1f; float scale = mIsOpening ? initialScale : finalScale; - mFolder.setScaleX(scale); - mFolder.setScaleY(scale); mFolder.setPivotX(0); mFolder.setPivotY(0); + // Scale the contents of the folder. + mFolder.mContent.setScaleX(scale); + mFolder.mContent.setScaleY(scale); + mFolder.mContent.setPivotX(0); + mFolder.mContent.setPivotY(0); + mFolder.mFooter.setScaleX(scale); + mFolder.mFooter.setScaleY(scale); + mFolder.mFooter.setPivotX(0); + mFolder.mFooter.setPivotY(0); + // We want to create a small X offset for the preview items, so that they follow their // expected path to their final locations. ie. an icon should not move right, if it's final // location is to its left. This value is arbitrarily defined. @@ -142,14 +153,13 @@ public class FolderAnimationManager { previewItemOffsetX = (int) (lp.width * initialScale - initialSize - previewItemOffsetX); } - final int paddingOffsetX = (int) ((mFolder.getPaddingLeft() + mContent.getPaddingLeft()) - * initialScale); - final int paddingOffsetY = (int) ((mFolder.getPaddingTop() + mContent.getPaddingTop()) - * initialScale); + final int paddingOffsetX = (int) (mContent.getPaddingLeft() * initialScale); + final int paddingOffsetY = (int) (mContent.getPaddingTop() * initialScale); - int initialX = folderIconPos.left + mPreviewBackground.getOffsetX() - paddingOffsetX - - previewItemOffsetX; - int initialY = folderIconPos.top + mPreviewBackground.getOffsetY() - paddingOffsetY; + int initialX = folderIconPos.left + mFolder.getPaddingLeft() + + mPreviewBackground.getOffsetX() - paddingOffsetX - previewItemOffsetX; + int initialY = folderIconPos.top + mFolder.getPaddingTop() + + mPreviewBackground.getOffsetY() - paddingOffsetY; final float xDistance = initialX - lp.x; final float yDistance = initialY - lp.y; @@ -163,11 +173,10 @@ public class FolderAnimationManager { // Set up the reveal animation that clips the Folder. int totalOffsetX = paddingOffsetX + previewItemOffsetX; - Rect startRect = new Rect( - Math.round(totalOffsetX / initialScale), - Math.round(paddingOffsetY / initialScale), - Math.round((totalOffsetX + initialSize) / initialScale), - Math.round((paddingOffsetY + initialSize) / initialScale)); + Rect startRect = new Rect(totalOffsetX, + paddingOffsetY, + Math.round((totalOffsetX + initialSize)), + Math.round((paddingOffsetY + initialSize))); Rect endRect = new Rect(0, 0, lp.width, lp.height); float finalRadius = ResourceUtils.pxFromDp(2, mContext.getResources().getDisplayMetrics()); @@ -188,17 +197,46 @@ public class FolderAnimationManager { play(a, getAnimator(mFolder, View.TRANSLATION_X, xDistance, 0f)); play(a, getAnimator(mFolder, View.TRANSLATION_Y, yDistance, 0f)); - play(a, getAnimator(mFolder, SCALE_PROPERTY, initialScale, finalScale)); + play(a, getAnimator(mFolder.mContent, SCALE_PROPERTY, initialScale, finalScale)); + play(a, getAnimator(mFolder.mFooter, SCALE_PROPERTY, initialScale, finalScale)); play(a, getAnimator(mFolderBackground, "color", initialColor, finalColor)); play(a, mFolderIcon.mFolderName.createTextAlphaAnimator(!mIsOpening)); play(a, getShape().createRevealAnimator( mFolder, startRect, endRect, finalRadius, !mIsOpening)); + // Fade in the folder name, as the text can overlap the icons when grid size is small. + mFolder.mFolderName.setAlpha(mIsOpening ? 0f : 1f); + play(a, getAnimator(mFolder.mFolderName, View.ALPHA, 0, 1), + mIsOpening ? FOLDER_NAME_ALPHA_DURATION : 0, + mIsOpening ? mDuration - FOLDER_NAME_ALPHA_DURATION : FOLDER_NAME_ALPHA_DURATION); + + // Translate the footer so that it tracks the bottom of the content. + float normalHeight = mFolder.getContentAreaHeight(); + float scaledHeight = normalHeight * initialScale; + float diff = normalHeight - scaledHeight; + play(a, getAnimator(mFolder.mFooter, View.TRANSLATION_Y, -diff, 0f)); // Animate the elevation midway so that the shadow is not noticeable in the background. int midDuration = mDuration / 2; Animator z = getAnimator(mFolder, View.TRANSLATION_Z, -mFolder.getElevation(), 0); play(a, z, mIsOpening ? midDuration : 0, midDuration); + + // Store clip variables + CellLayout cellLayout = mContent.getCurrentCellLayout(); + boolean folderClipChildren = mFolder.getClipChildren(); + boolean folderClipToPadding = mFolder.getClipToPadding(); + boolean contentClipChildren = mContent.getClipChildren(); + boolean contentClipToPadding = mContent.getClipToPadding(); + boolean cellLayoutClipChildren = cellLayout.getClipChildren(); + boolean cellLayoutClipPadding = cellLayout.getClipToPadding(); + + mFolder.setClipChildren(false); + mFolder.setClipToPadding(false); + mContent.setClipChildren(false); + mContent.setClipToPadding(false); + cellLayout.setClipChildren(false); + cellLayout.setClipToPadding(false); + a.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -206,8 +244,20 @@ public class FolderAnimationManager { mFolder.setTranslationX(0.0f); mFolder.setTranslationY(0.0f); mFolder.setTranslationZ(0.0f); - mFolder.setScaleX(1f); - mFolder.setScaleY(1f); + mFolder.mContent.setScaleX(1f); + mFolder.mContent.setScaleY(1f); + mFolder.mFooter.setScaleX(1f); + mFolder.mFooter.setScaleY(1f); + mFolder.mFooter.setTranslationX(0f); + mFolder.mFolderName.setAlpha(1f); + + mFolder.setClipChildren(folderClipChildren); + mFolder.setClipToPadding(folderClipToPadding); + mContent.setClipChildren(contentClipChildren); + mContent.setClipToPadding(contentClipToPadding); + cellLayout.setClipChildren(cellLayoutClipChildren); + cellLayout.setClipToPadding(cellLayoutClipPadding); + } }); @@ -226,15 +276,22 @@ public class FolderAnimationManager { } /** + * Returns the list of "preview items" on {@param page}. + */ + private List<BubbleTextView> getPreviewIconsOnPage(int page) { + return mPreviewVerifier.setFolderInfo(mFolder.mInfo) + .previewItemsForPage(page, mFolder.getIconsInReadingOrder()); + } + + /** * Animate the items on the current page. */ private void addPreviewItemAnimators(AnimatorSet animatorSet, final float folderScale, int previewItemOffsetX, int previewItemOffsetY) { ClippedFolderIconLayoutRule rule = mFolderIcon.getLayoutRule(); boolean isOnFirstPage = mFolder.mContent.getCurrentPage() == 0; - final List<BubbleTextView> itemsInPreview = isOnFirstPage - ? mFolderIcon.getPreviewItems() - : mFolderIcon.getPreviewItemsOnPage(mFolder.mContent.getCurrentPage()); + final List<BubbleTextView> itemsInPreview = getPreviewIconsOnPage( + isOnFirstPage ? 0 : mFolder.mContent.getCurrentPage()); final int numItemsInPreview = itemsInPreview.size(); final int numItemsInFirstPagePreview = isOnFirstPage ? numItemsInPreview : MAX_NUM_ITEMS_IN_PREVIEW; diff --git a/src/com/android/launcher3/folder/FolderGridOrganizer.java b/src/com/android/launcher3/folder/FolderGridOrganizer.java new file mode 100644 index 000000000..9d14a5ffb --- /dev/null +++ b/src/com/android/launcher3/folder/FolderGridOrganizer.java @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.folder; + +import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW; + +import android.graphics.Point; + +import com.android.launcher3.FolderInfo; +import com.android.launcher3.InvariantDeviceProfile; +import com.android.launcher3.ItemInfo; + +import java.util.ArrayList; +import java.util.List; + +/** + * Utility class for managing item positions in a folder based on rank + */ +public class FolderGridOrganizer { + + private final Point mPoint = new Point(); + private final int mMaxCountX; + private final int mMaxCountY; + private final int mMaxItemsPerPage; + + private int mNumItemsInFolder; + private int mCountX; + private int mCountY; + private boolean mDisplayingUpperLeftQuadrant = false; + + /** + * Note: must call {@link #setFolderInfo(FolderInfo)} manually for verifier to work. + */ + public FolderGridOrganizer(InvariantDeviceProfile profile) { + mMaxCountX = profile.numFolderColumns; + mMaxCountY = profile.numFolderRows; + mMaxItemsPerPage = mMaxCountX * mMaxCountY; + } + + /** + * Updates the organizer with the provided folder info + */ + public FolderGridOrganizer setFolderInfo(FolderInfo info) { + return setContentSize(info.contents.size()); + } + + /** + * Updates the organizer to reflect the content size + */ + public FolderGridOrganizer setContentSize(int contentSize) { + if (contentSize != mNumItemsInFolder) { + calculateGridSize(contentSize); + + mDisplayingUpperLeftQuadrant = contentSize > MAX_NUM_ITEMS_IN_PREVIEW; + mNumItemsInFolder = contentSize; + } + return this; + } + + public int getCountX() { + return mCountX; + } + + public int getCountY() { + return mCountY; + } + + public int getMaxItemsPerPage() { + return mMaxItemsPerPage; + } + + /** + * Calculates the grid size such that {@param count} items can fit in the grid. + * The grid size is calculated such that countY <= countX and countX = ceil(sqrt(count)) while + * maintaining the restrictions of {@link #mMaxCountX} & {@link #mMaxCountY}. + */ + private void calculateGridSize(int count) { + boolean done; + int gridCountX = mCountX; + int gridCountY = mCountY; + + if (count >= mMaxItemsPerPage) { + gridCountX = mMaxCountX; + gridCountY = mMaxCountY; + done = true; + } else { + done = false; + } + + while (!done) { + int oldCountX = gridCountX; + int oldCountY = gridCountY; + if (gridCountX * gridCountY < count) { + // Current grid is too small, expand it + if ((gridCountX <= gridCountY || gridCountY == mMaxCountY) + && gridCountX < mMaxCountX) { + gridCountX++; + } else if (gridCountY < mMaxCountY) { + gridCountY++; + } + if (gridCountY == 0) gridCountY++; + } else if ((gridCountY - 1) * gridCountX >= count && gridCountY >= gridCountX) { + gridCountY = Math.max(0, gridCountY - 1); + } else if ((gridCountX - 1) * gridCountY >= count) { + gridCountX = Math.max(0, gridCountX - 1); + } + done = gridCountX == oldCountX && gridCountY == oldCountY; + } + + mCountX = gridCountX; + mCountY = gridCountY; + } + + /** + * Updates the item's cellX, cellY and rank corresponding to the provided rank. + * @return true if there was any change + */ + public boolean updateRankAndPos(ItemInfo item, int rank) { + Point pos = getPosForRank(rank); + if (!pos.equals(item.cellX, item.cellY) || rank != item.rank) { + item.rank = rank; + item.cellX = pos.x; + item.cellY = pos.y; + return true; + } + return false; + } + + /** + * Returns the position of the item in the grid + */ + public Point getPosForRank(int rank) { + int pagePos = rank % mMaxItemsPerPage; + mPoint.x = pagePos % mCountX; + mPoint.y = pagePos / mCountX; + return mPoint; + } + + /** + * Returns the preview items for the provided pageNo using the full list of contents + */ + public <T, R extends T> ArrayList<R> previewItemsForPage(int page, List<T> contents) { + ArrayList<R> result = new ArrayList<>(); + int itemsPerPage = mCountX * mCountY; + int start = itemsPerPage * page; + int end = Math.min(start + itemsPerPage, contents.size()); + + for (int i = start, rank = 0; i < end; i++, rank++) { + if (isItemInPreview(page, rank)) { + result.add((R) contents.get(i)); + } + + if (result.size() == MAX_NUM_ITEMS_IN_PREVIEW) { + break; + } + } + return result; + } + + /** + * Returns whether the item with 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) { + // 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. + if (page > 0 || mDisplayingUpperLeftQuadrant) { + int col = rank % mCountX; + int row = rank / mCountX; + return col < 2 && row < 2; + } + return rank < MAX_NUM_ITEMS_IN_PREVIEW; + } +} diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java index 0e2d4673e..fd6d1e38a 100644 --- a/src/com/android/launcher3/folder/FolderIcon.java +++ b/src/com/android/launcher3/folder/FolderIcon.java @@ -58,18 +58,21 @@ import com.android.launcher3.Utilities; import com.android.launcher3.Workspace; import com.android.launcher3.WorkspaceItemInfo; import com.android.launcher3.anim.Interpolators; +import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.dot.FolderDotInfo; import com.android.launcher3.dragndrop.BaseItemDragListener; import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.dragndrop.DragView; import com.android.launcher3.icons.DotRenderer; import com.android.launcher3.touch.ItemClickHandler; +import com.android.launcher3.util.Executors; import com.android.launcher3.util.Thunk; import com.android.launcher3.views.IconLabelDotView; import com.android.launcher3.widget.PendingAddShortcutInfo; import java.util.ArrayList; import java.util.List; +import java.util.function.Predicate; /** * An icon that can appear on in the workspace representing an {@link Folder}. @@ -96,11 +99,11 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel PreviewBackground mBackground = new PreviewBackground(); private boolean mBackgroundIsVisible = true; - FolderIconPreviewVerifier mPreviewVerifier; + FolderGridOrganizer mPreviewVerifier; ClippedFolderIconLayoutRule mPreviewLayoutRule; private PreviewItemManager mPreviewItemManager; private PreviewItemDrawingParams mTmpParams = new PreviewItemDrawingParams(0, 0, 0, 0); - private List<BubbleTextView> mCurrentPreviewItems = new ArrayList<>(); + private List<WorkspaceItemInfo> mCurrentPreviewItems = new ArrayList<>(); boolean mAnimating = false; @@ -175,7 +178,7 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel icon.setOnClickListener(ItemClickHandler.INSTANCE); icon.mInfo = folderInfo; icon.mLauncher = launcher; - icon.mDotRenderer = grid.mDotRenderer; + icon.mDotRenderer = grid.mDotRendererWorkSpace; icon.setContentDescription(launcher.getString(R.string.folder_name_format, folderInfo.title)); Folder folder = Folder.fromXml(launcher); folder.setDragController(launcher.getDragController()); @@ -214,7 +217,7 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel private void setFolder(Folder folder) { mFolder = folder; - mPreviewVerifier = new FolderIconPreviewVerifier(mLauncher.getDeviceProfile().inv); + mPreviewVerifier = new FolderGridOrganizer(mLauncher.getDeviceProfile().inv); mPreviewVerifier.setFolderInfo(mFolder.getInfo()); updatePreviewItems(false); } @@ -232,11 +235,7 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel } public void addItem(WorkspaceItemInfo item) { - addItem(item, true); - } - - public void addItem(WorkspaceItemInfo item, boolean animate) { - mInfo.add(item, animate); + mInfo.add(item, true); } public void removeItem(WorkspaceItemInfo item, boolean animate) { @@ -261,7 +260,6 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel OnAlarmListener mOnOpenListener = new OnAlarmListener() { public void onAlarm(Alarm alarm) { mFolder.beginExternalDrag(); - mFolder.animateOpen(); } }; @@ -296,8 +294,7 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel } private void onDrop(final WorkspaceItemInfo item, DragView animateView, Rect finalRect, - float scaleRelativeToDragLayer, int index, - boolean itemReturnedOnFailedDrop) { + float scaleRelativeToDragLayer, int index, boolean itemReturnedOnFailedDrop) { item.cellX = -1; item.cellY = -1; @@ -328,18 +325,17 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel int numItemsInPreview = Math.min(MAX_NUM_ITEMS_IN_PREVIEW, index + 1); boolean itemAdded = false; if (itemReturnedOnFailedDrop || index >= MAX_NUM_ITEMS_IN_PREVIEW) { - List<BubbleTextView> oldPreviewItems = new ArrayList<>(mCurrentPreviewItems); - addItem(item, false); + List<WorkspaceItemInfo> oldPreviewItems = new ArrayList<>(mCurrentPreviewItems); + mInfo.add(item, index, false); mCurrentPreviewItems.clear(); - mCurrentPreviewItems.addAll(getPreviewItems()); + mCurrentPreviewItems.addAll(getPreviewItemsOnPage(0)); if (!oldPreviewItems.equals(mCurrentPreviewItems)) { - for (int i = 0; i < mCurrentPreviewItems.size(); ++i) { - if (mCurrentPreviewItems.get(i).getTag().equals(item)) { - // If the item dropped is going to be in the preview, we update the - // index here to reflect its position in the preview. - index = i; - } + int newIndex = mCurrentPreviewItems.indexOf(item); + if (newIndex >= 0) { + // If the item dropped is going to be in the preview, we update the + // index here to reflect its position in the preview. + index = newIndex; } mPreviewItemManager.hidePreviewItem(index, true); @@ -351,13 +347,13 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel } if (!itemAdded) { - addItem(item); + mInfo.add(item, index, true); } int[] center = new int[2]; float scale = getLocalCenterForIndex(index, numItemsInPreview, center); - center[0] = (int) Math.round(scaleRelativeToDragLayer * center[0]); - center[1] = (int) Math.round(scaleRelativeToDragLayer * center[1]); + center[0] = Math.round(scaleRelativeToDragLayer * center[0]); + center[1] = Math.round(scaleRelativeToDragLayer * center[1]); to.offset(center[0] - animateView.getMeasuredWidth() / 2, center[1] - animateView.getMeasuredHeight() / 2); @@ -374,12 +370,17 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel if (!itemAdded) mPreviewItemManager.hidePreviewItem(index, true); final int finalIndex = index; - postDelayed(new Runnable() { - public void run() { - mPreviewItemManager.hidePreviewItem(finalIndex, false); - mFolder.showItem(item); - invalidate(); - } + + String[] suggestedNameOut = new String[1]; + if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) { + Executors.UI_HELPER_EXECUTOR.post(() -> mLauncher.getFolderNameProvider() + .getSuggestedFolderName(getContext(), mInfo.contents, suggestedNameOut)); + } + postDelayed(() -> { + mPreviewItemManager.hidePreviewItem(finalIndex, false); + mFolder.showItem(item); + invalidate(); + mFolder.showSuggestedTitle(suggestedNameOut[0]); }, DROP_IN_ANIMATION_DURATION); } else { addItem(item); @@ -398,7 +399,8 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel item = (WorkspaceItemInfo) d.dragInfo; } mFolder.notifyDrop(); - onDrop(item, d.dragView, null, 1.0f, mInfo.contents.size(), + onDrop(item, d.dragView, null, 1.0f, + itemReturnedOnFailedDrop ? item.rank : mInfo.contents.size(), itemReturnedOnFailedDrop); } @@ -510,8 +512,7 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel mBackground.drawBackground(canvas); } - if (mFolder == null) return; - if (mFolder.getItemCount() == 0 && !mAnimating) return; + if (mCurrentPreviewItems.isEmpty() && !mAnimating) return; final int saveCount = canvas.save(); canvas.clipPath(mBackground.getClipPath()); @@ -553,31 +554,10 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel } /** - * Returns the list of preview items displayed in the icon. + * Returns the list of items which should be visible in the preview */ - 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<BubbleTextView> itemsOnPage = mFolder.getItemsOnPage(page); - int numItems = itemsOnPage.size(); - for (int rank = 0; rank < numItems; ++rank) { - if (mPreviewVerifier.isItemInPreview(page, rank)) { - itemsToDisplay.add(itemsOnPage.get(rank)); - } - - if (itemsToDisplay.size() == MAX_NUM_ITEMS_IN_PREVIEW) { - break; - } - } - return itemsToDisplay; + public List<WorkspaceItemInfo> getPreviewItemsOnPage(int page) { + return mPreviewVerifier.setFolderInfo(mInfo).previewItemsForPage(page, mInfo.contents); } @Override @@ -595,11 +575,14 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel private void updatePreviewItems(boolean animate) { mPreviewItemManager.updatePreviewItems(animate); mCurrentPreviewItems.clear(); - mCurrentPreviewItems.addAll(getPreviewItems()); + mCurrentPreviewItems.addAll(getPreviewItemsOnPage(0)); } - @Override - public void prepareAutoUpdate() { + /** + * Updates the preview items which match the provided condition + */ + public void updatePreviewItems(Predicate<WorkspaceItemInfo> itemCheck) { + mPreviewItemManager.updatePreviewItems(itemCheck); } @Override @@ -622,7 +605,6 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel requestLayout(); } - @Override public void onTitleChanged(CharSequence title) { mFolderName.setText(title); setContentDescription(getContext().getString(R.string.folder_name_format, title)); diff --git a/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java b/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java deleted file mode 100644 index 4c84e354d..000000000 --- a/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.launcher3.folder; - -import android.util.Log; - -import com.android.launcher3.FolderInfo; -import com.android.launcher3.InvariantDeviceProfile; - -import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW; - -/** - * Verifies whether an item in a Folder is displayed in the FolderIcon preview. - */ -public class FolderIconPreviewVerifier { - - private static final String TAG = "FolderPreviewVerifier"; - - private final int mMaxGridCountX; - private final int mMaxGridCountY; - private final int mMaxItemsPerPage; - private final int[] mGridSize = new int[] { 1, 1 }; - - private int mNumItemsInFolder; - private int mGridCountX; - private boolean mDisplayingUpperLeftQuadrant = false; - - /** - * Note: must call {@link #setFolderInfo(FolderInfo)} manually for verifier to work. - */ - public FolderIconPreviewVerifier(InvariantDeviceProfile profile) { - mMaxGridCountX = profile.numFolderColumns; - mMaxGridCountY = profile.numFolderRows; - mMaxItemsPerPage = mMaxGridCountX * mMaxGridCountY; - } - - public void setFolderInfo(FolderInfo info) { - int numItemsInFolder = info.contents.size(); - if (numItemsInFolder != mNumItemsInFolder) { - FolderPagedView.calculateGridSize(numItemsInFolder, 0, 0, mMaxGridCountX, - mMaxGridCountY, mMaxItemsPerPage, mGridSize); - mGridCountX = mGridSize[0]; - - mDisplayingUpperLeftQuadrant = numItemsInFolder > MAX_NUM_ITEMS_IN_PREVIEW; - mNumItemsInFolder = numItemsInFolder; - } - } - - /** - * 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 (mGridSize[0] == 1) { - Log.w(TAG, "setFolderInfo not called before checking if item is in preview."); - } - - // 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. - if (page > 0 || mDisplayingUpperLeftQuadrant) { - int col = rank % mGridCountX; - int row = rank / mGridCountX; - return col < 2 && row < 2; - } - return rank < MAX_NUM_ITEMS_IN_PREVIEW; - } -} diff --git a/src/com/android/launcher3/folder/FolderNameProvider.java b/src/com/android/launcher3/folder/FolderNameProvider.java new file mode 100644 index 000000000..0a1221e69 --- /dev/null +++ b/src/com/android/launcher3/folder/FolderNameProvider.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.folder; + +import android.content.ComponentName; +import android.content.Context; + +import com.android.launcher3.LauncherSettings.Favorites; +import com.android.launcher3.WorkspaceItemInfo; + +import java.util.ArrayList; + +/** + * Locates provider for the folder name. + */ +public class FolderNameProvider { + + /** + * Returns suggested folder name. + */ + public CharSequence getSuggestedFolderName(Context context, + ArrayList<WorkspaceItemInfo> workspaceItemInfos, CharSequence[] suggestName) { + // Currently only run the algorithm on initial folder creation. + // For more than 2 items in the folder, the ranking algorithm for finding + // candidate folder name should be rewritten. + if (workspaceItemInfos.size() == 2) { + ComponentName cmp1 = workspaceItemInfos.get(0).getTargetComponent(); + ComponentName cmp2 = workspaceItemInfos.get(1).getTargetComponent(); + + String pkgName0 = cmp1 == null ? "" : cmp1.getPackageName(); + String pkgName1 = cmp2 == null ? "" : cmp2.getPackageName(); + // If the two icons are from the same package, + // then assign the main icon's name + if (pkgName0.equals(pkgName1)) { + WorkspaceItemInfo wInfo0 = workspaceItemInfos.get(0); + WorkspaceItemInfo wInfo1 = workspaceItemInfos.get(1); + if (workspaceItemInfos.get(0).itemType == Favorites.ITEM_TYPE_APPLICATION) { + suggestName[0] = wInfo0.title; + } else if (wInfo1.itemType == Favorites.ITEM_TYPE_APPLICATION) { + suggestName[0] = wInfo1.title; + } + return suggestName[0]; + // two icons are all shortcuts. Don't assign title + } + } + return suggestName[0]; + } +} diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java index 57105e7ca..3b5fd5902 100644 --- a/src/com/android/launcher3/folder/FolderPagedView.java +++ b/src/com/android/launcher3/folder/FolderPagedView.java @@ -24,10 +24,10 @@ import android.util.ArrayMap; import android.util.AttributeSet; import android.util.Log; import android.view.Gravity; -import android.view.LayoutInflater; import android.view.View; import android.view.ViewDebug; +import com.android.launcher3.BaseActivity; import com.android.launcher3.BubbleTextView; import com.android.launcher3.CellLayout; import com.android.launcher3.DeviceProfile; @@ -38,18 +38,22 @@ import com.android.launcher3.LauncherAppState; import com.android.launcher3.PagedView; import com.android.launcher3.R; import com.android.launcher3.ShortcutAndWidgetContainer; -import com.android.launcher3.WorkspaceItemInfo; import com.android.launcher3.Utilities; import com.android.launcher3.Workspace.ItemOperator; +import com.android.launcher3.WorkspaceItemInfo; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.keyboard.ViewGroupFocusHelper; import com.android.launcher3.pageindicators.PageIndicatorDots; import com.android.launcher3.touch.ItemClickHandler; import com.android.launcher3.util.Thunk; +import com.android.launcher3.util.ViewCache; import java.util.ArrayList; import java.util.Iterator; +import java.util.List; import java.util.Map; +import java.util.function.ToIntFunction; +import java.util.stream.Collectors; public class FolderPagedView extends PagedView<PageIndicatorDots> { @@ -68,17 +72,12 @@ public class FolderPagedView extends PagedView<PageIndicatorDots> { public final boolean mIsRtl; - private final LayoutInflater mInflater; private final ViewGroupFocusHelper mFocusIndicatorHelper; @Thunk final ArrayMap<View, Runnable> mPendingAnimations = new ArrayMap<>(); - @ViewDebug.ExportedProperty(category = "launcher") - private final int mMaxCountX; - @ViewDebug.ExportedProperty(category = "launcher") - private final int mMaxCountY; - @ViewDebug.ExportedProperty(category = "launcher") - private final int mMaxItemsPerPage; + private final FolderGridOrganizer mOrganizer; + private final ViewCache mViewCache; private int mAllocatedContentSize; @ViewDebug.ExportedProperty(category = "launcher") @@ -88,20 +87,20 @@ public class FolderPagedView extends PagedView<PageIndicatorDots> { private Folder mFolder; + // If the views are attached to the folder or not. A folder should be bound when its + // animating or is open. + private boolean mViewsBound = false; + public FolderPagedView(Context context, AttributeSet attrs) { super(context, attrs); InvariantDeviceProfile profile = LauncherAppState.getIDP(context); - mMaxCountX = profile.numFolderColumns; - mMaxCountY = profile.numFolderRows; - - mMaxItemsPerPage = mMaxCountX * mMaxCountY; - - mInflater = LayoutInflater.from(context); + mOrganizer = new FolderGridOrganizer(profile); mIsRtl = Utilities.isRtl(getResources()); setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES); mFocusIndicatorHelper = new ViewGroupFocusHelper(this); + mViewCache = BaseActivity.fromContext(context).getViewCache(); } public void setFolder(Folder folder) { @@ -111,57 +110,13 @@ public class FolderPagedView extends PagedView<PageIndicatorDots> { } /** - * Calculates the grid size such that {@param count} items can fit in the grid. - * The grid size is calculated such that countY <= countX and countX = ceil(sqrt(count)) while - * maintaining the restrictions of {@link #mMaxCountX} & {@link #mMaxCountY}. - */ - public static void calculateGridSize(int count, int countX, int countY, int maxCountX, - int maxCountY, int maxItemsPerPage, int[] out) { - boolean done; - int gridCountX = countX; - int gridCountY = countY; - - if (count >= maxItemsPerPage) { - gridCountX = maxCountX; - gridCountY = maxCountY; - done = true; - } else { - done = false; - } - - while (!done) { - int oldCountX = gridCountX; - int oldCountY = gridCountY; - if (gridCountX * gridCountY < count) { - // Current grid is too small, expand it - if ((gridCountX <= gridCountY || gridCountY == maxCountY) - && gridCountX < maxCountX) { - gridCountX++; - } else if (gridCountY < maxCountY) { - gridCountY++; - } - if (gridCountY == 0) gridCountY++; - } else if ((gridCountY - 1) * gridCountX >= count && gridCountY >= gridCountX) { - gridCountY = Math.max(0, gridCountY - 1); - } else if ((gridCountX - 1) * gridCountY >= count) { - gridCountX = Math.max(0, gridCountX - 1); - } - done = gridCountX == oldCountX && gridCountY == oldCountY; - } - - out[0] = gridCountX; - out[1] = gridCountY; - } - - /** * Sets up the grid size such that {@param count} items can fit in the grid. */ - public void setupContentDimensions(int count) { + private void setupContentDimensions(int count) { mAllocatedContentSize = count; - calculateGridSize(count, mGridCountX, mGridCountY, mMaxCountX, mMaxCountY, mMaxItemsPerPage, - sTmpArray); - mGridCountX = sTmpArray[0]; - mGridCountY = sTmpArray[1]; + mOrganizer.setContentSize(count); + mGridCountX = mOrganizer.getCountX(); + mGridCountY = mOrganizer.getCountY(); // Update grid size for (int i = getPageCount() - 1; i >= 0; i--) { @@ -178,35 +133,50 @@ public class FolderPagedView extends PagedView<PageIndicatorDots> { /** * Binds items to the layout. */ - public void bindItems(ArrayList<WorkspaceItemInfo> items) { - ArrayList<View> icons = new ArrayList<>(); - for (WorkspaceItemInfo item : items) { - icons.add(createNewView(item)); + public void bindItems(List<WorkspaceItemInfo> items) { + if (mViewsBound) { + unbindItems(); } - arrangeChildren(icons, icons.size(), false); + arrangeChildren(items.stream().map(this::createNewView).collect(Collectors.toList())); + mViewsBound = true; } - public void allocateSpaceForRank(int rank) { - ArrayList<View> views = new ArrayList<>(mFolder.getItemsInReadingOrder()); - views.add(rank, null); - arrangeChildren(views, views.size(), false); + /** + * Removes all the icons from the folder + */ + public void unbindItems() { + for (int i = getChildCount() - 1; i >= 0; i--) { + CellLayout page = (CellLayout) getChildAt(i); + ShortcutAndWidgetContainer container = page.getShortcutsAndWidgets(); + for (int j = container.getChildCount() - 1; j >= 0; j--) { + mViewCache.recycleView(R.layout.folder_application, container.getChildAt(j)); + } + page.removeAllViews(); + mViewCache.recycleView(R.layout.folder_page, page); + } + removeAllViews(); + mViewsBound = false; } /** - * Create space for a new item at the end, and returns the rank for that item. - * Also sets the current page to the last page. + * Returns true if the icons are bound to the folder */ - public int allocateRankForNewItem() { - int rank = getItemCount(); - allocateSpaceForRank(rank); - setCurrentPage(rank / mMaxItemsPerPage); - return rank; + public boolean areViewsBound() { + return mViewsBound; } + /** + * Creates and adds an icon corresponding to the provided rank + * @return the created icon + */ public View createAndAddViewForRank(WorkspaceItemInfo item, int rank) { View icon = createNewView(item); - allocateSpaceForRank(rank); - addViewForRank(icon, item, rank); + if (!mViewsBound) { + return icon; + } + ArrayList<View> views = new ArrayList<>(mFolder.getIconsInReadingOrder()); + views.add(rank, icon); + arrangeChildren(views); return icon; } @@ -215,31 +185,33 @@ public class FolderPagedView extends PagedView<PageIndicatorDots> { * related attributes. It assumes that {@param item} is already attached to the view. */ public void addViewForRank(View view, WorkspaceItemInfo item, int rank) { - int pagePos = rank % mMaxItemsPerPage; - int pageNo = rank / mMaxItemsPerPage; - - item.rank = rank; - item.cellX = pagePos % mGridCountX; - item.cellY = pagePos / mGridCountX; + int pageNo = rank / mOrganizer.getMaxItemsPerPage(); CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams(); - lp.cellX = item.cellX; - lp.cellY = item.cellY; + lp.setXY(mOrganizer.getPosForRank(rank)); getPageAt(pageNo).addViewToCellLayout(view, -1, item.getViewId(), lp, true); } @SuppressLint("InflateParams") public View createNewView(WorkspaceItemInfo item) { - final BubbleTextView textView = (BubbleTextView) mInflater.inflate( - R.layout.folder_application, null, false); + if (item == null) { + return null; + } + final BubbleTextView textView = mViewCache.getView( + R.layout.folder_application, getContext(), null); textView.applyFromWorkspaceItem(item); - textView.setHapticFeedbackEnabled(false); textView.setOnClickListener(ItemClickHandler.INSTANCE); textView.setOnLongClickListener(mFolder); textView.setOnFocusChangeListener(mFocusIndicatorHelper); - - textView.setLayoutParams(new CellLayout.LayoutParams( - item.cellX, item.cellY, item.spanX, item.spanY)); + CellLayout.LayoutParams lp = (CellLayout.LayoutParams) textView.getLayoutParams(); + if (lp == null) { + textView.setLayoutParams(new CellLayout.LayoutParams( + item.cellX, item.cellY, item.spanX, item.spanY)); + } else { + lp.cellX = item.cellX; + lp.cellY = item.cellY; + lp.cellHSpan = lp.cellVSpan = 1; + } return textView; } @@ -254,7 +226,7 @@ public class FolderPagedView extends PagedView<PageIndicatorDots> { private CellLayout createAndAddNewPage() { DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile(); - CellLayout page = (CellLayout) mInflater.inflate(R.layout.folder_page, this, false); + CellLayout page = mViewCache.getView(R.layout.folder_page, getContext(), this); page.setCellDimensions(grid.folderCellWidthPx, grid.folderCellHeightPx); page.getShortcutsAndWidgets().setMotionEventSplittingEnabled(false); page.setInvertIfRtl(true); @@ -295,37 +267,28 @@ public class FolderPagedView extends PagedView<PageIndicatorDots> { * page. * * @param list the ordered list of children. - * @param itemCount if greater than the total children count, empty spaces are left - * at the end, otherwise it is ignored. - * */ - public void arrangeChildren(ArrayList<View> list, int itemCount) { - arrangeChildren(list, itemCount, true); - } - @SuppressLint("RtlHardcoded") - private void arrangeChildren(ArrayList<View> list, int itemCount, boolean saveChanges) { + public void arrangeChildren(List<View> list) { + int itemCount = list.size(); ArrayList<CellLayout> pages = new ArrayList<>(); for (int i = 0; i < getChildCount(); i++) { CellLayout page = (CellLayout) getChildAt(i); page.removeAllViews(); pages.add(page); } + mOrganizer.setFolderInfo(mFolder.getInfo()); setupContentDimensions(itemCount); Iterator<CellLayout> pageItr = pages.iterator(); CellLayout currentPage = null; int position = 0; - int newX, newY, rank; + int rank = 0; - FolderIconPreviewVerifier verifier = new FolderIconPreviewVerifier( - Launcher.getLauncher(getContext()).getDeviceProfile().inv); - verifier.setFolderInfo(mFolder.getInfo()); - rank = 0; for (int i = 0; i < itemCount; i++) { View v = list.size() > i ? list.get(i) : null; - if (currentPage == null || position >= mMaxItemsPerPage) { + if (currentPage == null || position >= mOrganizer.getMaxItemsPerPage()) { // Next page if (pageItr.hasNext()) { currentPage = pageItr.next(); @@ -337,28 +300,16 @@ public class FolderPagedView extends PagedView<PageIndicatorDots> { if (v != null) { CellLayout.LayoutParams lp = (CellLayout.LayoutParams) v.getLayoutParams(); - newX = position % mGridCountX; - newY = position / mGridCountX; ItemInfo info = (ItemInfo) v.getTag(); - if (info.cellX != newX || info.cellY != newY || info.rank != rank) { - info.cellX = newX; - info.cellY = newY; - info.rank = rank; - if (saveChanges) { - mFolder.mLauncher.getModelWriter().addOrMoveItemInDatabase(info, - mFolder.mInfo.id, 0, info.cellX, info.cellY); - } - } - lp.cellX = info.cellX; - lp.cellY = info.cellY; + lp.setXY(mOrganizer.getPosForRank(rank)); currentPage.addViewToCellLayout(v, -1, info.getViewId(), lp, true); - if (verifier.isItemInPreview(rank) && v instanceof BubbleTextView) { + if (mOrganizer.isItemInPreview(rank) && v instanceof BubbleTextView) { ((BubbleTextView) v).verifyHighRes(); } } - rank ++; + rank++; position++; } @@ -391,16 +342,6 @@ public class FolderPagedView extends PagedView<PageIndicatorDots> { (getPageAt(0).getDesiredHeight() + getPaddingTop() + getPaddingBottom()) : 0; } - public int getItemCount() { - int lastPageIndex = getChildCount() - 1; - if (lastPageIndex < 0) { - // If there are no pages, nothing has yet been added to the folder. - return 0; - } - return getPageAt(lastPageIndex).getShortcutsAndWidgets().getChildCount() - + lastPageIndex * mMaxItemsPerPage; - } - /** * @return the rank of the cell nearest to the provided pixel position. */ @@ -412,31 +353,28 @@ public class FolderPagedView extends PagedView<PageIndicatorDots> { sTmpArray[0] = page.getCountX() - sTmpArray[0] - 1; } return Math.min(mAllocatedContentSize - 1, - pageIndex * mMaxItemsPerPage + sTmpArray[1] * mGridCountX + sTmpArray[0]); + pageIndex * mOrganizer.getMaxItemsPerPage() + + sTmpArray[1] * mGridCountX + sTmpArray[0]); } public View getFirstItem() { - if (getChildCount() < 1) { - return null; - } - ShortcutAndWidgetContainer currContainer = getCurrentCellLayout().getShortcutsAndWidgets(); - if (mGridCountX > 0) { - return currContainer.getChildAt(0, 0); - } else { - return currContainer.getChildAt(0); - } + return getViewInCurrentPage(c -> 0); } public View getLastItem() { + return getViewInCurrentPage(c -> c.getChildCount() - 1); + } + + private View getViewInCurrentPage(ToIntFunction<ShortcutAndWidgetContainer> rankProvider) { if (getChildCount() < 1) { return null; } - ShortcutAndWidgetContainer currContainer = getCurrentCellLayout().getShortcutsAndWidgets(); - int lastRank = currContainer.getChildCount() - 1; + ShortcutAndWidgetContainer container = getCurrentCellLayout().getShortcutsAndWidgets(); + int rank = rankProvider.applyAsInt(container); if (mGridCountX > 0) { - return currContainer.getChildAt(lastRank % mGridCountX, lastRank / mGridCountX); + return container.getChildAt(rank % mGridCountX, rank / mGridCountX); } else { - return currContainer.getChildAt(lastRank); + return container.getChildAt(rank); } } @@ -517,7 +455,7 @@ public class FolderPagedView extends PagedView<PageIndicatorDots> { } public boolean rankOnCurrentPage(int rank) { - int p = rank / mMaxItemsPerPage; + int p = rank / mOrganizer.getMaxItemsPerPage(); return p == getNextPage(); } @@ -563,15 +501,16 @@ public class FolderPagedView extends PagedView<PageIndicatorDots> { // Animation only happens on the current page. int pageToAnimate = getNextPage(); + int maxItemsPerPage = mOrganizer.getMaxItemsPerPage(); - int pageT = target / mMaxItemsPerPage; - int pagePosT = target % mMaxItemsPerPage; + int pageT = target / maxItemsPerPage; + int pagePosT = target % maxItemsPerPage; if (pageT != pageToAnimate) { Log.e(TAG, "Cannot animate when the target cell is invisible"); } - int pagePosE = empty % mMaxItemsPerPage; - int pageE = empty / mMaxItemsPerPage; + int pagePosE = empty % maxItemsPerPage; + int pageE = empty / maxItemsPerPage; int startPos, endPos; int moveStart, moveEnd; @@ -588,7 +527,7 @@ public class FolderPagedView extends PagedView<PageIndicatorDots> { if (pageE < pageToAnimate) { moveStart = empty; // Instantly move the first item in the current page. - moveEnd = pageToAnimate * mMaxItemsPerPage; + moveEnd = pageToAnimate * maxItemsPerPage; // Animate the 2nd item in the current page, as the first item was already moved to // the last page. startPos = 0; @@ -606,10 +545,10 @@ public class FolderPagedView extends PagedView<PageIndicatorDots> { // Move the items immediately. moveStart = empty; // Instantly move the last item in the current page. - moveEnd = (pageToAnimate + 1) * mMaxItemsPerPage - 1; + moveEnd = (pageToAnimate + 1) * maxItemsPerPage - 1; // Animations start with the second last item in the page - startPos = mMaxItemsPerPage - 1; + startPos = maxItemsPerPage - 1; } else { moveStart = moveEnd = -1; startPos = pagePosE; @@ -621,8 +560,8 @@ public class FolderPagedView extends PagedView<PageIndicatorDots> { // Instant moving views. while (moveStart != moveEnd) { int rankToMove = moveStart + direction; - int p = rankToMove / mMaxItemsPerPage; - int pagePos = rankToMove % mMaxItemsPerPage; + int p = rankToMove / maxItemsPerPage; + int pagePos = rankToMove % maxItemsPerPage; int x = pagePos % mGridCountX; int y = pagePos / mGridCountX; @@ -667,9 +606,6 @@ public class FolderPagedView extends PagedView<PageIndicatorDots> { for (int i = startPos; i != endPos; i += direction) { int nextPos = i + direction; View v = page.getChildAt(nextPos % mGridCountX, nextPos / mGridCountX); - if (v != null) { - ((ItemInfo) v.getTag()).rank -= direction; - } if (page.animateChildToPosition(v, i % mGridCountX, i / mGridCountX, REORDER_ANIMATION_DURATION, delay, true, true)) { delay += delayAmount; @@ -679,6 +615,6 @@ public class FolderPagedView extends PagedView<PageIndicatorDots> { } public int itemsPerPage() { - return mMaxItemsPerPage; + return mOrganizer.getMaxItemsPerPage(); } } diff --git a/src/com/android/launcher3/folder/PreviewItemDrawingParams.java b/src/com/android/launcher3/folder/PreviewItemDrawingParams.java index c81846256..caf6e55b7 100644 --- a/src/com/android/launcher3/folder/PreviewItemDrawingParams.java +++ b/src/com/android/launcher3/folder/PreviewItemDrawingParams.java @@ -17,6 +17,8 @@ package com.android.launcher3.folder; import android.graphics.drawable.Drawable; +import com.android.launcher3.WorkspaceItemInfo; + /** * Manages the parameters used to draw a Folder preview item. */ @@ -25,9 +27,10 @@ class PreviewItemDrawingParams { float transY; float scale; float overlayAlpha; - FolderPreviewItemAnim anim; + public FolderPreviewItemAnim anim; public boolean hidden; - Drawable drawable; + public Drawable drawable; + public WorkspaceItemInfo item; PreviewItemDrawingParams(float transX, float transY, float scale, float overlayAlpha) { this.transX = transX; diff --git a/src/com/android/launcher3/folder/PreviewItemManager.java b/src/com/android/launcher3/folder/PreviewItemManager.java index 49763ba9d..2d817e6d2 100644 --- a/src/com/android/launcher3/folder/PreviewItemManager.java +++ b/src/com/android/launcher3/folder/PreviewItemManager.java @@ -23,28 +23,51 @@ import static com.android.launcher3.folder.FolderIcon.DROP_IN_ANIMATION_DURATION import android.animation.Animator; import android.animation.AnimatorListenerAdapter; +import android.animation.ObjectAnimator; import android.animation.ValueAnimator; +import android.content.Context; import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.util.FloatProperty; import android.view.View; import android.widget.TextView; -import com.android.launcher3.BubbleTextView; -import com.android.launcher3.WorkspaceItemInfo; +import androidx.annotation.NonNull; + +import com.android.launcher3.Launcher; import com.android.launcher3.Utilities; +import com.android.launcher3.WorkspaceItemInfo; +import com.android.launcher3.graphics.DrawableFactory; +import com.android.launcher3.graphics.PreloadIconDrawable; import java.util.ArrayList; import java.util.List; - -import androidx.annotation.NonNull; +import java.util.function.Predicate; /** * Manages the drawing and animations of {@link PreviewItemDrawingParams} for a {@link FolderIcon}. */ public class PreviewItemManager { - private FolderIcon mIcon; + private static final FloatProperty<PreviewItemManager> CURRENT_PAGE_ITEMS_TRANS_X = + new FloatProperty<PreviewItemManager>("currentPageItemsTransX") { + @Override + public void setValue(PreviewItemManager manager, float v) { + manager.mCurrentPageItemsTransX = v; + manager.onParamsChanged(); + } + + @Override + public Float get(PreviewItemManager manager) { + return manager.mCurrentPageItemsTransX; + } + }; + + private final Context mContext; + private final FolderIcon mIcon; + private final DrawableFactory mDrawableFactory; + private final int mIconSize; // These variables are all associated with the drawing of the preview; they are stored // as member variables for shared usage and to avoid computation on each frame @@ -69,7 +92,10 @@ public class PreviewItemManager { private static final int ITEM_SLIDE_IN_OUT_DISTANCE_PX = 200; public PreviewItemManager(FolderIcon icon) { + mContext = icon.getContext(); mIcon = icon; + mDrawableFactory = DrawableFactory.INSTANCE.get(mContext); + mIconSize = Launcher.getLauncher(mContext).getDeviceProfile().folderChildIconSizePx; } /** @@ -200,7 +226,7 @@ public class PreviewItemManager { } void buildParamsForPage(int page, ArrayList<PreviewItemDrawingParams> params, boolean animate) { - List<BubbleTextView> items = mIcon.getPreviewItemsOnPage(page); + List<WorkspaceItemInfo> items = mIcon.getPreviewItemsOnPage(page); int prevNumItems = params.size(); // We adjust the size of the list to match the number of items in the preview. @@ -214,13 +240,7 @@ public class PreviewItemManager { int numItemsInFirstPagePreview = page == 0 ? items.size() : MAX_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()) { - // Set the callback to FolderIcon as it is responsible to drawing the icon. The - // callback will be released when the folder is opened. - p.drawable.setCallback(mIcon); - } + setDrawable(p, items.get(i)); if (!animate) { computePreviewItemDrawingParams(i, numItemsInFirstPagePreview, p); @@ -253,14 +273,8 @@ public class PreviewItemManager { 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(); - } - }); + ValueAnimator slideAnimator = ObjectAnimator + .ofFloat(this, CURRENT_PAGE_ITEMS_TRANS_X, 0, ITEM_SLIDE_IN_OUT_DISTANCE_PX); slideAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -277,6 +291,25 @@ public class PreviewItemManager { buildParamsForPage(0, mFirstPageParams, animate); } + void updatePreviewItems(Predicate<WorkspaceItemInfo> itemCheck) { + boolean modified = false; + for (PreviewItemDrawingParams param : mFirstPageParams) { + if (itemCheck.test(param.item)) { + setDrawable(param, param.item); + modified = true; + } + } + for (PreviewItemDrawingParams param : mCurrentPageParams) { + if (itemCheck.test(param.item)) { + setDrawable(param, param.item); + modified = true; + } + } + if (modified) { + mIcon.invalidate(); + } + } + boolean verifyDrawable(@NonNull Drawable who) { for (int i = 0; i < mFirstPageParams.size(); i++) { if (mFirstPageParams.get(i).drawable == who) { @@ -296,46 +329,46 @@ public class PreviewItemManager { * - Moving into a new position * - Moving out of the preview * - * @param oldParams The list of items in the old preview. - * @param newParams The list of items in the new preview. + * @param oldItems The list of items in the old preview. + * @param newItems The list of items in the new preview. * @param dropped The item that was dropped onto the FolderIcon. */ - public void onDrop(List<BubbleTextView> oldParams, List<BubbleTextView> newParams, + public void onDrop(List<WorkspaceItemInfo> oldItems, List<WorkspaceItemInfo> newItems, WorkspaceItemInfo dropped) { - int numItems = newParams.size(); + int numItems = newItems.size(); final ArrayList<PreviewItemDrawingParams> params = mFirstPageParams; buildParamsForPage(0, params, false); // New preview items for items that are moving in (except for the dropped item). - List<BubbleTextView> moveIn = new ArrayList<>(); - for (BubbleTextView btv : newParams) { - if (!oldParams.contains(btv) && !btv.getTag().equals(dropped)) { - moveIn.add(btv); + List<WorkspaceItemInfo> moveIn = new ArrayList<>(); + for (WorkspaceItemInfo newItem : newItems) { + if (!oldItems.contains(newItem) && !newItem.equals(dropped)) { + moveIn.add(newItem); } } for (int i = 0; i < moveIn.size(); ++i) { - int prevIndex = newParams.indexOf(moveIn.get(i)); + int prevIndex = newItems.indexOf(moveIn.get(i)); PreviewItemDrawingParams p = params.get(prevIndex); computePreviewItemDrawingParams(prevIndex, numItems, p); - updateTransitionParam(p, moveIn.get(i), ENTER_INDEX, newParams.indexOf(moveIn.get(i)), + updateTransitionParam(p, moveIn.get(i), ENTER_INDEX, newItems.indexOf(moveIn.get(i)), numItems); } // Items that are moving into new positions within the preview. - for (int newIndex = 0; newIndex < newParams.size(); ++newIndex) { - int oldIndex = oldParams.indexOf(newParams.get(newIndex)); + for (int newIndex = 0; newIndex < newItems.size(); ++newIndex) { + int oldIndex = oldItems.indexOf(newItems.get(newIndex)); if (oldIndex >= 0 && newIndex != oldIndex) { PreviewItemDrawingParams p = params.get(newIndex); - updateTransitionParam(p, newParams.get(newIndex), oldIndex, newIndex, numItems); + updateTransitionParam(p, newItems.get(newIndex), oldIndex, newIndex, numItems); } } // Old preview items that need to be moved out. - List<BubbleTextView> moveOut = new ArrayList<>(oldParams); - moveOut.removeAll(newParams); + List<WorkspaceItemInfo> moveOut = new ArrayList<>(oldItems); + moveOut.removeAll(newItems); for (int i = 0; i < moveOut.size(); ++i) { - BubbleTextView item = moveOut.get(i); - int oldIndex = oldParams.indexOf(item); + WorkspaceItemInfo item = moveOut.get(i); + int oldIndex = oldItems.indexOf(item); PreviewItemDrawingParams p = computePreviewItemDrawingParams(oldIndex, numItems, null); updateTransitionParam(p, item, oldIndex, EXIT_INDEX, numItems); params.add(0, p); // We want these items first so that they are on drawn last. @@ -348,14 +381,9 @@ public class PreviewItemManager { } } - private void updateTransitionParam(final PreviewItemDrawingParams p, BubbleTextView btv, + private void updateTransitionParam(final PreviewItemDrawingParams p, WorkspaceItemInfo item, int prevIndex, int newIndex, int numItems) { - p.drawable = btv.getCompoundDrawables()[1]; - if (!mIcon.mFolder.isOpen()) { - // Set the callback to FolderIcon as it is responsible to drawing the icon. The - // callback will be released when the folder is opened. - p.drawable.setCallback(mIcon); - } + setDrawable(p, item); FolderPreviewItemAnim anim = new FolderPreviewItemAnim(this, p, prevIndex, numItems, newIndex, numItems, DROP_IN_ANIMATION_DURATION, null); @@ -364,4 +392,20 @@ public class PreviewItemManager { } p.anim = anim; } + + private void setDrawable(PreviewItemDrawingParams p, WorkspaceItemInfo item) { + if (item.hasPromiseIconUi()) { + PreloadIconDrawable drawable = mDrawableFactory.newPendingIcon(mContext, item); + drawable.setLevel(item.getInstallProgress()); + p.drawable = drawable; + } else { + p.drawable = mDrawableFactory.newIcon(mContext, item); + } + p.drawable.setBounds(0, 0, mIconSize, mIconSize); + p.item = item; + + // Set the callback to FolderIcon as it is responsible to drawing the icon. The + // callback will be released when the folder is opened. + p.drawable.setCallback(mIcon); + } } |