Merge "Refactoring startShareActivity to allow crop to be passed in." into sc-dev
diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml
index 9e76ce3..4fd2e40 100644
--- a/AndroidManifest-common.xml
+++ b/AndroidManifest-common.xml
@@ -141,7 +141,7 @@
         TODO: Add proper permissions
         -->
         <provider
-            android:name="com.android.launcher3.graphics.GridOptionsProvider"
+            android:name="com.android.launcher3.graphics.GridCustomizationsProvider"
             android:authorities="${packageName}.grid_control"
             android:exported="true" />
 
diff --git a/quickstep/res/layout/search_result_widget_live.xml b/quickstep/res/layout/search_result_widget_live.xml
index ffbad55..f2ac6cd 100644
--- a/quickstep/res/layout/search_result_widget_live.xml
+++ b/quickstep/res/layout/search_result_widget_live.xml
@@ -1,5 +1,43 @@
 <?xml version="1.0" encoding="utf-8"?>
-<com.android.launcher3.search.SearchResultWidget android:layout_height="wrap_content"
+<com.android.launcher3.search.SearchResultWidget
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:launcher="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:paddingVertical="@dimen/widget_section_vertical_padding"
+    android:layout_marginBottom="@dimen/widget_section_vertical_padding"
     android:gravity="center"
-    android:layout_width="match_parent" />
\ No newline at end of file
+    >
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:orientation="horizontal"
+        android:layout_height="wrap_content">
+
+        <com.android.launcher3.BubbleTextView
+            android:id="@+id/widget_provider"
+            android:layout_width="wrap_content"
+            android:layout_height="@dimen/widget_section_height"
+            android:drawablePadding="@dimen/widget_section_horizontal_padding"
+            android:focusable="true"
+            android:gravity="start|center_vertical"
+            android:paddingHorizontal="@dimen/widget_section_horizontal_padding"
+            android:paddingVertical="@dimen/widget_section_horizontal_padding"
+            android:singleLine="true"
+            android:textColor="?android:attr/textColorPrimary"
+            android:textSize="16sp"
+            android:textAlignment="viewStart"
+            launcher:iconDisplay="widget_section"
+            launcher:layoutHorizontal="true"
+            launcher:iconSizeOverride="@dimen/widget_section_icon_size" />
+
+        <TextView
+            android:id="@+id/widget_label"
+            android:layout_width="wrap_content"
+            android:layout_height="@dimen/widget_section_height"
+            android:textSize="16sp" />
+
+    </LinearLayout>
+
+</com.android.launcher3.search.SearchResultWidget>
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/search/DeviceSearchAdapterProvider.java b/quickstep/src/com/android/launcher3/search/DeviceSearchAdapterProvider.java
index 1e6243c..02be0b9 100644
--- a/quickstep/src/com/android/launcher3/search/DeviceSearchAdapterProvider.java
+++ b/quickstep/src/com/android/launcher3/search/DeviceSearchAdapterProvider.java
@@ -18,10 +18,7 @@
 
 import static com.android.launcher3.allapps.AllAppsGridAdapter.VIEW_TYPE_ICON;
 
-import android.app.search.Query;
-import android.app.search.SearchSession;
 import android.app.search.SearchTarget;
-import android.app.search.SearchTargetEvent;
 import android.util.Log;
 import android.util.SparseIntArray;
 import android.view.LayoutInflater;
@@ -125,6 +122,7 @@
                     return VIEW_TYPE_SEARCH_SLICE;
                 }
                 Log.w(TAG, "Dropping as LayoutType.ICON_SLICE target doesn't contain sliceUri.");
+                break;
             case LayoutType.ICON_DOUBLE_HORIZONTAL_TEXT:
             case LayoutType.ICON_SINGLE_HORIZONTAL_TEXT:
                 return VIEW_TYPE_SEARCH_ICON_ROW;
@@ -133,6 +131,11 @@
                     return VIEW_TYPE_SEARCH_THUMBNAIL;
                 }
                 Log.w(TAG, "Dropping as LayoutType.THUMBNAIL target doesn't contain searchAction.");
+                break;
+            case LayoutType.WIDGET_PREVIEW:
+                return VIEW_TYPE_SEARCH_WIDGET_PREVIEW;
+            case LayoutType.WIDGET_LIVE:
+                return VIEW_TYPE_SEARCH_WIDGET_LIVE;
         }
 
         return -1;
diff --git a/quickstep/src/com/android/launcher3/search/SearchResultIconRow.java b/quickstep/src/com/android/launcher3/search/SearchResultIconRow.java
index 66214f3..eafbc73 100644
--- a/quickstep/src/com/android/launcher3/search/SearchResultIconRow.java
+++ b/quickstep/src/com/android/launcher3/search/SearchResultIconRow.java
@@ -102,6 +102,7 @@
 
     @Override
     public void apply(SearchTarget parentTarget, List<SearchTarget> children) {
+        showSubtitleIfNeeded(null);
         mResultIcon.applySearchTarget(parentTarget, children, this::onItemInfoCreated);
         if (parentTarget.getShortcutInfo() != null) {
             updateWithShortcutInfo(parentTarget.getShortcutInfo());
diff --git a/quickstep/src/com/android/launcher3/search/SearchResultWidget.java b/quickstep/src/com/android/launcher3/search/SearchResultWidget.java
index 69da515..da14959 100644
--- a/quickstep/src/com/android/launcher3/search/SearchResultWidget.java
+++ b/quickstep/src/com/android/launcher3/search/SearchResultWidget.java
@@ -25,28 +25,34 @@
 import android.view.GestureDetector;
 import android.view.MotionEvent;
 import android.view.View;
-import android.widget.RelativeLayout;
+import android.widget.LinearLayout;
+import android.widget.TextView;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import com.android.launcher3.AppWidgetResizeFrame;
+import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.CheckLongPressHelper;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
 import com.android.launcher3.allapps.search.SearchWidgetInfoContainer;
 import com.android.launcher3.dragndrop.DraggableView;
+import com.android.launcher3.model.data.PackageItemInfo;
 import com.android.launcher3.touch.ItemLongClickListener;
+import com.android.launcher3.widget.PendingAddWidgetInfo;
+
+import java.util.List;
+
 
 /**
  * displays live version of a widget upon receiving {@link AppWidgetProviderInfo} from Search
  * provider
  */
-public class SearchResultWidget extends RelativeLayout implements
-        SearchTargetHandler, DraggableView {
+public class SearchResultWidget extends LinearLayout implements SearchTargetHandler, DraggableView,
+        View.OnLongClickListener {
 
-    private static final String TAG = "SearchResultWidget";
-
-    public static final String TARGET_TYPE_WIDGET_LIVE = "widget";
 
     private final Rect mWidgetOffset = new Rect();
 
@@ -56,10 +62,9 @@
     private final AppWidgetHostView mHostView;
     private final float mScaleToFit;
 
-    private SearchTarget mSearchTarget;
-    private AppWidgetProviderInfo mProviderInfo;
-
     private SearchWidgetInfoContainer mInfoContainer;
+    private BubbleTextView mWidgetProvider;
+    private TextView mWidgetLabel;
 
     public SearchResultWidget(@NonNull Context context) {
         this(context, null, 0);
@@ -80,7 +85,8 @@
 
         // detect tap event on widget container for search target event reporting
         mClickDetector = new GestureDetector(context,
-                new ClickListener(() -> handleSelection(SearchTargetEvent.ACTION_LAUNCH_TOUCH)));
+                new ClickListener(
+                        () -> reportEvent(SearchTargetEvent.ACTION_LAUNCH_TOUCH)));
         mLongPressHelper = new CheckLongPressHelper(this);
         mLongPressHelper.setLongPressTimeoutFactor(1);
         setOnLongClickListener(this);
@@ -89,9 +95,42 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
+        mWidgetProvider = findViewById(R.id.widget_provider);
+        mWidgetLabel = findViewById(R.id.widget_label);
         addView(mHostView);
     }
 
+    @Override
+    public void apply(SearchTarget parentTarget, List<SearchTarget> children) {
+        AppWidgetProviderInfo providerInfo = parentTarget.getAppWidgetProviderInfo();
+        removeListener();
+
+        showWidgetInfo(providerInfo);
+        mInfoContainer = mLauncher.getLiveSearchManager().getPlaceHolderWidget(providerInfo);
+        if (mInfoContainer == null) {
+            setVisibility(GONE);
+            return;
+        }
+        setVisibility(VISIBLE);
+        mInfoContainer.attachWidget(mHostView);
+        PendingAddWidgetInfo info = (PendingAddWidgetInfo) mHostView.getTag();
+        int[] size = mLauncher.getWorkspace().estimateItemSize(info);
+        mHostView.getLayoutParams().width = size[0];
+        mHostView.getLayoutParams().height = size[1];
+        AppWidgetResizeFrame.updateWidgetSizeRanges(mHostView, mLauncher, info.spanX,
+                info.spanY);
+        mHostView.requestLayout();
+        setTag(info);
+    }
+
+    private void showWidgetInfo(AppWidgetProviderInfo providerInfo) {
+        String title = providerInfo.loadLabel(mLauncher.getPackageManager());
+        PackageItemInfo pinfo = new PackageItemInfo(providerInfo.provider.getPackageName());
+        pinfo.user = providerInfo.getProfile();
+        mWidgetProvider.applyFromItemInfoWithIcon(pinfo);
+        mWidgetLabel.setText(title);
+    }
+
     /**
      * Stops hostView from getting updates on a widget provider
      */
@@ -101,7 +140,7 @@
         }
     }
 
-    public void handleSelection(int eventType) {
+    private void reportEvent(int eventType) {
         SearchSessionTracker.INSTANCE.get(getContext()).notifyEvent(
                 new SearchTargetEvent.Builder("search target id", eventType).build());
     }
diff --git a/quickstep/src/com/android/launcher3/search/SearchResultWidgetPreview.java b/quickstep/src/com/android/launcher3/search/SearchResultWidgetPreview.java
index c9fc8e1..1786baf 100644
--- a/quickstep/src/com/android/launcher3/search/SearchResultWidgetPreview.java
+++ b/quickstep/src/com/android/launcher3/search/SearchResultWidgetPreview.java
@@ -54,9 +54,7 @@
     private WidgetCell mWidgetCell;
     private Toast mWidgetToast;
 
-    private SearchTarget mSearchTarget;
-
-
+    private String mTargetId;
     public SearchResultWidgetPreview(Context context) {
         this(context, null, 0);
     }
@@ -83,7 +81,7 @@
 
     @Override
     public void apply(SearchTarget parentTarget, List<SearchTarget> children) {
-        mSearchTarget = parentTarget;
+        mTargetId = parentTarget.getId();
         AppWidgetProviderInfo providerInfo = parentTarget.getAppWidgetProviderInfo();
         LauncherAppWidgetProviderInfo pInfo = LauncherAppWidgetProviderInfo.fromProviderInfo(
                 getContext(), providerInfo);
@@ -114,18 +112,18 @@
         new PendingItemDragHelper(mWidgetCell).startDrag(
                 imageView.getBitmapBounds(), imageView.getBitmap().getWidth(), imageView.getWidth(),
                 new Point(loc[0], loc[1]), mLauncher.getAppsView(), new DragOptions());
-        handleSelection(SearchTargetEvent.ACTION_LONGPRESS);
+        reportEvent(SearchTargetEvent.ACTION_LONGPRESS);
         return true;
     }
 
     @Override
     public void onClick(View view) {
         mWidgetToast = BaseWidgetSheet.showWidgetToast(getContext(), mWidgetToast);
-        handleSelection(SearchTargetEvent.ACTION_LAUNCH_TOUCH);
+        reportEvent(SearchTargetEvent.ACTION_LAUNCH_TOUCH);
     }
 
-    public void handleSelection(int eventType) {
+    private void reportEvent(int eventType) {
         SearchSessionTracker.INSTANCE.get(getContext()).notifyEvent(
-                new SearchTargetEvent.Builder(mSearchTarget.getId(), eventType).build());
+                new SearchTargetEvent.Builder(mTargetId, eventType).build());
     }
 }
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 13b7d8d..5a2d6da 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -721,11 +721,12 @@
 
     @UiThread
     public void onGestureStarted(boolean isLikelyToStartNewTask) {
-        // Temporarily disable this until we have a view that we can use
-        // InteractionJankMonitorWrapper.begin(mRecentsView,
-        //         InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH, 2000 /* ms timeout */);
-        // InteractionJankMonitorWrapper.begin(mRecentsView,
-        //         InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_HOME);
+        if (mRecentsView != null) {
+            InteractionJankMonitorWrapper.begin(mRecentsView,
+                    InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH, 2000 /* ms timeout */);
+            InteractionJankMonitorWrapper.begin(mRecentsView,
+                    InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_HOME);
+        }
         notifyGestureStartedAsync();
         setIsLikelyToStartNewTask(isLikelyToStartNewTask, false /* animate */);
         mStateCallback.setStateOnUiThread(STATE_GESTURE_STARTED);
diff --git a/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java b/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
index 80308a5..65847f1 100644
--- a/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
+++ b/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
@@ -26,6 +26,7 @@
 import com.android.launcher3.MainProcessInitializer;
 import com.android.launcher3.util.Executors;
 import com.android.quickstep.logging.SettingsChangeLogger;
+import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
 import com.android.systemui.shared.system.ThreadedRendererCompat;
 
 @SuppressWarnings("unused")
@@ -35,7 +36,11 @@
     private static final String TAG = "QuickstepProcessInitializer";
     private static final int SETUP_DELAY_MILLIS = 5000;
 
-    public QuickstepProcessInitializer(Context context) { }
+    public QuickstepProcessInitializer(Context context) {
+        // Fake call to create an instance of InteractionJankMonitor to avoid binder calls during
+        // its initialization during transitions.
+        InteractionJankMonitorWrapper.cancel(-1);
+    }
 
     @Override
     protected void init(Context context) {
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 8066aa6..51d8e66 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -67,7 +67,7 @@
 import androidx.core.os.BuildCompat;
 
 import com.android.launcher3.dragndrop.FolderAdaptiveIcon;
-import com.android.launcher3.graphics.GridOptionsProvider;
+import com.android.launcher3.graphics.GridCustomizationsProvider;
 import com.android.launcher3.graphics.TintedDrawableSpan;
 import com.android.launcher3.icons.IconProvider;
 import com.android.launcher3.icons.LauncherIcons;
@@ -520,7 +520,7 @@
     public static boolean isGridOptionsEnabled(Context context) {
         return isComponentEnabled(context.getPackageManager(),
                 context.getPackageName(),
-                GridOptionsProvider.class.getName());
+                GridCustomizationsProvider.class.getName());
     }
 
     private static boolean isComponentEnabled(PackageManager pm, String pkgName, String clsName) {
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 65eba20..8a45c81 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -111,9 +111,11 @@
 import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlay;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 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.
@@ -3002,38 +3004,27 @@
      * shortcuts are not removed.
      */
     public void removeItemsByMatcher(final ItemInfoMatcher matcher) {
-        for (final CellLayout layoutParent: getWorkspaceAndHotseatCellLayouts()) {
-            final ViewGroup layout = layoutParent.getShortcutsAndWidgets();
+        for (CellLayout layout : getWorkspaceAndHotseatCellLayouts()) {
+            ShortcutAndWidgetContainer container = layout.getShortcutsAndWidgets();
+            // Iterate in reverse order as we are removing items
+            for (int i = container.getChildCount() - 1; i >= 0; i--) {
+                View child = container.getChildAt(i);
+                ItemInfo info = (ItemInfo) child.getTag();
 
-            IntSparseArrayMap<View> idToViewMap = new IntSparseArrayMap<>();
-            ArrayList<ItemInfo> items = new ArrayList<>();
-            for (int j = 0; j < layout.getChildCount(); j++) {
-                final View view = layout.getChildAt(j);
-                if (view.getTag() instanceof ItemInfo) {
-                    ItemInfo item = (ItemInfo) view.getTag();
-                    items.add(item);
-                    idToViewMap.put(item.id, view);
-                }
-            }
-
-            for (ItemInfo itemToRemove : matcher.filterItemInfos(items)) {
-                View child = idToViewMap.get(itemToRemove.id);
-
-                if (child != null) {
-                    // Note: We can not remove the view directly from CellLayoutChildren as this
-                    // does not re-mark the spaces as unoccupied.
-                    layoutParent.removeViewInLayout(child);
+                if (matcher.matchesInfo(info)) {
+                    layout.removeViewInLayout(child);
                     if (child instanceof DropTarget) {
                         mDragController.removeDropTarget((DropTarget) child);
                     }
-                } else if (itemToRemove.container >= 0) {
-                    // The item may belong to a folder.
-                    View parent = idToViewMap.get(itemToRemove.container);
-                    if (parent instanceof FolderIcon) {
-                        FolderInfo folderInfo = (FolderInfo) parent.getTag();
-                        folderInfo.remove((WorkspaceItemInfo) itemToRemove, false);
-                        if (((FolderIcon) parent).getFolder().isOpen()) {
-                            ((FolderIcon) parent).getFolder().close(false /* animate */);
+                } else if (child instanceof FolderIcon) {
+                    FolderInfo folderInfo = (FolderInfo) info;
+                    List<WorkspaceItemInfo> matches = folderInfo.contents.stream()
+                            .filter(matcher::matchesInfo)
+                            .collect(Collectors.toList());
+                    if (!matches.isEmpty()) {
+                        folderInfo.removeAll(matches, false);
+                        if (((FolderIcon) child).getFolder().isOpen()) {
+                            ((FolderIcon) child).getFolder().close(false /* animate */);
                         }
                     }
                 }
@@ -3143,9 +3134,8 @@
     }
 
     public void removeAbandonedPromise(String packageName, UserHandle user) {
-        HashSet<String> packages = new HashSet<>(1);
-        packages.add(packageName);
-        ItemInfoMatcher matcher = ItemInfoMatcher.ofPackages(packages, user);
+        ItemInfoMatcher matcher = ItemInfoMatcher.ofPackages(
+                Collections.singleton(packageName), user);
         mLauncher.getModelWriter().deleteItemsFromDatabase(matcher);
         removeItemsByMatcher(matcher);
     }
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 1c5c222..086d665 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -95,7 +95,7 @@
             "ENABLE_SUGGESTED_ACTIONS_OVERVIEW", true, "Show chip hints on the overview screen");
 
 
-    public static final BooleanFlag ENABLE_DEVICE_SEARCH = getDebugFlag(
+    public static final BooleanFlag ENABLE_DEVICE_SEARCH = new DeviceFlag(
             "ENABLE_DEVICE_SEARCH", false, "Allows on device search in all apps");
 
     public static final BooleanFlag USE_SEARCH_API = getDebugFlag(
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 63fa391..61938d1 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -1394,10 +1394,10 @@
         mItemsInvalidated = true;
     }
 
-    public void onRemove(WorkspaceItemInfo item) {
+    @Override
+    public void onRemove(List<WorkspaceItemInfo> items) {
         mItemsInvalidated = true;
-        View v = getViewForInfo(item);
-        mContent.removeItem(v);
+        items.stream().map(this::getViewForInfo).forEach(mContent::removeItem);
         if (mState == STATE_ANIMATING) {
             mRearrangeOnClose = true;
         } else {
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 3296eed..fe310f6 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -695,9 +695,9 @@
     }
 
     @Override
-    public void onRemove(WorkspaceItemInfo item) {
+    public void onRemove(List<WorkspaceItemInfo> items) {
         boolean wasDotted = mDotInfo.hasDot();
-        mDotInfo.subtractDotInfo(mActivity.getDotInfoForItem(item));
+        items.stream().map(mActivity::getDotInfoForItem).forEach(mDotInfo::subtractDotInfo);
         boolean isDotted = mDotInfo.hasDot();
         updateDotScale(wasDotted, isDotted);
         setContentDescription(getAccessiblityTitle(mInfo.title));
diff --git a/src/com/android/launcher3/graphics/GridOptionsProvider.java b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
similarity index 97%
rename from src/com/android/launcher3/graphics/GridOptionsProvider.java
rename to src/com/android/launcher3/graphics/GridCustomizationsProvider.java
index 08d7e4c..cb42e7a 100644
--- a/src/com/android/launcher3/graphics/GridOptionsProvider.java
+++ b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
@@ -40,9 +40,9 @@
  *     /default_grid: Call update to set the current grid, with values
  *          name: name of the grid to apply
  */
-public class GridOptionsProvider extends ContentProvider {
+public class GridCustomizationsProvider extends ContentProvider {
 
-    private static final String TAG = "GridOptionsProvider";
+    private static final String TAG = "GridCustomizationsProvider";
 
     private static final String KEY_NAME = "name";
     private static final String KEY_ROWS = "rows";
diff --git a/src/com/android/launcher3/model/ModelWriter.java b/src/com/android/launcher3/model/ModelWriter.java
index 2c99df7..f7b43d6 100644
--- a/src/com/android/launcher3/model/ModelWriter.java
+++ b/src/com/android/launcher3/model/ModelWriter.java
@@ -51,6 +51,7 @@
 import java.util.concurrent.Executor;
 import java.util.function.Supplier;
 import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
 
 /**
  * Class for handling model updates.
@@ -259,7 +260,9 @@
      * Removes all the items from the database matching {@param matcher}.
      */
     public void deleteItemsFromDatabase(ItemInfoMatcher matcher) {
-        deleteItemsFromDatabase(matcher.filterItemInfos(mBgDataModel.itemsIdMap));
+        deleteItemsFromDatabase(StreamSupport.stream(mBgDataModel.itemsIdMap.spliterator(), false)
+                        .filter(matcher::matchesInfo)
+                        .collect(Collectors.toList()));
     }
 
     /**
diff --git a/src/com/android/launcher3/model/data/FolderInfo.java b/src/com/android/launcher3/model/data/FolderInfo.java
index 06a2c92..cc783f7 100644
--- a/src/com/android/launcher3/model/data/FolderInfo.java
+++ b/src/com/android/launcher3/model/data/FolderInfo.java
@@ -40,6 +40,8 @@
 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;
 
@@ -137,9 +139,16 @@
      * @param item
      */
     public void remove(WorkspaceItemInfo item, boolean animate) {
-        contents.remove(item);
+        removeAll(Collections.singletonList(item), animate);
+    }
+
+    /**
+     * Remove all matching app or shortcut. Does not change the DB.
+     */
+    public void removeAll(List<WorkspaceItemInfo> items, boolean animate) {
+        contents.removeAll(items);
         for (int i = 0; i < mListeners.size(); i++) {
-            mListeners.get(i).onRemove(item);
+            mListeners.get(i).onRemove(items);
         }
         itemsChanged(animate);
     }
@@ -166,9 +175,9 @@
     }
 
     public interface FolderListener {
-        public void onAdd(WorkspaceItemInfo item, int rank);
-        public void onRemove(WorkspaceItemInfo item);
-        public void onItemsChanged(boolean animate);
+        void onAdd(WorkspaceItemInfo item, int rank);
+        void onRemove(List<WorkspaceItemInfo> item);
+        void onItemsChanged(boolean animate);
     }
 
     public boolean hasOption(int optionFlag) {
diff --git a/src/com/android/launcher3/util/ItemInfoMatcher.java b/src/com/android/launcher3/util/ItemInfoMatcher.java
index d26bb57..354609d 100644
--- a/src/com/android/launcher3/util/ItemInfoMatcher.java
+++ b/src/com/android/launcher3/util/ItemInfoMatcher.java
@@ -20,10 +20,7 @@
 import android.os.UserHandle;
 
 import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.model.data.FolderInfo;
 import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.model.data.LauncherAppWidgetInfo;
-import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.shortcuts.ShortcutKey;
 
 import java.util.HashSet;
@@ -37,34 +34,15 @@
     boolean matches(ItemInfo info, ComponentName cn);
 
     /**
-     * Filters {@param infos} to those satisfying the {@link #matches(ItemInfo, ComponentName)}.
+     * Returns true if the itemInfo matches this check
      */
-    default HashSet<ItemInfo> filterItemInfos(Iterable<ItemInfo> infos) {
-        HashSet<ItemInfo> filtered = new HashSet<>();
-        for (ItemInfo i : infos) {
-            if (i instanceof WorkspaceItemInfo) {
-                WorkspaceItemInfo info = (WorkspaceItemInfo) i;
-                ComponentName cn = info.getTargetComponent();
-                if (cn != null && matches(info, cn)) {
-                    filtered.add(info);
-                }
-            } else if (i instanceof FolderInfo) {
-                FolderInfo info = (FolderInfo) i;
-                for (WorkspaceItemInfo s : info.contents) {
-                    ComponentName cn = s.getTargetComponent();
-                    if (cn != null && matches(s, cn)) {
-                        filtered.add(s);
-                    }
-                }
-            } else if (i instanceof LauncherAppWidgetInfo) {
-                LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) i;
-                ComponentName cn = info.providerName;
-                if (cn != null && matches(info, cn)) {
-                    filtered.add(info);
-                }
-            }
+    default boolean matchesInfo(ItemInfo info) {
+        if (info != null) {
+            ComponentName cn = info.getTargetComponent();
+            return cn != null && matches(info, cn);
+        } else {
+            return false;
         }
-        return filtered;
     }
 
     /**
@@ -96,7 +74,7 @@
         return (info, cn) -> components.contains(cn) && info.user.equals(user);
     }
 
-    static ItemInfoMatcher ofPackages(HashSet<String> packageNames, UserHandle user) {
+    static ItemInfoMatcher ofPackages(Set<String> packageNames, UserHandle user) {
         return (info, cn) -> packageNames.contains(cn.getPackageName()) && info.user.equals(user);
     }