diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java index 477964a6f4..371bb80484 100644 --- a/src/com/android/launcher3/DeleteDropTarget.java +++ b/src/com/android/launcher3/DeleteDropTarget.java @@ -160,7 +160,7 @@ public class DeleteDropTarget extends ButtonDropTarget { // Remove the item from launcher and the db, we can ignore the containerInfo in this call // because we already remove the drag view from the folder (if the drag originated from // a folder) in Folder.beginDrag() - mLauncher.removeItem(view, item, true /* deleteFromDb */); + mLauncher.removeItem(view, item, true /* deleteFromDb */, "removed by accessibility drop"); mLauncher.getWorkspace().stripEmptyScreens(); mLauncher.getDragLayer() .announceForAccessibility(getContext().getString(R.string.item_removed)); diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 135b88d5b7..11bc11f778 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -1956,6 +1956,19 @@ public class Launcher extends StatefulActivity * @param deleteFromDb whether or not to delete this item from the db. */ public boolean removeItem(View v, final ItemInfo itemInfo, boolean deleteFromDb) { + return removeItem(v, itemInfo, deleteFromDb, null); + } + + /** + * Unbinds the view for the specified item, and removes the item and all its children. + * + * @param v the view being removed. + * @param itemInfo the {@link ItemInfo} for this view. + * @param deleteFromDb whether or not to delete this item from the db. + * @param reason the resaon for removal. + */ + public boolean removeItem(View v, final ItemInfo itemInfo, boolean deleteFromDb, + @Nullable final String reason) { if (itemInfo instanceof WorkspaceItemInfo) { // Remove the shortcut from the folder before removing it from launcher View folderIcon = mWorkspace.getHomescreenIconByItemId(itemInfo.container); @@ -1965,7 +1978,7 @@ public class Launcher extends StatefulActivity mWorkspace.removeWorkspaceItem(v); } if (deleteFromDb) { - getModelWriter().deleteItemFromDatabase(itemInfo); + getModelWriter().deleteItemFromDatabase(itemInfo, reason); } } else if (itemInfo instanceof FolderInfo) { final FolderInfo folderInfo = (FolderInfo) itemInfo; @@ -1980,7 +1993,7 @@ public class Launcher extends StatefulActivity final LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo) itemInfo; mWorkspace.removeWorkspaceItem(v); if (deleteFromDb) { - getModelWriter().deleteWidgetInfo(widgetInfo, getAppWidgetHost()); + getModelWriter().deleteWidgetInfo(widgetInfo, getAppWidgetHost(), reason); } } else { return false; @@ -2399,8 +2412,7 @@ public class Launcher extends StatefulActivity if (FeatureFlags.IS_STUDIO_BUILD) { throw (new RuntimeException(desc)); } else { - Log.d(TAG, desc); - getModelWriter().deleteItemFromDatabase(item); + getModelWriter().deleteItemFromDatabase(item, desc); if (TestProtocol.sDebugTracing) { Log.d(TestProtocol.MISSING_PROMISE_ICON, TAG + "bindItems failed for item=" + item); @@ -2480,7 +2492,8 @@ public class Launcher extends StatefulActivity if (item.hasOptionFlag(LauncherAppWidgetInfo.OPTION_SEARCH_WIDGET)) { item.providerName = QsbContainerView.getSearchComponentName(this); if (item.providerName == null) { - getModelWriter().deleteItemFromDatabase(item); + getModelWriter().deleteItemFromDatabase(item, + "search widget removed because search component cannot be found"); return null; } } @@ -2531,10 +2544,10 @@ public class Launcher extends StatefulActivity if (!item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) && (item.restoreStatus != LauncherAppWidgetInfo.RESTORE_COMPLETED)) { if (appWidgetInfo == null) { - FileLog.d(TAG, "Removing restored widget: id=" + item.appWidgetId + getModelWriter().deleteItemFromDatabase(item, + "Removing restored widget: id=" + item.appWidgetId + " belongs to component " + item.providerName + " user " + item.user + ", as the provider is null and " + removalReason); - getModelWriter().deleteItemFromDatabase(item); return null; } @@ -2604,7 +2617,7 @@ public class Launcher extends StatefulActivity // Verify that we own the widget if (appWidgetInfo == null) { FileLog.e(TAG, "Removing invalid widget: id=" + item.appWidgetId); - getModelWriter().deleteWidgetInfo(item, getAppWidgetHost()); + getModelWriter().deleteWidgetInfo(item, getAppWidgetHost(), removalReason); return null; } diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 5c5c101355..de0d3002e7 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -477,7 +477,9 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi } if (!removedIds.isEmpty()) { - deleteAndBindComponentsRemoved(ItemInfoMatcher.ofItemIds(removedIds)); + deleteAndBindComponentsRemoved( + ItemInfoMatcher.ofItemIds(removedIds), + "removed because install session failed"); } } }); diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 6c091f08c1..93f3d9f061 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -3068,7 +3068,8 @@ public class Workspace extends PagedView if (info instanceof LauncherAppWidgetInfo) { LauncherAppWidgetInfo appWidgetInfo = (LauncherAppWidgetInfo) info; if (appWidgetInfo.appWidgetId == appWidgetId) { - mLauncher.removeItem(view, appWidgetInfo, true); + mLauncher.removeItem(view, appWidgetInfo, true, + "widget is removed in response to widget remove broadcast"); return true; } } @@ -3318,8 +3319,9 @@ public class Workspace extends PagedView * * @param matcher the matcher generated by the caller. */ - public void persistRemoveItemsByMatcher(Predicate matcher) { - mLauncher.getModelWriter().deleteItemsFromDatabase(matcher); + public void persistRemoveItemsByMatcher(Predicate matcher, + @Nullable final String reason) { + mLauncher.getModelWriter().deleteItemsFromDatabase(matcher, reason); removeItemsByMatcher(matcher); } diff --git a/src/com/android/launcher3/folder/LauncherDelegate.java b/src/com/android/launcher3/folder/LauncherDelegate.java index c5b3913a2b..1f0a011b39 100644 --- a/src/com/android/launcher3/folder/LauncherDelegate.java +++ b/src/com/android/launcher3/folder/LauncherDelegate.java @@ -100,7 +100,8 @@ public class LauncherDelegate { } // Remove the folder - mLauncher.removeItem(folder.mFolderIcon, info, true /* deleteFromDb */); + mLauncher.removeItem(folder.mFolderIcon, info, true /* deleteFromDb */, + "folder removed because there's only 1 item in it"); if (folder.mFolderIcon instanceof DropTarget) { folder.mDragController.removeDropTarget((DropTarget) folder.mFolderIcon); } diff --git a/src/com/android/launcher3/model/BaseModelUpdateTask.java b/src/com/android/launcher3/model/BaseModelUpdateTask.java index 832c1dd166..2a6a6919e0 100644 --- a/src/com/android/launcher3/model/BaseModelUpdateTask.java +++ b/src/com/android/launcher3/model/BaseModelUpdateTask.java @@ -17,6 +17,8 @@ package com.android.launcher3.model; import android.util.Log; +import androidx.annotation.Nullable; + import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherModel; import com.android.launcher3.LauncherModel.CallbackTask; @@ -128,8 +130,9 @@ public abstract class BaseModelUpdateTask implements ModelUpdateTask { scheduleCallbackTask(c -> c.bindAllWidgets(widgets)); } - public void deleteAndBindComponentsRemoved(final Predicate matcher) { - getModelWriter().deleteItemsFromDatabase(matcher); + public void deleteAndBindComponentsRemoved(final Predicate matcher, + @Nullable final String reason) { + getModelWriter().deleteItemsFromDatabase(matcher, reason); // Call the components-removed callback scheduleCallbackTask(c -> c.bindWorkspaceComponentsRemoved(matcher)); diff --git a/src/com/android/launcher3/model/ModelWriter.java b/src/com/android/launcher3/model/ModelWriter.java index 015abe94bc..0a68d4aa34 100644 --- a/src/com/android/launcher3/model/ModelWriter.java +++ b/src/com/android/launcher3/model/ModelWriter.java @@ -23,8 +23,10 @@ import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; import android.net.Uri; +import android.text.TextUtils; import android.util.Log; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.launcher3.LauncherAppState; @@ -272,27 +274,30 @@ public class ModelWriter { /** * Removes the specified item from the database */ - public void deleteItemFromDatabase(ItemInfo item) { - deleteItemsFromDatabase(Arrays.asList(item)); + public void deleteItemFromDatabase(ItemInfo item, @Nullable final String reason) { + deleteItemsFromDatabase(Arrays.asList(item), reason); } /** * Removes all the items from the database matching {@param matcher}. */ - public void deleteItemsFromDatabase(Predicate matcher) { + public void deleteItemsFromDatabase(@NonNull final Predicate matcher, + @Nullable final String reason) { deleteItemsFromDatabase(StreamSupport.stream(mBgDataModel.itemsIdMap.spliterator(), false) - .filter(matcher).collect(Collectors.toList())); + .filter(matcher).collect(Collectors.toList()), reason); } /** * Removes the specified items from the database */ - public void deleteItemsFromDatabase(final Collection items) { + public void deleteItemsFromDatabase(final Collection items, + @Nullable final String reason) { ModelVerifier verifier = new ModelVerifier(); FileLog.d(TAG, "removing items from db " + items.stream().map( (item) -> item.getTargetComponent() == null ? "" : item.getTargetComponent().getPackageName()).collect( - Collectors.joining(","))); + Collectors.joining(",")) + + ". Reason: [" + (TextUtils.isEmpty(reason) ? "unknown" : reason) + "]"); notifyDelete(items); enqueueDeleteRunnable(() -> { for (ItemInfo item : items) { @@ -328,14 +333,15 @@ public class ModelWriter { /** * Deletes the widget info and the widget id. */ - public void deleteWidgetInfo(final LauncherAppWidgetInfo info, LauncherAppWidgetHost host) { + public void deleteWidgetInfo(final LauncherAppWidgetInfo info, LauncherAppWidgetHost host, + @Nullable final String reason) { notifyDelete(Collections.singleton(info)); if (host != null && !info.isCustomWidget() && info.isWidgetIdAllocated()) { // Deleting an app widget ID is a void call but writes to disk before returning // to the caller... enqueueDeleteRunnable(() -> host.deleteAppWidgetId(info.appWidgetId)); } - deleteItemFromDatabase(info); + deleteItemFromDatabase(info, reason); } private void notifyDelete(Collection items) { diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java index 239dd4565a..489bc38376 100644 --- a/src/com/android/launcher3/model/PackageUpdatedTask.java +++ b/src/com/android/launcher3/model/PackageUpdatedTask.java @@ -311,7 +311,9 @@ public class PackageUpdatedTask extends BaseModelUpdateTask { bindUpdatedWorkspaceItems(updatedWorkspaceItems); if (!removedShortcuts.isEmpty()) { - deleteAndBindComponentsRemoved(ItemInfoMatcher.ofItemIds(removedShortcuts)); + deleteAndBindComponentsRemoved( + ItemInfoMatcher.ofItemIds(removedShortcuts), + "removed because the target component is invalid"); } if (!widgets.isEmpty()) { @@ -340,7 +342,8 @@ public class PackageUpdatedTask extends BaseModelUpdateTask { Predicate removeMatch = ItemInfoMatcher.ofPackages(removedPackages, mUser) .or(ItemInfoMatcher.ofComponents(removedComponents, mUser)) .and(ItemInfoMatcher.ofItemIds(forceKeepShortcuts).negate()); - deleteAndBindComponentsRemoved(removeMatch); + deleteAndBindComponentsRemoved(removeMatch, + "removed because the corresponding package or component is removed"); // Remove any queued items from the install queue ItemInstallQueue.INSTANCE.get(context) diff --git a/src/com/android/launcher3/model/ShortcutsChangedTask.java b/src/com/android/launcher3/model/ShortcutsChangedTask.java index 4296d32f24..1026e0bd5b 100644 --- a/src/com/android/launcher3/model/ShortcutsChangedTask.java +++ b/src/com/android/launcher3/model/ShortcutsChangedTask.java @@ -108,7 +108,8 @@ public class ShortcutsChangedTask extends BaseModelUpdateTask { deleteAndBindComponentsRemoved(ItemInfoMatcher.ofShortcutKeys( nonPinnedIds.stream() .map(id -> new ShortcutKey(mPackageName, mUser, id)) - .collect(Collectors.toSet()))); + .collect(Collectors.toSet())), + "removed because the shortcut is no longer available in shortcut service"); } } diff --git a/src/com/android/launcher3/model/UserLockStateChangedTask.java b/src/com/android/launcher3/model/UserLockStateChangedTask.java index 5048e13e3e..1565b19f4c 100644 --- a/src/com/android/launcher3/model/UserLockStateChangedTask.java +++ b/src/com/android/launcher3/model/UserLockStateChangedTask.java @@ -96,7 +96,9 @@ public class UserLockStateChangedTask extends BaseModelUpdateTask { } bindUpdatedWorkspaceItems(updatedWorkspaceItemInfos); if (!removedKeys.isEmpty()) { - deleteAndBindComponentsRemoved(ItemInfoMatcher.ofShortcutKeys(removedKeys)); + deleteAndBindComponentsRemoved(ItemInfoMatcher.ofShortcutKeys(removedKeys), + "removed during unlock because it's no longer available" + + " (possibly due to clear data)"); } // Remove shortcut id map for that user diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java index c4d2bc44c8..e95a787cfd 100644 --- a/src/com/android/launcher3/touch/ItemClickHandler.java +++ b/src/com/android/launcher3/touch/ItemClickHandler.java @@ -176,7 +176,8 @@ public class ItemClickHandler { .setNeutralButton(R.string.abandoned_clean_this, (d, i) -> launcher.getWorkspace() .persistRemoveItemsByMatcher(ItemInfoMatcher.ofPackages( - Collections.singleton(packageName), user))) + Collections.singleton(packageName), user), + "user explicitly removes the promise app icon")) .create().show(); } @@ -259,7 +260,8 @@ public class ItemClickHandler { // Remove the icon if launcher is successfully initialized launcher.getWorkspace().persistRemoveItemsByMatcher(ItemInfoMatcher .ofShortcutKeys(Collections.singleton(ShortcutKey - .fromItemInfo(shortcut)))); + .fromItemInfo(shortcut))), + "user explicitly removes disabled shortcut"); }) .create() .show(); diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java index 53c772fec2..08651523a6 100644 --- a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java +++ b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java @@ -457,7 +457,8 @@ public class LauncherAppWidgetHostView extends BaseLauncherAppWidgetHostView } // Remove and rebind the current widget (which was inflated in the wrong // orientation), but don't delete it from the database - mLauncher.removeItem(this, info, false /* deleteFromDb */); + mLauncher.removeItem(this, info, false /* deleteFromDb */, + "widget removed because of configuration change"); mLauncher.bindAppWidget(info); }