From ca1f87d8f54534b1cf09afd0d410b690b6499baa Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 26 Mar 2025 13:51:18 -0700 Subject: [PATCH] Update Launcher UI when model changes are made outside of Launcher UI Bug: 305877212 Flag: EXEMPT bugfix Test: Verified manually by modifing folder in taskbar Change-Id: I513dcfbc9e4ffcd970766ce8352815571e576461 --- .../taskbar/TaskbarActivityContext.java | 6 ++++++ .../taskbar/TaskbarViewController.java | 7 +++++++ src/com/android/launcher3/Launcher.java | 9 +++++--- src/com/android/launcher3/ModelCallbacks.kt | 11 ++++++++-- src/com/android/launcher3/Workspace.java | 13 +++++++----- src/com/android/launcher3/folder/Folder.java | 18 ++++++++-------- .../android/launcher3/folder/FolderIcon.java | 2 +- .../launcher3/folder/LauncherDelegate.java | 21 ++----------------- .../launcher3/views/ActivityContext.java | 7 +++++++ 9 files changed, 55 insertions(+), 39 deletions(-) diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java index fc93d4a0ab..653bef9429 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java @@ -113,6 +113,7 @@ import com.android.launcher3.icons.BitmapRenderer; import com.android.launcher3.icons.FastBitmapDrawable; import com.android.launcher3.logger.LauncherAtom; import com.android.launcher3.logging.StatsLogManager; +import com.android.launcher3.model.ModelWriter; import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.model.data.AppPairInfo; import com.android.launcher3.model.data.FolderInfo; @@ -829,6 +830,11 @@ public class TaskbarActivityContext extends BaseTaskbarContext { return mControllers.taskbarDragController; } + @Override + public ModelWriter getModelWriter() { + return mControllers.taskbarViewController.getModelWriter(); + } + @Nullable public BubbleControllers getBubbleControllers() { return mControllers.bubbleControllers.orElse(null); diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java index ac9d2ba8bf..8aff417510 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java @@ -81,6 +81,7 @@ import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.anim.RevealOutlineAnimation; import com.android.launcher3.anim.RoundedRectRevealOutlineProvider; import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.model.ModelWriter; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.TaskItemInfo; import com.android.launcher3.taskbar.bubbles.BubbleBarController; @@ -412,6 +413,12 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar return mTaskbarIconAlpha; } + /** Creates a ModelWriter for updating model properties */ + public ModelWriter getModelWriter() { + return LauncherAppState.getInstance(mActivity).getModel() + .getWriter(false, mActivity.getCellPosMapper(), mModelCallbacks); + } + /** * Should be called when the recents button is disabled, so we can hide Taskbar icons as well. */ diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index d5b3ed5645..0024ee6430 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -2534,6 +2534,11 @@ public class Launcher extends StatefulActivity mModelCallbacks.bindWorkspaceComponentsRemoved(matcher); } + @Override + public void bindItemsModified(List items) { + mModelCallbacks.bindItemsModified(items); + } + /** * See {@code LauncherBindingDelegate} */ @@ -2983,9 +2988,7 @@ public class Launcher extends StatefulActivity return mModel; } - /** - * Returns the ModelWriter writer, make sure to call the function every time you want to use it. - */ + @Override public ModelWriter getModelWriter() { return mModelWriter; } diff --git a/src/com/android/launcher3/ModelCallbacks.kt b/src/com/android/launcher3/ModelCallbacks.kt index f38dc418a0..dbfc2c51ef 100644 --- a/src/com/android/launcher3/ModelCallbacks.kt +++ b/src/com/android/launcher3/ModelCallbacks.kt @@ -21,6 +21,7 @@ import com.android.launcher3.popup.PopupContainerWithArrow import com.android.launcher3.util.ComponentKey import com.android.launcher3.util.IntArray as LIntArray import com.android.launcher3.util.IntSet as LIntSet +import com.android.launcher3.util.ItemInfoMatcher import com.android.launcher3.util.PackageUserKey import com.android.launcher3.util.Preconditions import com.android.launcher3.util.RunnableList @@ -228,12 +229,18 @@ class ModelCallbacks(private var launcher: Launcher) : BgDataModel.Callbacks { * updated as well. In that scenario, we only remove specific components from the workspace and * hotseat, where as package-removal should clear all items by package name. */ - override fun bindWorkspaceComponentsRemoved(matcher: Predicate?) { - launcher.workspace.removeItemsByMatcher(matcher) + override fun bindWorkspaceComponentsRemoved(matcher: Predicate) { + launcher.workspace.removeItemsByMatcher(matcher, true) launcher.dragController.onAppsRemoved(matcher) PopupContainerWithArrow.dismissInvalidPopup(launcher) } + override fun bindItemsModified(items: MutableList) { + 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 59f84ab891..1c5bf277a0 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -3318,8 +3318,10 @@ public class Workspace extends PagedView * Removes items that match the {@param matcher}. When applications are removed * as a part of an update, this is called to ensure that other widgets and application * shortcuts are not removed. + * + * @param persistChanges if true, any dependent changes will be persisted to the DB */ - public void removeItemsByMatcher(final Predicate matcher) { + public void removeItemsByMatcher(final Predicate matcher, boolean persistChanges) { for (CellLayout layout : getWorkspaceAndHotseatCellLayouts()) { ShortcutAndWidgetContainer container = layout.getShortcutsAndWidgets(); // Iterate in reverse order as we are removing items @@ -3346,14 +3348,15 @@ public class Workspace extends PagedView } else if (info instanceof AppPairInfo api) { // If an app pair's member apps are being removed, delete the whole app pair. if (api.anyMatch(matcher)) { - mLauncher.removeItem(child, info, true); + mLauncher.removeItem(child, info, persistChanges); } } } } - // Strip all the empty screens - stripEmptyScreens(); + if (persistChanges) { + stripEmptyScreens(); + } } @Override @@ -3390,7 +3393,7 @@ public class Workspace extends PagedView public void persistRemoveItemsByMatcher(Predicate matcher, @Nullable final String reason) { mLauncher.getModelWriter().deleteItemsFromDatabase(matcher, reason); - removeItemsByMatcher(matcher); + removeItemsByMatcher(matcher, true); } public boolean isOverlayShown() { diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java index 996c5e7f55..be365b2c7f 100644 --- a/src/com/android/launcher3/folder/Folder.java +++ b/src/com/android/launcher3/folder/Folder.java @@ -413,7 +413,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo if (DEBUG) { Log.d(TAG, "onBackKey newTitle=" + newTitle); } - mInfo.setTitle(newTitle, mLauncherDelegate.getModelWriter()); + mInfo.setTitle(newTitle, mActivityContext.getModelWriter()); mFolderIcon.onTitleChanged(newTitle); if (TextUtils.isEmpty(mInfo.title)) { @@ -767,7 +767,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo if (updateAnimationFlag) { mInfo.setOption(FolderInfo.FLAG_MULTI_PAGE_ANIMATION, true, - mLauncherDelegate.getModelWriter()); + mActivityContext.getModelWriter()); } } }); @@ -1147,7 +1147,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo if (getItemCount() <= mContent.itemsPerPage()) { // Show the animation, next time something is added to the folder. mInfo.setOption(FolderInfo.FLAG_MULTI_PAGE_ANIMATION, false, - mLauncherDelegate.getModelWriter()); + mActivityContext.getModelWriter()); } } @@ -1165,7 +1165,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo } if (!items.isEmpty()) { - mLauncherDelegate.getModelWriter().moveItemsInDatabase(items, mInfo.id, 0); + mActivityContext.getModelWriter().moveItemsInDatabase(items, mInfo.id, 0); } if (!isBind && total > 1 /* no need to update if there's one icon */) { Executors.MODEL_EXECUTOR.post(() -> { @@ -1403,7 +1403,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo // Actually move the item in the database if it was an external drag. Call this // before creating the view, so that the ItemInfo is updated appropriately. - mLauncherDelegate.getModelWriter().addOrMoveItemInDatabase( + mActivityContext.getModelWriter().addOrMoveItemInDatabase( si, mInfo.id, 0, si.cellX, si.cellY); mIsExternalDrag = false; } else { @@ -1445,7 +1445,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo if (mContent.getPageCount() > 1) { // The animation has already been shown while opening the folder. mInfo.setOption(FolderInfo.FLAG_MULTI_PAGE_ANIMATION, true, - mLauncherDelegate.getModelWriter()); + mActivityContext.getModelWriter()); } if (!launcher.isInState(EDIT_MODE)) { @@ -1494,7 +1494,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo FolderGridOrganizer verifier = createFolderGridOrganizer( mActivityContext.getDeviceProfile()).setFolderInfo(mInfo); verifier.updateRankAndPos(item, rank); - mLauncherDelegate.getModelWriter().addOrMoveItemInDatabase(item, mInfo.id, 0, + mActivityContext.getModelWriter().addOrMoveItemInDatabase(item, mInfo.id, 0, item.cellX, item.cellY); updateItemLocationsInDatabaseBatch(false); @@ -1506,7 +1506,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo updateTextViewFocus(); } - mLauncherDelegate.getModelWriter().notifyItemModified(mInfo); + mActivityContext.getModelWriter().notifyItemModified(mInfo); mFolderIcon.onItemsChanged(animate); } @@ -1514,7 +1514,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo public void removeFolderContent(boolean animate, ItemInfo... items) { List itemArray = Arrays.asList(items); if (mInfo.getContents().removeAll(itemArray)) { - mLauncherDelegate.getModelWriter().notifyItemModified(mInfo); + mActivityContext.getModelWriter().notifyItemModified(mInfo); } if (!mSuppressContentUpdate) { diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java index 1cd9bcc965..b694fcd934 100644 --- a/src/com/android/launcher3/folder/FolderIcon.java +++ b/src/com/android/launcher3/folder/FolderIcon.java @@ -442,7 +442,7 @@ public class FolderIcon extends FrameLayout implements FloatingIconViewCompanion CharSequence newTitle = nameInfos.getLabels()[0]; FromState fromState = mInfo.getFromLabelState(); - mInfo.setTitle(newTitle, mFolder.mLauncherDelegate.getModelWriter()); + mInfo.setTitle(newTitle, mActivity.getModelWriter()); onTitleChanged(mInfo.title); mFolder.getFolderName().setText(mInfo.title); diff --git a/src/com/android/launcher3/folder/LauncherDelegate.java b/src/com/android/launcher3/folder/LauncherDelegate.java index 07215c4710..54ea16a07f 100644 --- a/src/com/android/launcher3/folder/LauncherDelegate.java +++ b/src/com/android/launcher3/folder/LauncherDelegate.java @@ -17,7 +17,6 @@ package com.android.launcher3.folder; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_CONVERTED_TO_ICON; -import android.content.Context; import android.view.MotionEvent; import android.view.View; @@ -27,11 +26,9 @@ import com.android.launcher3.CellLayout; import com.android.launcher3.DragSource; import com.android.launcher3.DropTarget; import com.android.launcher3.Launcher; -import com.android.launcher3.LauncherAppState; import com.android.launcher3.dragndrop.DragOptions; import com.android.launcher3.logging.InstanceId; import com.android.launcher3.logging.StatsLogManager.StatsLogger; -import com.android.launcher3.model.ModelWriter; import com.android.launcher3.model.data.FolderInfo; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.views.ActivityContext; @@ -64,10 +61,6 @@ public class LauncherDelegate { mLauncher.getWorkspace().beginDragShared(child, source, options); } - ModelWriter getModelWriter() { - return mLauncher.getModelWriter(); - } - void forEachVisibleWorkspacePage(Consumer callback) { mLauncher.getWorkspace().forEachVisiblePage(callback); } @@ -154,7 +147,6 @@ public class LauncherDelegate { private static class FallbackDelegate extends LauncherDelegate { private final ActivityContext mContext; - private ModelWriter mWriter; FallbackDelegate(ActivityContext context) { super(null); @@ -174,15 +166,6 @@ public class LauncherDelegate { @Override void beginDragShared(View child, DragSource source, DragOptions options) { } - @Override - ModelWriter getModelWriter() { - if (mWriter == null) { - mWriter = LauncherAppState.getInstance((Context) mContext).getModel().getWriter( - false, mContext.getCellPosMapper(), null); - } - return mWriter; - } - @Override void forEachVisibleWorkspacePage(Consumer callback) { } @@ -204,8 +187,8 @@ public class LauncherDelegate { } static LauncherDelegate from(ActivityContext context) { - return context instanceof Launcher - ? new LauncherDelegate((Launcher) context) + return context instanceof Launcher l + ? new LauncherDelegate(l) : new FallbackDelegate(context); } } diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java index cbf534187d..facde2c846 100644 --- a/src/com/android/launcher3/views/ActivityContext.java +++ b/src/com/android/launcher3/views/ActivityContext.java @@ -75,6 +75,7 @@ import com.android.launcher3.logger.LauncherAtom; import com.android.launcher3.logging.InstanceId; import com.android.launcher3.logging.InstanceIdSequence; import com.android.launcher3.logging.StatsLogManager; +import com.android.launcher3.model.ModelWriter; import com.android.launcher3.model.StringCache; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; @@ -521,6 +522,12 @@ public interface ActivityContext extends SavedStateRegistryOwner { return new CellPosMapper(dp.isVerticalBarLayout(), dp.numShownHotseatIcons); } + /** Returns a writer for updating model properties */ + default ModelWriter getModelWriter() { + return LauncherAppState.getInstance(asContext()).getModel().getWriter( + false, getCellPosMapper(), null); + } + /** Set to manage objects that can be cleaned up along with the context */ WeakCleanupSet getOwnerCleanupSet();