summaryrefslogtreecommitdiffstats
path: root/src/com/android/launcher3/folder
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/launcher3/folder')
-rw-r--r--src/com/android/launcher3/folder/Folder.java257
-rw-r--r--src/com/android/launcher3/folder/FolderAnimationManager.java101
-rw-r--r--src/com/android/launcher3/folder/FolderGridOrganizer.java196
-rw-r--r--src/com/android/launcher3/folder/FolderIcon.java104
-rw-r--r--src/com/android/launcher3/folder/FolderIconPreviewVerifier.java89
-rw-r--r--src/com/android/launcher3/folder/FolderNameProvider.java61
-rw-r--r--src/com/android/launcher3/folder/FolderPagedView.java262
-rw-r--r--src/com/android/launcher3/folder/PreviewItemDrawingParams.java7
-rw-r--r--src/com/android/launcher3/folder/PreviewItemManager.java134
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} &amp; {@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} &amp; {@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);
+ }
}