diff options
author | 2025-03-11 16:43:14 -0700 | |
---|---|---|
committer | 2025-03-13 10:28:07 -0700 | |
commit | 1ac78b6ec69d92ca868c3e1a5b0561fe7e5cbc6b (patch) | |
tree | f2d2927d866222153db472ba638f0e56ccbc91b6 /src | |
parent | a29ec52ccec438e4a7de77a19d7dffba70f3d129 (diff) |
Removing listeners from FolderInfo
Folder and FolderIcon are UI objects and can talk to each other directly.
FolderInfo changes are already propogated via model callbacks, and doesn't need a separate listener pattern.
Bug: 398791288
Flag: EXEMPT bugfix
Test: atest FolderTest
Change-Id: Iefd47b8ea12a8ecaf34211a3d908220a0e999187
Diffstat (limited to 'src')
-rw-r--r-- | src/com/android/launcher3/Launcher.java | 12 | ||||
-rw-r--r-- | src/com/android/launcher3/Workspace.java | 37 | ||||
-rw-r--r-- | src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java | 2 | ||||
-rw-r--r-- | src/com/android/launcher3/dot/FolderDotInfo.java | 13 | ||||
-rw-r--r-- | src/com/android/launcher3/folder/Folder.java | 162 | ||||
-rw-r--r-- | src/com/android/launcher3/folder/FolderIcon.java | 107 | ||||
-rw-r--r-- | src/com/android/launcher3/model/ModelWriter.java | 2 | ||||
-rw-r--r-- | src/com/android/launcher3/model/data/FolderInfo.java | 106 | ||||
-rw-r--r-- | src/com/android/launcher3/model/data/ItemInfo.java | 9 | ||||
-rw-r--r-- | src/com/android/launcher3/popup/PopupDataProvider.java | 7 |
10 files changed, 147 insertions, 310 deletions
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 289f175aa9..5c9392d69d 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -1472,8 +1472,7 @@ public class Launcher extends StatefulActivity<LauncherState> // Adding a shortcut to a Folder. FolderIcon folderIcon = findFolderIcon(container); if (folderIcon != null) { - FolderInfo folderInfo = (FolderInfo) folderIcon.getTag(); - folderInfo.add(info, args.rank, false); + folderIcon.getFolder().addFolderContent(info, args.rank, false); } else { Log.e(TAG, "Could not find folder with id " + container + " to add shortcut."); } @@ -1792,7 +1791,6 @@ public class Launcher extends StatefulActivity<LauncherState> SettingsCache.INSTANCE.get(this).unregister(TOUCHPAD_NATURAL_SCROLLING, mNaturalScrollingChangedListener); ScreenOnTracker.INSTANCE.get(this).removeListener(mScreenOnListener); - mWorkspace.removeFolderListeners(); PluginManagerWrapper.INSTANCE.get(this).removePluginListener(this); mModel.removeCallbacks(this); @@ -2053,9 +2051,10 @@ public class Launcher extends StatefulActivity<LauncherState> @Nullable final String reason) { if (itemInfo instanceof WorkspaceItemInfo) { View collectionIcon = mWorkspace.getViewByItemId(itemInfo.container); - if (collectionIcon instanceof FolderIcon) { + if (collectionIcon instanceof FolderIcon folderIcon) { // Remove the shortcut from the folder before removing it from launcher - ((FolderInfo) collectionIcon.getTag()).remove((WorkspaceItemInfo) itemInfo, true); + Folder folder = folderIcon.getFolder(); + folder.removeFolderContent(true, itemInfo); } else if (collectionIcon instanceof AppPairIcon appPairIcon) { removeItem(appPairIcon, appPairIcon.getInfo(), deleteFromDb, "removing app pair because one of its member apps was removed"); @@ -2066,9 +2065,6 @@ public class Launcher extends StatefulActivity<LauncherState> getModelWriter().deleteItemFromDatabase(itemInfo, reason); } } else if (itemInfo instanceof CollectionInfo ci) { - if (v instanceof FolderIcon) { - ((FolderIcon) v).removeListeners(); - } mWorkspace.removeWorkspaceItem(v); if (deleteFromDb) { getModelWriter().deleteCollectionAndContentsFromDatabase(ci); diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 6ed183ab23..59f84ab891 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -137,7 +137,6 @@ import java.util.Iterator; import java.util.List; import java.util.function.Consumer; import java.util.function.Predicate; -import java.util.stream.Collectors; /** * The workspace is a wide area with a wallpaper and a finite number of pages. @@ -659,7 +658,6 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> } // Remove the pages and clear the screen models - removeFolderListeners(); removeAllViews(); mScreenOrder.clear(); mWorkspaceScreens.clear(); @@ -1914,7 +1912,7 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> boolean aboveShortcut = Folder.willAccept(dropOverView.getTag()) && ((ItemInfo) dropOverView.getTag()).container != CONTAINER_HOTSEAT_PREDICTION; - boolean willBecomeShortcut = Folder.willAcceptItemType(info.itemType); + boolean willBecomeShortcut = FolderInfo.willAcceptItemType(info.itemType); return (aboveShortcut && willBecomeShortcut); } @@ -1994,8 +1992,8 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> fi.performCreateAnimation(destInfo, v, sourceInfo, d, folderLocation, scale); } else { fi.prepareCreateAnimation(v); - fi.addItem(destInfo); - fi.addItem(sourceInfo); + fi.getFolder().addFolderContent(destInfo); + fi.getFolder().addFolderContent(sourceInfo); } return true; } @@ -3209,21 +3207,6 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> }); } - /** - * Removes all folder listeners - */ - public void removeFolderListeners() { - mapOverItems(new ItemOperator() { - @Override - public boolean evaluate(ItemInfo info, View view) { - if (view instanceof FolderIcon) { - ((FolderIcon) view).removeListeners(); - } - return false; - } - }); - } - public boolean isDropEnabled() { return true; } @@ -3349,15 +3332,15 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> if (child instanceof DropTarget) { mDragController.removeDropTarget((DropTarget) child); } - } else if (child instanceof FolderIcon) { + } else if (child instanceof FolderIcon folderIcon) { FolderInfo folderInfo = (FolderInfo) info; - List<ItemInfo> matches = folderInfo.getContents().stream() + ItemInfo[] matches = folderInfo.getContents().stream() .filter(matcher) - .collect(Collectors.toList()); - if (!matches.isEmpty()) { - folderInfo.removeAll(matches, false); - if (((FolderIcon) child).getFolder().isOpen()) { - ((FolderIcon) child).getFolder().close(false /* animate */); + .toArray(ItemInfo[]::new); + if (matches.length > 0) { + folderIcon.getFolder().removeFolderContent(false, matches); + if (folderIcon.getFolder().isOpen()) { + folderIcon.getFolder().close(false /* animate */); } } } else if (info instanceof AppPairInfo api) { diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java index cd91f8e00b..df34ccf090 100644 --- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java +++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java @@ -519,7 +519,7 @@ public class LauncherAccessibilityDelegate extends BaseAccessibilityDelegate<Lau Folder folder = Folder.getOpen(mContext); folder.close(true); WorkspaceItemInfo info = (WorkspaceItemInfo) item; - folder.getInfo().remove(info, false); + folder.removeFolderContent(false, info); final int[] coordinates = new int[2]; final int screenId = findSpaceOnWorkspace(item, coordinates); diff --git a/src/com/android/launcher3/dot/FolderDotInfo.java b/src/com/android/launcher3/dot/FolderDotInfo.java index 54800a07a8..cb91db7654 100644 --- a/src/com/android/launcher3/dot/FolderDotInfo.java +++ b/src/com/android/launcher3/dot/FolderDotInfo.java @@ -30,6 +30,10 @@ public class FolderDotInfo extends DotInfo { private int mNumNotifications; + public void reset() { + mNumNotifications = 0; + } + public void addDotInfo(DotInfo dotToAdd) { if (dotToAdd == null) { return; @@ -39,15 +43,6 @@ public class FolderDotInfo extends DotInfo { mNumNotifications, MIN_COUNT, DotInfo.MAX_COUNT); } - public void subtractDotInfo(DotInfo dotToSubtract) { - if (dotToSubtract == null) { - return; - } - mNumNotifications -= dotToSubtract.getNotificationKeys().size(); - mNumNotifications = Utilities.boundToRange( - mNumNotifications, MIN_COUNT, DotInfo.MAX_COUNT); - } - @Override public int getNotificationCount() { return mNumNotifications; diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java index 0ae95196fd..967af053fb 100644 --- a/src/com/android/launcher3/folder/Folder.java +++ b/src/com/android/launcher3/folder/Folder.java @@ -19,9 +19,6 @@ package com.android.launcher3.folder; import static android.text.TextUtils.isEmpty; import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY; -import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; -import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR; -import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT; import static com.android.launcher3.LauncherState.EDIT_MODE; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent; @@ -29,6 +26,7 @@ import static com.android.launcher3.config.FeatureFlags.ALWAYS_USE_HARDWARE_OPTI import static com.android.launcher3.folder.FolderGridOrganizer.createFolderGridOrganizer; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_LABEL_UPDATED; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED; +import static com.android.launcher3.model.data.FolderInfo.willAcceptItemType; import static com.android.launcher3.testing.shared.TestProtocol.FOLDER_OPENED_MESSAGE; import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs; @@ -95,7 +93,6 @@ import com.android.launcher3.logger.LauncherAtom.ToState; import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.logging.StatsLogManager.StatsLogger; import com.android.launcher3.model.data.FolderInfo; -import com.android.launcher3.model.data.FolderInfo.FolderListener; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.WorkspaceItemFactory; import com.android.launcher3.model.data.WorkspaceItemInfo; @@ -111,6 +108,7 @@ import com.android.launcher3.widget.PendingAddShortcutInfo; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; +import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.Objects; @@ -122,7 +120,7 @@ import java.util.stream.Stream; * Represents a set of icons chosen by the user or generated by the system. */ public class Folder extends AbstractFloatingView implements ClipPathView, DragSource, - View.OnLongClickListener, DropTarget, FolderListener, TextView.OnEditorActionListener, + View.OnLongClickListener, DropTarget, TextView.OnEditorActionListener, View.OnFocusChangeListener, DragListener, ExtendedEditText.OnBackKeyListener, LauncherBindableItemsContainer { private static final String TAG = "Launcher.Folder"; @@ -179,15 +177,6 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo return o instanceof ItemInfo info && willAcceptItemType(info.itemType); } - /** - * Checks if {@code itemType} is a type that can be placed in folders. - */ - public static boolean willAcceptItemType(int itemType) { - return itemType == ITEM_TYPE_APPLICATION - || itemType == ITEM_TYPE_DEEP_SHORTCUT - || itemType == ITEM_TYPE_APP_PAIR; - } - private Alarm mReorderAlarm = new Alarm(Looper.getMainLooper()); private Alarm mOnExitAlarm = new Alarm(Looper.getMainLooper()); private Alarm mOnScrollHintAlarm = new Alarm(Looper.getMainLooper()); @@ -243,7 +232,10 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo private boolean mIsExternalDrag; private boolean mIsDragInProgress = false; private boolean mDeleteFolderOnDropCompleted = false; + private boolean mSuppressFolderDeletion = false; + private boolean mSuppressContentUpdate = false; + private boolean mItemAddedBackToSelfViaIcon = false; private boolean mIsEditingName = false; @@ -385,9 +377,8 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo // We do not want to get events for the item being removed, as they will get handled // when the drop completes - try (SuppressInfoChanges s = new SuppressInfoChanges()) { - mInfo.remove(dragObject.dragInfo, true); - } + executeWithContentUpdateSuppressed(() -> removeFolderContent(true, dragObject.dragInfo)); + mIsDragInProgress = true; mItemAddedBackToSelfViaIcon = false; } @@ -532,8 +523,17 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo lp.customPosition = true; setLayoutParams(lp); } + reapplyItemInfo(); + // In case any children didn't come across during loading, clean up the folder accordingly + mFolderIcon.post(() -> { + if (getItemCount() <= 1) { + replaceFolderWithFinalItem(); + } + }); + } + + public void reapplyItemInfo() { mItemsInvalidated = true; - mInfo.addListener(this); if (!isEmpty(mInfo.title)) { mFolderName.setText(mInfo.title); @@ -542,15 +542,8 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo mFolderName.setText(""); mFolderName.setHint(R.string.folder_hint_text); } - // In case any children didn't come across during loading, clean up the folder accordingly - mFolderIcon.post(() -> { - if (getItemCount() <= 1) { - replaceFolderWithFinalItem(); - } - }); } - /** * Show suggested folder title in FolderEditText if the first suggestion is non-empty, push * rest of the suggestions to InputMethodManager. @@ -680,7 +673,6 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo if (!shouldAnimateOpen(items)) { return; } - Folder openFolder = getOpen(mActivityContext); closeOpenFolder(openFolder); @@ -954,9 +946,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo @Override public boolean acceptDrop(DragObject d) { - final ItemInfo item = d.dragInfo; - final int itemType = item.itemType; - return Folder.willAcceptItemType(itemType); + return willAcceptItemType(d.dragInfo.itemType); } public void onDragEnter(DragObject d) { @@ -1121,9 +1111,8 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo mContent.arrangeChildren(views); mItemsInvalidated = true; - try (SuppressInfoChanges s = new SuppressInfoChanges()) { - mFolderIcon.onDrop(d, true /* itemReturnedOnFailedDrop */); - } + executeWithContentUpdateSuppressed( + () -> mFolderIcon.onDrop(d, true /* itemReturnedOnFailedDrop */)); } } @@ -1417,9 +1406,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo rearrangeChildren(); // Temporarily suppress the listener, as we did all the work already here. - try (SuppressInfoChanges s = new SuppressInfoChanges()) { - mInfo.add(si, mEmptyCellRank, false); - } + executeWithContentUpdateSuppressed(() -> addFolderContent(si, mEmptyCellRank, false)); // We only need to update the locations if it doesn't get handled in // #onDropCompleted. @@ -1465,37 +1452,66 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo } } - @Override - public void onAdd(ItemInfo item, int rank) { - FolderGridOrganizer verifier = createFolderGridOrganizer( - mActivityContext.getDeviceProfile()).setFolderInfo(mInfo); - verifier.updateRankAndPos(item, rank); - mLauncherDelegate.getModelWriter().addOrMoveItemInDatabase(item, mInfo.id, 0, item.cellX, - item.cellY); - updateItemLocationsInDatabaseBatch(false); + /** Add an app or shortcut */ + public void addFolderContent(ItemInfo item) { + addFolderContent(item, mInfo.getContents().size(), true); + } - if (mContent.areViewsBound()) { - mContent.createAndAddViewForRank(item, rank); + /** Add an app or shortcut for a specified rank */ + public void addFolderContent(ItemInfo item, int rank, boolean animate) { + if (!willAcceptItemType(item.itemType)) { + throw new RuntimeException("tried to add an illegal type into a folder"); } - mItemsInvalidated = true; + + rank = Utilities.boundToRange(rank, 0, mInfo.getContents().size()); + mInfo.getContents().add(rank, item); + + if (!mSuppressContentUpdate) { + FolderGridOrganizer verifier = createFolderGridOrganizer( + mActivityContext.getDeviceProfile()).setFolderInfo(mInfo); + verifier.updateRankAndPos(item, rank); + mLauncherDelegate.getModelWriter().addOrMoveItemInDatabase(item, mInfo.id, 0, + item.cellX, + item.cellY); + updateItemLocationsInDatabaseBatch(false); + + if (mContent.areViewsBound()) { + mContent.createAndAddViewForRank(item, rank); + } + mItemsInvalidated = true; + updateTextViewFocus(); + } + + mLauncherDelegate.getModelWriter().notifyItemModified(mInfo); + mFolderIcon.onItemsChanged(animate); } - @Override - public void onRemove(List<ItemInfo> items) { - mItemsInvalidated = true; - items.stream().map(this::getViewForInfo).forEach(mContent::removeItem); - if (mState == STATE_ANIMATING) { - mRearrangeOnClose = true; - } else { - rearrangeChildren(); + /** Remove all matching app or shortcut. Does not change the DB. */ + public void removeFolderContent(boolean animate, ItemInfo... items) { + List<ItemInfo> itemArray = Arrays.asList(items); + if (mInfo.getContents().removeAll(itemArray)) { + mLauncherDelegate.getModelWriter().notifyItemModified(mInfo); } - if (getItemCount() <= 1) { - if (mIsOpen) { - close(true); + + if (!mSuppressContentUpdate) { + mItemsInvalidated = true; + itemArray.forEach(item -> mContent.removeItem(getViewForInfo(item))); + if (mState == STATE_ANIMATING) { + mRearrangeOnClose = true; } else { - replaceFolderWithFinalItem(); + rearrangeChildren(); + } + if (getItemCount() <= 1) { + if (mIsOpen) { + close(true); + } else { + replaceFolderWithFinalItem(); + } } + updateTextViewFocus(); } + + mFolderIcon.onItemsChanged(animate); } @VisibleForTesting @@ -1503,16 +1519,6 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo return mContent.iterateOverItems((info, view) -> info == item); } - @Override - public void onItemsChanged(boolean animate) { - updateTextViewFocus(); - } - - @Override - public void onTitleChanged(CharSequence title) { - mFolderName.setText(title); - } - /** * Utility methods to iterate over items of the view */ @@ -1666,18 +1672,14 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo } }; - /** - * Temporary resource held while we don't want to handle info changes - */ - private class SuppressInfoChanges implements AutoCloseable { - - SuppressInfoChanges() { - mInfo.removeListener(Folder.this); - } - - @Override - public void close() { - mInfo.addListener(Folder.this); + /** Executes the task while suppressing the content update for the folder */ + private void executeWithContentUpdateSuppressed(Runnable task) { + if (mSuppressContentUpdate) { + task.run(); + } else { + mSuppressContentUpdate = true; + task.run(); + mSuppressContentUpdate = false; updateTextViewFocus(); } } diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java index 0ed87871cc..1cd9bcc965 100644 --- a/src/com/android/launcher3/folder/FolderIcon.java +++ b/src/com/android/launcher3/folder/FolderIcon.java @@ -23,6 +23,7 @@ import static com.android.launcher3.folder.PreviewItemManager.INITIAL_ITEM_ANIMA import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_AUTO_LABELED; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_AUTO_LABELING_SKIPPED_EMPTY_PRIMARY; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_AUTO_LABELING_SKIPPED_EMPTY_SUGGESTIONS; +import static com.android.launcher3.model.data.FolderInfo.willAcceptItemType; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -73,7 +74,6 @@ import com.android.launcher3.logging.InstanceId; import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.model.data.AppPairInfo; import com.android.launcher3.model.data.FolderInfo; -import com.android.launcher3.model.data.FolderInfo.FolderListener; import com.android.launcher3.model.data.FolderInfo.LabelState; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.WorkspaceItemFactory; @@ -92,7 +92,7 @@ import java.util.function.Predicate; /** * An icon that can appear on in the workspace representing an {@link Folder}. */ -public class FolderIcon extends FrameLayout implements FolderListener, FloatingIconViewCompanion, +public class FolderIcon extends FrameLayout implements FloatingIconViewCompanion, DraggableView, Reorderable { private final MultiTranslateDelegate mTranslateDelegate = new MultiTranslateDelegate(this); @@ -127,7 +127,7 @@ public class FolderIcon extends FrameLayout implements FolderListener, FloatingI private boolean mForceHideDot; @ViewDebug.ExportedProperty(category = "launcher", deepExport = true) - private FolderDotInfo mDotInfo = new FolderDotInfo(); + private final FolderDotInfo mDotInfo = new FolderDotInfo(); private DotRenderer mDotRenderer; @ViewDebug.ExportedProperty(category = "launcher", deepExport = true) private DotRenderer.DrawParams mDotParams; @@ -178,7 +178,6 @@ public class FolderIcon extends FrameLayout implements FolderListener, FloatingI folder.bind(folderInfo); icon.setFolder(folder); - folderInfo.addListener(icon); return icon; } @@ -217,13 +216,7 @@ public class FolderIcon extends FrameLayout implements FolderListener, FloatingI icon.mDotRenderer = grid.mDotRendererWorkSpace; icon.setContentDescription(icon.getAccessiblityTitle(folderInfo.title)); - - // Keep the notification dot up to date with the sum of all the content's dots. - FolderDotInfo folderDotInfo = new FolderDotInfo(); - for (ItemInfo si : folderInfo.getContents()) { - folderDotInfo.addDotInfo(activity.getDotInfoForItem(si)); - } - icon.setDotInfo(folderDotInfo); + icon.updateDotInfo(); icon.setAccessibilityDelegate(activity.getAccessibilityDelegate()); @@ -264,22 +257,13 @@ public class FolderIcon extends FrameLayout implements FolderListener, FloatingI } private boolean willAcceptItem(ItemInfo item) { - final int itemType = item.itemType; - return (Folder.willAcceptItemType(itemType) && item != mInfo && !mFolder.isOpen()); + return (willAcceptItemType(item.itemType) && item != mInfo && !mFolder.isOpen()); } public boolean acceptDrop(ItemInfo dragInfo) { return !mFolder.isDestroyed() && willAcceptItem(dragInfo); } - public void addItem(ItemInfo item) { - mInfo.add(item, true); - } - - public void removeItem(ItemInfo item, boolean animate) { - mInfo.remove(item, animate); - } - public void onDragEnter(ItemInfo dragInfo) { if (mFolder.isDestroyed() || !willAcceptItem(dragInfo)) return; CellLayoutLayoutParams lp = (CellLayoutLayoutParams) getLayoutParams(); @@ -308,9 +292,8 @@ public class FolderIcon extends FrameLayout implements FolderListener, FloatingI public void performCreateAnimation(final ItemInfo destInfo, final View destView, final ItemInfo srcInfo, final DragObject d, Rect dstRect, float scaleRelativeToDragLayer) { - final DragView srcView = d.dragView; prepareCreateAnimation(destView); - addItem(destInfo); + getFolder().addFolderContent(destInfo); // This will animate the first item from it's position as an icon into its // position as the first item in the preview mPreviewItemManager.createFirstItemAnimation(false /* reverse */, null) @@ -364,7 +347,7 @@ public class FolderIcon extends FrameLayout implements FolderListener, FloatingI boolean itemAdded = false; if (itemReturnedOnFailedDrop || index >= MAX_NUM_ITEMS_IN_PREVIEW) { List<ItemInfo> oldPreviewItems = new ArrayList<>(mCurrentPreviewItems); - mInfo.add(item, index, false); + getFolder().addFolderContent(item, index, false); mCurrentPreviewItems.clear(); mCurrentPreviewItems.addAll(getPreviewItemsOnPage(0)); @@ -380,12 +363,12 @@ public class FolderIcon extends FrameLayout implements FolderListener, FloatingI mPreviewItemManager.onDrop(oldPreviewItems, mCurrentPreviewItems, item); itemAdded = true; } else { - removeItem(item, false); + getFolder().removeFolderContent(false, item); } } if (!itemAdded) { - mInfo.add(item, index, true); + getFolder().addFolderContent(item, index, true); } int[] center = new int[2]; @@ -431,7 +414,7 @@ public class FolderIcon extends FrameLayout implements FolderListener, FloatingI }, DROP_IN_ANIMATION_DURATION); }); } else { - addItem(item); + getFolder().addFolderContent(item); } } @@ -500,9 +483,23 @@ public class FolderIcon extends FrameLayout implements FolderListener, FloatingI ); } - public void setDotInfo(FolderDotInfo dotInfo) { - updateDotScale(mDotInfo.hasDot(), dotInfo.hasDot()); - mDotInfo = dotInfo; + /** Keep the notification dot up to date with the sum of all the content's dots. */ + public void updateDotInfo() { + boolean hadDot = mDotInfo.hasDot(); + mDotInfo.reset(); + for (ItemInfo si : mInfo.getContents()) { + mDotInfo.addDotInfo(mActivity.getDotInfoForItem(si)); + } + boolean isDotted = mDotInfo.hasDot(); + float newDotScale = isDotted ? 1f : 0f; + // Animate when a dot is first added or when it is removed. + if ((hadDot ^ isDotted) && isShown()) { + animateDotScale(newDotScale); + } else { + cancelDotScaleAnim(); + mDotScale = newDotScale; + invalidate(); + } } public ClippedFolderIconLayoutRule getLayoutRule() { @@ -523,22 +520,6 @@ public class FolderIcon extends FrameLayout implements FolderListener, FloatingI } } - /** - * Sets mDotScale to 1 or 0, animating if wasDotted or isDotted is false - * (the dot is being added or removed). - */ - private void updateDotScale(boolean wasDotted, boolean isDotted) { - float newDotScale = isDotted ? 1f : 0f; - // Animate when a dot is first added or when it is removed. - if ((wasDotted ^ isDotted) && isShown()) { - animateDotScale(newDotScale); - } else { - cancelDotScaleAnim(); - mDotScale = newDotScale; - invalidate(); - } - } - private void cancelDotScaleAnim() { if (mDotScaleAnim != null) { mDotScaleAnim.cancel(); @@ -682,13 +663,6 @@ public class FolderIcon extends FrameLayout implements FolderListener, FloatingI return mPreviewItemManager.verifyDrawable(who) || super.verifyDrawable(who); } - @Override - public void onItemsChanged(boolean animate) { - updatePreviewItems(animate); - invalidate(); - requestLayout(); - } - private void updatePreviewItems(boolean animate) { mPreviewItemManager.updatePreviewItems(animate); mCurrentPreviewItems.clear(); @@ -702,31 +676,15 @@ public class FolderIcon extends FrameLayout implements FolderListener, FloatingI mPreviewItemManager.updatePreviewItems(itemCheck); } - @Override - public void onAdd(ItemInfo item, int rank) { - updatePreviewItems(false); - boolean wasDotted = mDotInfo.hasDot(); - mDotInfo.addDotInfo(mActivity.getDotInfoForItem(item)); - boolean isDotted = mDotInfo.hasDot(); - updateDotScale(wasDotted, isDotted); - setContentDescription(getAccessiblityTitle(mInfo.title)); - invalidate(); - requestLayout(); - } - - @Override - public void onRemove(List<ItemInfo> items) { + public void onItemsChanged(boolean animate) { updatePreviewItems(false); - boolean wasDotted = mDotInfo.hasDot(); - items.stream().map(mActivity::getDotInfoForItem).forEach(mDotInfo::subtractDotInfo); - boolean isDotted = mDotInfo.hasDot(); - updateDotScale(wasDotted, isDotted); + updateDotInfo(); setContentDescription(getAccessiblityTitle(mInfo.title)); + updatePreviewItems(animate); invalidate(); requestLayout(); } - @Override public void onTitleChanged(CharSequence title) { mFolderName.setText(title); setContentDescription(getAccessiblityTitle(title)); @@ -762,11 +720,6 @@ public class FolderIcon extends FrameLayout implements FolderListener, FloatingI mLongPressHelper.cancelLongPress(); } - public void removeListeners() { - mInfo.removeListener(this); - mInfo.removeListener(mFolder); - } - private boolean isInHotseat() { return mInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT; } diff --git a/src/com/android/launcher3/model/ModelWriter.java b/src/com/android/launcher3/model/ModelWriter.java index 0332775224..dc103a6a3d 100644 --- a/src/com/android/launcher3/model/ModelWriter.java +++ b/src/com/android/launcher3/model/ModelWriter.java @@ -229,7 +229,7 @@ public class ModelWriter { }).executeOnModelThread(); } - private void notifyItemModified(ItemInfo item) { + public void notifyItemModified(ItemInfo item) { notifyOtherCallbacks(c -> c.bindItemsModified(Collections.singletonList(item))); } diff --git a/src/com/android/launcher3/model/data/FolderInfo.java b/src/com/android/launcher3/model/data/FolderInfo.java index 9656ac10b2..4c792a7672 100644 --- a/src/com/android/launcher3/model/data/FolderInfo.java +++ b/src/com/android/launcher3/model/data/FolderInfo.java @@ -20,6 +20,9 @@ import static android.text.TextUtils.isEmpty; import static androidx.core.util.Preconditions.checkNotNull; +import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; +import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR; +import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT; import static com.android.launcher3.logger.LauncherAtom.Attribute.EMPTY_LABEL; import static com.android.launcher3.logger.LauncherAtom.Attribute.MANUAL_LABEL; import static com.android.launcher3.logger.LauncherAtom.Attribute.SUGGESTED_LABEL; @@ -30,8 +33,6 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.launcher3.LauncherSettings; -import com.android.launcher3.Utilities; -import com.android.launcher3.folder.Folder; import com.android.launcher3.folder.FolderNameInfos; import com.android.launcher3.logger.LauncherAtom; import com.android.launcher3.logger.LauncherAtom.Attribute; @@ -42,8 +43,6 @@ import com.android.launcher3.model.ModelWriter; import com.android.launcher3.util.ContentWriter; import java.util.ArrayList; -import java.util.Collections; -import java.util.List; import java.util.OptionalInt; import java.util.stream.IntStream; @@ -52,18 +51,6 @@ import java.util.stream.IntStream; */ public class FolderInfo extends CollectionInfo { - public static final int NO_FLAGS = 0x00000000; - - /** - * The folder is locked in sorted mode - */ - public static final int FLAG_ITEMS_SORTED = 0x00000001; - - /** - * It is a work folder - */ - public static final int FLAG_WORK_FOLDER = 0x00000002; - /** * The multi-page animation has run for this folder */ @@ -95,8 +82,6 @@ public class FolderInfo extends CollectionInfo { } } - public static final String EXTRA_FOLDER_SUGGESTIONS = "suggest"; - public int options; public FolderNameInfos suggestedFolderNames; @@ -106,61 +91,16 @@ public class FolderInfo extends CollectionInfo { */ private final ArrayList<ItemInfo> contents = new ArrayList<>(); - private ArrayList<FolderListener> mListeners = new ArrayList<>(); - public FolderInfo() { itemType = LauncherSettings.Favorites.ITEM_TYPE_FOLDER; } - /** Adds a app or shortcut to the contents ArrayList without animation. */ @Override public void add(@NonNull ItemInfo item) { - add(item, false /* animate */); - } - - /** - * Add an app or shortcut - * - * @param item - */ - public void add(ItemInfo item, boolean animate) { - add(item, getContents().size(), animate); - } - - /** - * Add an app or shortcut for a specified rank. - */ - public void add(ItemInfo item, int rank, boolean animate) { - if (!Folder.willAccept(item)) { + if (!willAcceptItemType(item.itemType)) { throw new RuntimeException("tried to add an illegal type into a folder"); } - - rank = Utilities.boundToRange(rank, 0, getContents().size()); - getContents().add(rank, item); - for (int i = 0; i < mListeners.size(); i++) { - mListeners.get(i).onAdd(item, rank); - } - itemsChanged(animate); - } - - /** - * Remove an app or shortcut. Does not change the DB. - * - * @param item - */ - public void remove(ItemInfo item, boolean animate) { - removeAll(Collections.singletonList(item), animate); - } - - /** - * Remove all matching app or shortcut. Does not change the DB. - */ - public void removeAll(List<ItemInfo> items, boolean animate) { - contents.removeAll(items); - for (int i = 0; i < mListeners.size(); i++) { - mListeners.get(i).onRemove(items); - } - itemsChanged(animate); + getContents().add(item); } /** @@ -197,28 +137,6 @@ public class FolderInfo extends CollectionInfo { writer.put(LauncherSettings.Favorites.OPTIONS, options); } - public void addListener(FolderListener listener) { - mListeners.add(listener); - } - - public void removeListener(FolderListener listener) { - mListeners.remove(listener); - } - - public void itemsChanged(boolean animate) { - for (int i = 0; i < mListeners.size(); i++) { - mListeners.get(i).onItemsChanged(animate); - } - } - - public interface FolderListener { - void onAdd(ItemInfo item, int rank); - void onRemove(List<ItemInfo> item); - void onItemsChanged(boolean animate); - void onTitleChanged(CharSequence title); - - } - public boolean hasOption(int optionFlag) { return (options & optionFlag) != 0; } @@ -261,7 +179,6 @@ public class FolderInfo extends CollectionInfo { .build(); } - @Override public void setTitle(@Nullable CharSequence title, ModelWriter modelWriter) { // Updating label from null to empty is considered as false touch. // Retaining null title(ie., UNLABELED state) allows auto-labeling when new items added. @@ -289,10 +206,6 @@ public class FolderInfo extends CollectionInfo { if (modelWriter != null) { modelWriter.updateItemInDatabase(this); } - - for (int i = 0; i < mListeners.size(); i++) { - mListeners.get(i).onTitleChanged(title); - } } /** @@ -401,4 +314,13 @@ public class FolderInfo extends CollectionInfo { } return LauncherAtom.ToState.TO_STATE_UNSPECIFIED; } + + /** + * Checks if {@code itemType} is a type that can be placed in folders. + */ + public static boolean willAcceptItemType(int itemType) { + return itemType == ITEM_TYPE_APPLICATION + || itemType == ITEM_TYPE_DEEP_SHORTCUT + || itemType == ITEM_TYPE_APP_PAIR; + } } diff --git a/src/com/android/launcher3/model/data/ItemInfo.java b/src/com/android/launcher3/model/data/ItemInfo.java index 588e75959e..ad7696cd15 100644 --- a/src/com/android/launcher3/model/data/ItemInfo.java +++ b/src/com/android/launcher3/model/data/ItemInfo.java @@ -61,7 +61,6 @@ import com.android.launcher3.logger.LauncherAtom.ShortcutsContainer; import com.android.launcher3.logger.LauncherAtom.TaskSwitcherContainer; import com.android.launcher3.logger.LauncherAtom.WallpapersContainer; import com.android.launcher3.logger.LauncherAtomExtensions.ExtendedContainers; -import com.android.launcher3.model.ModelWriter; import com.android.launcher3.pm.UserCache; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.ContentWriter; @@ -536,14 +535,6 @@ public class ItemInfo { } /** - * Sets the title of the item and writes to DB model if needed. - */ - public void setTitle(@Nullable final CharSequence title, - @Nullable final ModelWriter modelWriter) { - this.title = title; - } - - /** * Returns a string ID that is stable for a user session, but may not be persisted */ @Nullable diff --git a/src/com/android/launcher3/popup/PopupDataProvider.java b/src/com/android/launcher3/popup/PopupDataProvider.java index 5c1a7553a5..95110329d3 100644 --- a/src/com/android/launcher3/popup/PopupDataProvider.java +++ b/src/com/android/launcher3/popup/PopupDataProvider.java @@ -26,7 +26,6 @@ import androidx.annotation.Nullable; import com.android.launcher3.BubbleTextView; import com.android.launcher3.allapps.ActivityAllAppsContainerView; import com.android.launcher3.dot.DotInfo; -import com.android.launcher3.dot.FolderDotInfo; import com.android.launcher3.folder.Folder; import com.android.launcher3.folder.FolderIcon; import com.android.launcher3.model.data.FolderInfo; @@ -76,11 +75,7 @@ public class PopupDataProvider implements NotificationListener.NotificationsChan ((BubbleTextView) v).applyDotState(info, true /* animate */); } else if (v instanceof FolderIcon icon && info instanceof FolderInfo fi && fi.anyMatch(matcher)) { - FolderDotInfo folderDotInfo = new FolderDotInfo(); - for (ItemInfo si : fi.getContents()) { - folderDotInfo.addDotInfo(getDotInfoForItem(si)); - } - icon.setDotInfo(folderDotInfo); + icon.updateDotInfo(); } // process all the shortcuts |