diff options
Diffstat (limited to 'src/com/android/launcher3/folder/Folder.java')
-rw-r--r-- | src/com/android/launcher3/folder/Folder.java | 253 |
1 files changed, 129 insertions, 124 deletions
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java index f22b53338..6b9e20525 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(); @@ -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() { 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. |