diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java index 15c7a8d59d..836c06cb38 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java @@ -15,17 +15,23 @@ */ package com.android.launcher3.taskbar; +import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT; +import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION; + import android.util.SparseArray; import android.view.View; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.annotation.UiThread; import com.android.launcher3.LauncherSettings.Favorites; +import com.android.launcher3.celllayout.CellInfo; import com.android.launcher3.model.BgDataModel; import com.android.launcher3.model.BgDataModel.FixedContainerItems; import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.model.data.ItemInfo; +import com.android.launcher3.taskbar.TaskbarView.TaskbarLayoutParams; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.IntArray; import com.android.launcher3.util.IntSet; @@ -104,7 +110,7 @@ public class TaskbarModelCallbacks implements } } - private boolean handleItemsAdded(List items) { + private boolean handleItemsAdded(Iterable items) { boolean modified = false; for (ItemInfo item : items) { if (item.container == Favorites.CONTAINER_HOTSEAT) { @@ -117,7 +123,24 @@ public class TaskbarModelCallbacks implements @Override public void bindItemsUpdated(Set updates) { - updateContainerItems(updates, mContext); + Set itemsToRebind = updateContainerItems(updates, mContext); + + boolean removed = handleItemsRemoved(ItemInfoMatcher.ofItems(itemsToRebind)); + boolean added = handleItemsAdded(itemsToRebind); + if (removed || added) { + commitItemsToUI(); + } + } + + @Nullable + @Override + public CellInfo getCellInfoForView(@NonNull View view) { + return view.getLayoutParams() instanceof TaskbarLayoutParams tlp ? tlp.bindInfo : null; + } + + @Override + public boolean isContainerSupported(int container) { + return container == CONTAINER_HOTSEAT || container == CONTAINER_HOTSEAT_PREDICTION; } @Override @@ -150,15 +173,6 @@ public class TaskbarModelCallbacks implements return modified; } - @Override - public void bindItemsModified(List items) { - boolean removed = handleItemsRemoved(ItemInfoMatcher.ofItems(items)); - boolean added = handleItemsAdded(items); - if (removed || added) { - commitItemsToUI(); - } - } - @Override public void bindExtraContainerItems(FixedContainerItems item) { if (item.containerId == Favorites.CONTAINER_HOTSEAT_PREDICTION) { diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java index b4eb16d0b6..60e4cd8606 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java @@ -38,6 +38,7 @@ import android.view.InputDevice; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; +import android.view.ViewGroup; import android.widget.FrameLayout; import androidx.annotation.LayoutRes; @@ -51,6 +52,7 @@ import com.android.launcher3.Insettable; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.apppairs.AppPairIcon; +import com.android.launcher3.celllayout.CellInfo; import com.android.launcher3.folder.FolderIcon; import com.android.launcher3.folder.PreviewBackground; import com.android.launcher3.model.data.AppPairInfo; @@ -570,7 +572,7 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar } else { hotseatView = inflate(expectedLayoutResId); } - LayoutParams lp = new LayoutParams(mIconTouchSize, mIconTouchSize); + LayoutParams lp = new TaskbarLayoutParams(mIconTouchSize, mIconTouchSize); hotseatView.setPadding(mItemPadding, mItemPadding, mItemPadding, mItemPadding); addView(hotseatView, mNextViewIndex, lp); } else if (hotseatView instanceof FolderIcon fi) { @@ -578,6 +580,13 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar fi.getFolder().reapplyItemInfo(); } + if (hotseatView.getLayoutParams() instanceof TaskbarLayoutParams tlp) { + tlp.bindInfo = new CellInfo(hotseatView, + hotseatItemInfo.screenId, hotseatItemInfo.container, + hotseatItemInfo.cellX, hotseatItemInfo.cellY, + hotseatItemInfo.spanX, hotseatItemInfo.spanY); + } + // Apply the Hotseat ItemInfos, or hide the view if there is none for a given index. if (hotseatView instanceof BubbleTextView btv && hotseatItemInfo instanceof WorkspaceItemInfo workspaceInfo) { @@ -696,7 +705,7 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar if (recentIcon == null) { // TODO(b/343289567 and b/316004172): support app pairs and desktop mode. recentIcon = inflate(expectedLayoutResId); - LayoutParams lp = new LayoutParams(mIconTouchSize, mIconTouchSize); + LayoutParams lp = new TaskbarLayoutParams(mIconTouchSize, mIconTouchSize); recentIcon.setPadding(mItemPadding, mItemPadding, mItemPadding, mItemPadding); addView(recentIcon, mNextViewIndex, lp); } @@ -1130,4 +1139,36 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar return navSpaceNeeded + getIconLayoutWidth(); } } + + @Override + protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) { + return new TaskbarLayoutParams(lp); + } + + @Override + public LayoutParams generateLayoutParams(AttributeSet attrs) { + return new TaskbarLayoutParams(getContext(), attrs); + } + + @Override + protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { + return p instanceof TaskbarLayoutParams; + } + + public static class TaskbarLayoutParams extends FrameLayout.LayoutParams { + + @Nullable public CellInfo bindInfo; + + public TaskbarLayoutParams(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public TaskbarLayoutParams(ViewGroup.LayoutParams source) { + super(source); + } + + public TaskbarLayoutParams(int width, int height) { + super(width, height); + } + } } diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index 0ce966b99b..74572fc38a 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -1076,10 +1076,8 @@ public class CellLayout extends ViewGroup { int delay, boolean permanent, boolean adjustOccupied) { ShortcutAndWidgetContainer clc = getShortcutsAndWidgets(); - if (clc.indexOfChild(child) != -1 && (child instanceof Reorderable)) { + if (clc.indexOfChild(child) != -1 && (child instanceof Reorderable item)) { final CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams(); - final ItemInfo info = (ItemInfo) child.getTag(); - final Reorderable item = (Reorderable) child; // We cancel any existing animations if (mReorderAnimators.containsKey(lp)) { @@ -1485,7 +1483,7 @@ public class CellLayout extends ViewGroup { lp.setCellX(lp.getTmpCellX()); lp.setCellY(lp.getTmpCellY()); if (requiresDbUpdate) { - Launcher.cast(mActivity).getModelWriter().modifyItemInDatabase(info, container, + mActivity.getModelWriter().modifyItemInDatabase(info, container, screenId, lp.getCellX(), lp.getCellY(), lp.cellHSpan, lp.cellVSpan); } } @@ -1855,8 +1853,7 @@ public class CellLayout extends ViewGroup { public void markCellsAsOccupiedForView(View view) { if (view instanceof LauncherAppWidgetHostView - && view.getTag() instanceof LauncherAppWidgetInfo) { - LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) view.getTag(); + && view.getTag() instanceof LauncherAppWidgetInfo info) { CellPos pos = mActivity.getCellPosMapper().mapModelToPresenter(info); mOccupied.markCells(pos.cellX, pos.cellY, info.spanX, info.spanY, true); return; @@ -1869,8 +1866,7 @@ public class CellLayout extends ViewGroup { public void markCellsAsUnoccupiedForView(View view) { if (view instanceof LauncherAppWidgetHostView - && view.getTag() instanceof LauncherAppWidgetInfo) { - LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) view.getTag(); + && view.getTag() instanceof LauncherAppWidgetInfo info) { CellPos pos = mActivity.getCellPosMapper().mapModelToPresenter(info); mOccupied.markCells(pos.cellX, pos.cellY, info.spanX, info.spanY, false); return; diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index fccec91e7e..2d77762563 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -2526,11 +2526,6 @@ public class Launcher extends StatefulActivity mModelCallbacks.bindWorkspaceComponentsRemoved(matcher); } - @Override - public void bindItemsModified(List items) { - mModelCallbacks.bindItemsModified(items); - } - /** * See {@code LauncherBindingDelegate} */ diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java index bd730feab1..b17f44f5ba 100644 --- a/src/com/android/launcher3/LauncherSettings.java +++ b/src/com/android/launcher3/LauncherSettings.java @@ -197,17 +197,6 @@ public class LauncherSettings { public static final int CONTAINER_UNKNOWN = -1; - - /** - * Function to know if a container belongs to the Workspace. - */ - public static boolean containerBelongsToWorkspace(int container) { - return container == CONTAINER_DESKTOP - || container == CONTAINER_HOTSEAT - || container == CONTAINER_PREDICTION - || container == CONTAINER_HOTSEAT_PREDICTION; - } - public static final String containerToString(int container) { switch (container) { case CONTAINER_DESKTOP: return "desktop"; diff --git a/src/com/android/launcher3/ModelCallbacks.kt b/src/com/android/launcher3/ModelCallbacks.kt index 0ea27db319..cc5ac499a8 100644 --- a/src/com/android/launcher3/ModelCallbacks.kt +++ b/src/com/android/launcher3/ModelCallbacks.kt @@ -6,7 +6,6 @@ import android.os.Trace import android.util.Log import androidx.annotation.UiThread import com.android.launcher3.LauncherConstants.TraceEvents -import com.android.launcher3.LauncherSettings.Favorites.containerBelongsToWorkspace import com.android.launcher3.Utilities.SHOULD_SHOW_FIRST_PAGE_WIDGET import com.android.launcher3.WorkspaceLayoutManager.FIRST_SCREEN_ID import com.android.launcher3.allapps.AllAppsStore @@ -204,8 +203,20 @@ class ModelCallbacks(private var launcher: Launcher) : BgDataModel.Callbacks { * method from LauncherModel.Callbacks. */ override fun bindItemsUpdated(updates: Set) { - launcher.workspace.updateContainerItems(updates, launcher) + val workspace = launcher.workspace + val itemsToRebind = workspace.updateContainerItems(updates, launcher) PopupContainerWithArrow.dismissInvalidPopup(launcher) + + if (itemsToRebind.isEmpty()) return + workspace.removeItemsByMatcher(ItemInfoMatcher.ofItems(itemsToRebind), false) + itemsToRebind + .filter { workspace.isContainerSupported(it.container) } + .let { + if (it.isNotEmpty()) { + launcher.bindItems(it, false) + } + } + workspace.stripEmptyScreens() } /** @@ -220,13 +231,6 @@ class ModelCallbacks(private var launcher: Launcher) : BgDataModel.Callbacks { PopupContainerWithArrow.dismissInvalidPopup(launcher) } - override fun bindItemsModified(itemsParam: MutableList) { - val items = itemsParam.filter { containerBelongsToWorkspace(it.container) } - launcher.workspace.removeItemsByMatcher(ItemInfoMatcher.ofItems(items), false) - launcher.bindItems(items, false) - launcher.workspace.stripEmptyScreens() - } - override fun bindAllWidgets(allWidgets: List) { launcher.widgetPickerDataProvider.setWidgets(allWidgets) } diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index ba3c6b102d..db5dc8054e 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -19,7 +19,10 @@ package com.android.launcher3; import static com.android.launcher3.AbstractFloatingView.TYPE_WIDGET_RESIZE_FRAME; import static com.android.launcher3.BubbleTextView.DISPLAY_FOLDER; import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY; +import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP; +import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT; import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION; +import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_PREDICTION; import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.EDIT_MODE; import static com.android.launcher3.LauncherState.FLAG_MULTI_PAGE; @@ -62,6 +65,7 @@ import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; +import android.view.ViewParent; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.FrameLayout; import android.widget.Toast; @@ -2043,9 +2047,7 @@ public class Workspace extends PagedView // Move internally boolean hasMovedLayouts = (getParentCellLayoutForView(cell) != dropTargetLayout); boolean hasMovedIntoHotseat = mLauncher.isHotseatLayout(dropTargetLayout); - int container = hasMovedIntoHotseat ? - LauncherSettings.Favorites.CONTAINER_HOTSEAT : - LauncherSettings.Favorites.CONTAINER_DESKTOP; + int container = hasMovedIntoHotseat ? CONTAINER_HOTSEAT : CONTAINER_DESKTOP; int screenId = (mTargetCell[0] < 0) ? mDragInfo.screenId : getCellLayoutId(dropTargetLayout); int spanX = mDragInfo != null ? mDragInfo.spanX : 1; @@ -2148,8 +2150,8 @@ public class Workspace extends PagedView lp.cellVSpan = item.spanY; lp.isLockedToGrid = true; - if (container != LauncherSettings.Favorites.CONTAINER_HOTSEAT && - cell instanceof LauncherAppWidgetHostView) { + if (container != CONTAINER_HOTSEAT + && cell instanceof LauncherAppWidgetHostView) { // We post this call so that the widget has a chance to be placed // in its final location @@ -2805,8 +2807,8 @@ public class Workspace extends PagedView */ private void onDropExternal(final int[] touchXY, final CellLayout cellLayout, DragObject d) { final int container = mLauncher.isHotseatLayout(cellLayout) - ? LauncherSettings.Favorites.CONTAINER_HOTSEAT - : LauncherSettings.Favorites.CONTAINER_DESKTOP; + ? CONTAINER_HOTSEAT + : CONTAINER_DESKTOP; if (d.dragInfo instanceof PendingAddShortcutInfo) { WorkspaceItemInfo si = ((PendingAddShortcutInfo) d.dragInfo) .getActivityInfo(mLauncher).createWorkspaceItemInfo(); @@ -3347,6 +3349,30 @@ public class Workspace extends PagedView } } + @Nullable + @Override + public CellInfo getCellInfoForView(@NonNull View view) { + ViewParent parent = view.getParent(); + if (parent != null + && parent.getParent() instanceof CellLayout cl + && view.getLayoutParams() instanceof CellLayoutLayoutParams lp) { + int container = mLauncher.isHotseatLayout(cl) ? CONTAINER_HOTSEAT : CONTAINER_DESKTOP; + CellPos pos = getCellPosMapper().mapPresenterToModel( + lp.getCellX(), lp.getCellY(), getCellLayoutId(cl), container); + return new CellInfo(view, + pos.screenId, container, pos.cellX, pos.cellY, lp.cellHSpan, lp.cellVSpan); + } + return null; + } + + @Override + public boolean isContainerSupported(int container) { + return container == CONTAINER_DESKTOP + || container == CONTAINER_HOTSEAT + || container == CONTAINER_PREDICTION + || container == CONTAINER_HOTSEAT_PREDICTION; + } + @Override public View mapOverItems(@NonNull ItemOperator op) { return mapOverCellLayouts(getWorkspaceAndHotseatCellLayouts(), op); diff --git a/src/com/android/launcher3/celllayout/CellInfo.kt b/src/com/android/launcher3/celllayout/CellInfo.kt index 5a3b7f7685..817857985e 100644 --- a/src/com/android/launcher3/celllayout/CellInfo.kt +++ b/src/com/android/launcher3/celllayout/CellInfo.kt @@ -26,17 +26,38 @@ import com.android.launcher3.util.CellAndSpan // 2. When long clicking on an empty cell in a CellLayout, we save information about the // cellX and cellY coordinates and which page was clicked. We then set this as a tag on // the CellLayout that was long clicked -class CellInfo(v: View?, info: ItemInfo, cellPos: CellPos) : - CellAndSpan(cellPos.cellX, cellPos.cellY, info.spanX, info.spanY) { - @JvmField val cell: View? - @JvmField val screenId: Int - @JvmField val container: Int +class CellInfo( + @JvmField val cell: View?, + @JvmField val screenId: Int, + @JvmField val container: Int, + cellX: Int, + cellY: Int, + spanX: Int, + spanY: Int, +) : CellAndSpan(cellX, cellY, spanX, spanY) { - init { - cell = v - screenId = cellPos.screenId - container = info.container - } + constructor( + cell: View?, + info: ItemInfo, + cellPos: CellPos, + ) : this( + cell, + cellPos.screenId, + info.container, + cellPos.cellX, + cellPos.cellY, + info.spanX, + info.spanY, + ) + + /** Returns if the provided [item] represents the same position info as itself */ + fun isSameAs(item: ItemInfo) = + item.container == container && + item.screenId == screenId && + item.cellX == cellX && + item.cellY == cellY && + item.spanX == spanX && + item.spanY == spanY override fun toString(): String { return "CellInfo(cell=$cell, screenId=$screenId, container=$container)" diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java index 7998b6fd5d..3a16be3a41 100644 --- a/src/com/android/launcher3/folder/Folder.java +++ b/src/com/android/launcher3/folder/Folder.java @@ -1594,6 +1594,11 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo return mContent.iterateOverItems((info, view) -> info == item); } + @Override + public boolean isContainerSupported(int container) { + return container == mInfo.id; + } + /** * Utility methods to iterate over items of the view */ diff --git a/src/com/android/launcher3/folder/FolderGridOrganizer.java b/src/com/android/launcher3/folder/FolderGridOrganizer.java index 06286d62f7..ce76d505ac 100644 --- a/src/com/android/launcher3/folder/FolderGridOrganizer.java +++ b/src/com/android/launcher3/folder/FolderGridOrganizer.java @@ -141,11 +141,8 @@ public class FolderGridOrganizer { * @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) { + if (rank != item.rank) { item.rank = rank; - item.cellX = pos.x; - item.cellY = pos.y; return true; } return false; @@ -156,8 +153,13 @@ public class FolderGridOrganizer { */ public Point getPosForRank(int rank) { int pagePos = rank % mMaxItemsPerPage; - mPoint.x = pagePos % mCountX; - mPoint.y = pagePos / mCountX; + if (mCountX == 0) { + mPoint.x = 0; + mPoint.y = 0; + } else { + mPoint.x = pagePos % mCountX; + mPoint.y = pagePos / mCountX; + } return mPoint; } diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java index ceac72d5c4..6510f681c8 100644 --- a/src/com/android/launcher3/folder/FolderPagedView.java +++ b/src/com/android/launcher3/folder/FolderPagedView.java @@ -25,6 +25,7 @@ import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Canvas; import android.graphics.Path; +import android.graphics.Point; import android.util.ArrayMap; import android.util.AttributeSet; import android.util.Log; @@ -262,12 +263,11 @@ public class FolderPagedView extends PagedView implements Cli icon.setOnFocusChangeListener(mFocusIndicatorHelper); CellLayoutLayoutParams lp = (CellLayoutLayoutParams) icon.getLayoutParams(); + Point pos = mOrganizer.getPosForRank(item.rank); if (lp == null) { - icon.setLayoutParams(new CellLayoutLayoutParams( - item.cellX, item.cellY, item.spanX, item.spanY)); + icon.setLayoutParams(new CellLayoutLayoutParams(pos.x, pos.y, 1, 1)); } else { - lp.setCellX(item.cellX); - lp.setCellY(item.cellY); + lp.setCellXY(pos); lp.cellHSpan = lp.cellVSpan = 1; } diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java index 4ca832d5b5..9189eb8e3a 100644 --- a/src/com/android/launcher3/model/BgDataModel.java +++ b/src/com/android/launcher3/model/BgDataModel.java @@ -46,6 +46,8 @@ import com.android.launcher3.Flags; import com.android.launcher3.Workspace; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.dagger.LauncherAppSingleton; +import com.android.launcher3.logging.DumpManager; +import com.android.launcher3.logging.DumpManager.LauncherDumpable; import com.android.launcher3.logging.FileLog; import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.model.data.CollectionInfo; @@ -61,8 +63,6 @@ import com.android.launcher3.shortcuts.ShortcutRequest; import com.android.launcher3.shortcuts.ShortcutRequest.QueryResult; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.DaggerSingletonTracker; -import com.android.launcher3.logging.DumpManager; -import com.android.launcher3.logging.DumpManager.LauncherDumpable; import com.android.launcher3.util.IntArray; import com.android.launcher3.util.IntSet; import com.android.launcher3.util.IntSparseArrayMap; @@ -465,11 +465,6 @@ public class BgDataModel implements LauncherDumpable { default void bindAppsAdded(IntArray newScreens, ArrayList addNotAnimated, ArrayList addAnimated) { } - /** - * Called when some persistent property of an item is modified - */ - default void bindItemsModified(List items) { } - /** * Binds updated incremental download progress */ diff --git a/src/com/android/launcher3/model/ModelWriter.java b/src/com/android/launcher3/model/ModelWriter.java index db3ad8a91a..01e1730f47 100644 --- a/src/com/android/launcher3/model/ModelWriter.java +++ b/src/com/android/launcher3/model/ModelWriter.java @@ -51,6 +51,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.function.Predicate; import java.util.function.Supplier; @@ -177,7 +178,7 @@ public class ModelWriter { public void moveItemsInDatabase(final ArrayList items, int container, int screen) { ArrayList contentValues = new ArrayList<>(); int count = items.size(); - notifyOtherCallbacks(c -> c.bindItemsModified(items)); + notifyOtherCallbacks(c -> c.bindItemsUpdated(new HashSet<>(items))); for (int i = 0; i < count; i++) { ItemInfo item = items.get(i); @@ -229,7 +230,7 @@ public class ModelWriter { } public void notifyItemModified(ItemInfo item) { - notifyOtherCallbacks(c -> c.bindItemsModified(Collections.singletonList(item))); + notifyOtherCallbacks(c -> c.bindItemsUpdated(Collections.singleton(item))); } /** diff --git a/src/com/android/launcher3/util/LauncherBindableItemsContainer.kt b/src/com/android/launcher3/util/LauncherBindableItemsContainer.kt index 8fdedef77e..ea1eecfb1d 100644 --- a/src/com/android/launcher3/util/LauncherBindableItemsContainer.kt +++ b/src/com/android/launcher3/util/LauncherBindableItemsContainer.kt @@ -19,6 +19,7 @@ package com.android.launcher3.util import android.view.View import com.android.launcher3.BubbleTextView import com.android.launcher3.apppairs.AppPairIcon +import com.android.launcher3.celllayout.CellInfo import com.android.launcher3.folder.Folder import com.android.launcher3.folder.FolderIcon import com.android.launcher3.model.data.AppPairInfo @@ -31,18 +32,28 @@ import com.android.launcher3.widget.PendingAppWidgetHostView import java.util.function.Predicate /** Interface representing a container which can bind Launcher items with some utility methods */ -interface LauncherBindableItemsContainer { +fun interface LauncherBindableItemsContainer { /** - * Called to update workspace items as a result of {@link - * com.android.launcher3.model.BgDataModel.Callbacks#bindItemsUpdated(Set)} + * Called to update items in a specific container as a result of + * [com.android.launcher3.model.BgDataModel.Callbacks.bindItemsUpdated]. Returns a set of items + * which were potentially moved and need to be rebound to the container. */ - fun updateContainerItems(updates: Set, context: ActivityContext) { + fun updateContainerItems(updates: Set, context: ActivityContext): Set { val op = ItemOperator { info, v -> when { v is BubbleTextView && info is WorkspaceItemInfo && updates.contains(info) -> v.applyFromWorkspaceItem(info) - v is FolderIcon && info is FolderInfo -> v.updatePreviewItems(updates::contains) + v is FolderIcon && info is FolderInfo -> { + v.updatePreviewItems(updates::contains) + if (updates.contains(info)) { + v.onItemsChanged(false) + v.folder.apply { + reapplyItemInfo() + if (isOpen()) close(false) + } + } + } v is AppPairIcon && info is AppPairInfo -> v.maybeRedrawForWorkspaceUpdate(updates::contains) v is PendingAppWidgetHostView && updates.contains(info) -> { @@ -57,6 +68,20 @@ interface LauncherBindableItemsContainer { mapOverItems(op) Folder.getOpen(context)?.mapOverItems(op) + + // Check for moved items + val itemsToRebind = updates.filterTo(mutableSetOf()) { isContainerSupported(it.container) } + mapOverItems { info, v -> + info?.apply { + if (!updates.contains(this)) return@apply + val uiInfo = getCellInfoForView(v) ?: return@apply + if (uiInfo.isSameAs(this)) itemsToRebind.remove(this) else itemsToRebind.add(this) + } + + // Iterate all items + false + } + return itemsToRebind } /** Returns the first view, matching the [op] */ @@ -69,6 +94,16 @@ interface LauncherBindableItemsContainer { fun getViewByItemId(id: Int): View? = mapOverItems { info, _ -> info != null && info.id == id } + /** + * Returns the currently bound info for the [view] or null if the view is not bound. It can be + * used to determine if the UI needs to be updated if it is different that the info represented + * underlying item + */ + fun getCellInfoForView(view: View): CellInfo? = null + + /** Returns if the provided [container] is supported by to this container */ + fun isContainerSupported(container: Int) = false + /** * Map the [op] over the shortcuts and widgets. Once we found the first view which matches, we * will stop the iteration and return that view.