Merge "Some cleanup in RecentsView" into sc-dev
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index 1603321..2a06830 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -59,7 +59,7 @@
     <string name="all_apps_prediction_tip">Your predicted apps</string>
 
     <!-- Content description for a close button. [CHAR LIMIT=NONE] -->
-    <string  name="gesture_tutorial_close_button_content_description" translatable="false">Close</string>
+    <string name="gesture_tutorial_close_button_content_description" translatable="false">Close</string>
 
     <!-- Hotseat educational strings for users who don't qualify for migration -->
     <string name="hotseat_edu_title_migrate">Get app suggestions on the bottom row of your Home screen</string>
@@ -91,14 +91,6 @@
     <!-- content description for hotseat items -->
     <string name="hotseat_prediction_content_description">Predicted app: <xliff:g id="title" example="Chrome">%1$s</xliff:g></string>
 
-    <!-- primary educational text shown for first time search users -->
-    <string name="search_edu_primary">Search your phone for apps, people, settings and more!</string>
-    <!-- secondary educational text shown for first time search users -->
-    <string name="search_edu_secondary">Tap keyboard search button to launch the first search
-        result.</string>
-
-    <!-- Dismiss button string for search education view -->
-    <string name="search_edu_dismiss">Got it.</string>
     <!-- Feedback shown during interactive parts of Back gesture tutorial for left edge when the gesture is too far from the edge. [CHAR LIMIT=100] -->
     <string name="back_gesture_feedback_swipe_too_far_from_left_edge">Make sure you swipe from the far-left edge.</string>
     <!-- Feedback shown during interactive parts of Back gesture tutorial for left edge when the gesture is cancelled. [CHAR LIMIT=100] -->
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
index 7b31b13..74a253e 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
@@ -111,7 +111,7 @@
         float progressMultiplier = super.initCurrentAnimation();
         if (mToState == HINT_STATE) {
             // Track the drag across the entire height of the screen.
-            progressMultiplier = -1 / getShiftRange();
+            progressMultiplier = -1f / mLauncher.getDeviceProfile().heightPx;
         }
         return progressMultiplier;
     }
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 5a3e009..2a903eb 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -319,9 +319,9 @@
         mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED,
                 this::invalidateHandlerWithLauncher);
         mStateCallback.runOnceAtState(STATE_HANDLER_INVALIDATED | STATE_RESUME_LAST_TASK,
-                this::notifyTransitionCancelled);
+                this::resetStateForAnimationCancel);
         mStateCallback.runOnceAtState(STATE_HANDLER_INVALIDATED | STATE_FINISH_WITH_NO_END,
-                this::notifyTransitionCancelled);
+                this::resetStateForAnimationCancel);
 
         if (!LIVE_TILE.get()) {
             mStateCallback.addChangeListener(STATE_APP_CONTROLLER_RECEIVED | STATE_LAUNCHER_PRESENT
@@ -1383,10 +1383,6 @@
         mActivity.getRootView().setOnApplyWindowInsetsListener(null);
     }
 
-    private void notifyTransitionCancelled() {
-        mAnimationFactory.onTransitionCancelled();
-    }
-
     private void resetStateForAnimationCancel() {
         boolean wasVisible = mWasLauncherAlreadyVisible || mGestureStarted;
         mActivityInterface.onTransitionCancelled(wasVisible);
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index d810947..147297a 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -368,8 +368,6 @@
 
         void createActivityInterface(long transitionLength);
 
-        default void onTransitionCancelled() { }
-
         /**
          * @param attached Whether to show RecentsView alongside the app window. If false, recents
          *                 will be hidden by some property we can animate, e.g. alpha.
@@ -436,11 +434,6 @@
         }
 
         @Override
-        public void onTransitionCancelled() {
-            mActivity.getStateManager().goToState(mStartState, false /* animate */);
-        }
-
-        @Override
         public void setRecentsAttachedToAppWindow(boolean attached, boolean animate) {
             if (mIsAttachedToWindow == attached && animate) {
                 return;
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 1fb9465..e9d2eb4 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -239,8 +239,9 @@
         @BinderThread
         public void onSystemUiStateChanged(int stateFlags) {
             MAIN_EXECUTOR.execute(() -> {
+                int lastFlags = mDeviceState.getSystemUiStateFlags();
                 mDeviceState.setSystemUiFlags(stateFlags);
-                TouchInteractionService.this.onSystemUiFlagsChanged();
+                TouchInteractionService.this.onSystemUiFlagsChanged(lastFlags);
             });
         }
 
@@ -362,7 +363,7 @@
         mResetGestureInputConsumer = new ResetGestureInputConsumer(mTaskAnimationManager);
         mInputConsumer = InputConsumerController.getRecentsAnimationInputConsumer();
         mInputConsumer.registerInputConsumer();
-        onSystemUiFlagsChanged();
+        onSystemUiFlagsChanged(mDeviceState.getSystemUiStateFlags());
         onAssistantVisibilityChanged();
 
         // Temporarily disable model preload
@@ -414,7 +415,7 @@
     }
 
     @UiThread
-    private void onSystemUiFlagsChanged() {
+    private void onSystemUiFlagsChanged(int lastSysUIFlags) {
         if (mDeviceState.isUserUnlocked()) {
             int systemUiStateFlags = mDeviceState.getSystemUiStateFlags();
             SystemUiProxy.INSTANCE.get(this).setLastSystemUiStateFlags(systemUiStateFlags);
@@ -422,14 +423,17 @@
             mOverviewComponentObserver.getActivityInterface().onSystemUiFlagsChanged(
                     systemUiStateFlags);
 
-            // Update the tracing state
-            if ((systemUiStateFlags & SYSUI_STATE_TRACING_ENABLED) != 0) {
-                Log.d(TAG, "Starting tracing.");
-                ProtoTracer.INSTANCE.get(this).start();
-            } else {
-                Log.d(TAG, "Stopping tracing. Dumping to file="
-                    + ProtoTracer.INSTANCE.get(this).getTraceFile());
-                ProtoTracer.INSTANCE.get(this).stop();
+            if ((lastSysUIFlags & SYSUI_STATE_TRACING_ENABLED) !=
+                    (systemUiStateFlags & SYSUI_STATE_TRACING_ENABLED)) {
+                // Update the tracing state
+                if ((systemUiStateFlags & SYSUI_STATE_TRACING_ENABLED) != 0) {
+                    Log.d(TAG, "Starting tracing.");
+                    ProtoTracer.INSTANCE.get(this).start();
+                } else {
+                    Log.d(TAG, "Stopping tracing. Dumping to file="
+                            + ProtoTracer.INSTANCE.get(this).getTraceFile());
+                    ProtoTracer.INSTANCE.get(this).stop();
+                }
             }
         }
     }
diff --git a/res/layout/widget_cell_content.xml b/res/layout/widget_cell_content.xml
index 30bd8b1..0f6fc6c 100644
--- a/res/layout/widget_cell_content.xml
+++ b/res/layout/widget_cell_content.xml
@@ -17,16 +17,31 @@
     android:layout_width="wrap_content"
     android:layout_height="wrap_content">
 
-    <!-- The image of the widget. This view does not support padding. Any placement adjustment
-         should be done using margins. Width & height are set at runtime after scaling the preview
-         image. -->
-    <com.android.launcher3.widget.WidgetImageView
-        android:id="@+id/widget_preview"
+    <com.android.launcher3.widget.WidgetCellPreview
+        android:id="@+id/widget_preview_container"
         android:layout_width="0dp"
         android:layout_height="0dp"
         android:layout_weight="1"
         android:importantForAccessibility="no"
-        android:layout_marginVertical="8dp" />
+        android:layout_marginVertical="8dp">
+        <!-- The image of the widget. This view does not support padding. Any placement adjustment
+             should be done using margins. Width & height are set at runtime after scaling the
+             preview image. -->
+        <com.android.launcher3.widget.WidgetImageView
+            android:id="@+id/widget_preview"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:importantForAccessibility="no"
+            android:layout_gravity="fill"/>
+
+        <ImageView
+            android:id="@+id/widget_badge"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:importantForAccessibility="no"
+            android:layout_gravity="end|bottom"
+            android:layout_margin="@dimen/profile_badge_margin"/>
+    </com.android.launcher3.widget.WidgetCellPreview>
 
     <!-- The name of the widget. -->
     <TextView
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithmTest.java b/robolectric_tests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithmTest.java
index c2bf1ae..36b6f01 100644
--- a/robolectric_tests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithmTest.java
+++ b/robolectric_tests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithmTest.java
@@ -18,64 +18,197 @@
 
 import static android.os.Looper.getMainLooper;
 
+import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.matches;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.verify;
 import static org.robolectric.Shadows.shadowOf;
 
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.os.UserHandle;
+
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.icons.ComponentWithLabel;
+import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.model.data.PackageItemInfo;
+import com.android.launcher3.popup.PopupDataProvider;
 import com.android.launcher3.search.SearchCallback;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.widget.model.WidgetsListBaseEntry;
+import com.android.launcher3.widget.model.WidgetsListContentEntry;
+import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
+import com.android.launcher3.widget.model.WidgetsListSearchHeaderEntry;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.shadows.ShadowPackageManager;
+import org.robolectric.util.ReflectionHelpers;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
-import java.util.function.Consumer;
 
 @RunWith(RobolectricTestRunner.class)
 public class SimpleWidgetsSearchAlgorithmTest {
 
+    @Mock private IconCache mIconCache;
+
+    private InvariantDeviceProfile mTestProfile;
+    private WidgetsListHeaderEntry mCalendarHeaderEntry;
+    private WidgetsListContentEntry mCalendarContentEntry;
+    private WidgetsListHeaderEntry mCameraHeaderEntry;
+    private WidgetsListContentEntry mCameraContentEntry;
+    private WidgetsListHeaderEntry mClockHeaderEntry;
+    private WidgetsListContentEntry mClockContentEntry;
+    private Context mContext;
+
     private SimpleWidgetsSearchAlgorithm mSimpleWidgetsSearchAlgorithm;
     @Mock
-    private WidgetsPickerSearchPipeline mSearchPipeline;
+    private PopupDataProvider mDataProvider;
     @Mock
     private SearchCallback<WidgetsListBaseEntry> mSearchCallback;
-    @Captor
-    private ArgumentCaptor<Consumer<List<WidgetsListBaseEntry>>> mConsumerCaptor;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mSimpleWidgetsSearchAlgorithm = new SimpleWidgetsSearchAlgorithm(mSearchPipeline);
+        doAnswer(invocation -> {
+            ComponentWithLabel componentWithLabel = (ComponentWithLabel) invocation.getArgument(0);
+            return componentWithLabel.getComponent().getShortClassName();
+        }).when(mIconCache).getTitleNoCache(any());
+        mTestProfile = new InvariantDeviceProfile();
+        mTestProfile.numRows = 5;
+        mTestProfile.numColumns = 5;
+        mContext = RuntimeEnvironment.application;
+
+        mCalendarHeaderEntry =
+                createWidgetsHeaderEntry("com.example.android.Calendar", "Calendar", 2);
+        mCalendarContentEntry =
+                createWidgetsContentEntry("com.example.android.Calendar", "Calendar", 2);
+        mCameraHeaderEntry = createWidgetsHeaderEntry("com.example.android.Camera", "Camera", 11);
+        mCameraContentEntry = createWidgetsContentEntry("com.example.android.Camera", "Camera", 11);
+        mClockHeaderEntry = createWidgetsHeaderEntry("com.example.android.Clock", "Clock", 3);
+        mClockContentEntry = createWidgetsContentEntry("com.example.android.Clock", "Clock", 3);
+
+
+        mSimpleWidgetsSearchAlgorithm = new SimpleWidgetsSearchAlgorithm(mDataProvider);
+        doReturn(Collections.EMPTY_LIST).when(mDataProvider).getAllWidgets();
     }
 
     @Test
-    public void doSearch_shouldQueryPipeline() {
-        mSimpleWidgetsSearchAlgorithm.doSearch("abc", mSearchCallback);
+    public void filter_shouldMatchOnAppName() {
+        doReturn(List.of(mCalendarHeaderEntry, mCalendarContentEntry, mCameraHeaderEntry,
+                mCameraContentEntry, mClockHeaderEntry, mClockContentEntry))
+                .when(mDataProvider)
+                .getAllWidgets();
 
-        verify(mSearchPipeline).query(eq("abc"), any());
+        assertEquals(List.of(
+                new WidgetsListSearchHeaderEntry(
+                        mCalendarHeaderEntry.mPkgItem,
+                        mCalendarHeaderEntry.mTitleSectionName,
+                        mCalendarHeaderEntry.mWidgets),
+                mCalendarContentEntry,
+                new WidgetsListSearchHeaderEntry(
+                        mCameraHeaderEntry.mPkgItem,
+                        mCameraHeaderEntry.mTitleSectionName,
+                        mCameraHeaderEntry.mWidgets),
+                mCameraContentEntry),
+                SimpleWidgetsSearchAlgorithm.getFilteredWidgets(mDataProvider, "Ca"));
     }
 
     @Test
-    public void doSearch_shouldInformSearchCallbackOnQueryResult() {
-        ArrayList<WidgetsListBaseEntry> baseEntries = new ArrayList<>();
+    public void filter_shouldMatchOnWidgetLabel() {
+        doReturn(List.of(mCalendarHeaderEntry, mCalendarContentEntry, mCameraHeaderEntry,
+                mCameraContentEntry))
+                .when(mDataProvider)
+                .getAllWidgets();
 
-        mSimpleWidgetsSearchAlgorithm.doSearch("abc", mSearchCallback);
+        assertEquals(List.of(
+                new WidgetsListSearchHeaderEntry(
+                        mCalendarHeaderEntry.mPkgItem,
+                        mCalendarHeaderEntry.mTitleSectionName,
+                        mCalendarHeaderEntry.mWidgets.subList(1, 2)),
+                new WidgetsListContentEntry(
+                        mCalendarHeaderEntry.mPkgItem,
+                        mCalendarHeaderEntry.mTitleSectionName,
+                        mCalendarHeaderEntry.mWidgets.subList(1, 2)),
+                new WidgetsListSearchHeaderEntry(
+                        mCameraHeaderEntry.mPkgItem,
+                        mCameraHeaderEntry.mTitleSectionName,
+                        mCameraHeaderEntry.mWidgets.subList(1, 3)),
+                new WidgetsListContentEntry(
+                        mCameraHeaderEntry.mPkgItem,
+                        mCameraHeaderEntry.mTitleSectionName,
+                        mCameraHeaderEntry.mWidgets.subList(1, 3))),
+                SimpleWidgetsSearchAlgorithm.getFilteredWidgets(mDataProvider, "Widget1"));
+    }
 
-        verify(mSearchPipeline).query(eq("abc"), mConsumerCaptor.capture());
-        mConsumerCaptor.getValue().accept(baseEntries);
+    @Test
+    public void doSearch_shouldInformCallback() {
+        doReturn(List.of(mCalendarHeaderEntry, mCalendarContentEntry, mCameraHeaderEntry,
+                mCameraContentEntry, mClockHeaderEntry, mClockContentEntry))
+                .when(mDataProvider)
+                .getAllWidgets();
+        mSimpleWidgetsSearchAlgorithm.doSearch("Ca", mSearchCallback);
         shadowOf(getMainLooper()).idle();
-        // Verify SearchCallback#onSearchResult receives a query token along with the search
-        // results. The query token is the original query string concatenated with the query
-        // timestamp.
-        verify(mSearchCallback).onSearchResult(matches("abc\t\\d*"), eq(baseEntries));
+        verify(mSearchCallback).onSearchResult(
+                matches("Ca"), argThat(a -> a != null && !a.isEmpty()));
+    }
+
+    private WidgetsListHeaderEntry createWidgetsHeaderEntry(String packageName, String appName,
+            int numOfWidgets) {
+        List<WidgetItem> widgetItems = generateWidgetItems(packageName, numOfWidgets);
+        PackageItemInfo pInfo = createPackageItemInfo(packageName, appName,
+                widgetItems.get(0).user);
+
+        return new WidgetsListHeaderEntry(pInfo, /* titleSectionName= */ "", widgetItems);
+    }
+
+    private WidgetsListContentEntry createWidgetsContentEntry(String packageName, String appName,
+            int numOfWidgets) {
+        List<WidgetItem> widgetItems = generateWidgetItems(packageName, numOfWidgets);
+        PackageItemInfo pInfo = createPackageItemInfo(packageName, appName,
+                widgetItems.get(0).user);
+
+        return new WidgetsListContentEntry(pInfo, /* titleSectionName= */ "", widgetItems);
+    }
+
+    private PackageItemInfo createPackageItemInfo(String packageName, String appName,
+            UserHandle userHandle) {
+        PackageItemInfo pInfo = new PackageItemInfo(packageName);
+        pInfo.title = appName;
+        pInfo.user = userHandle;
+        pInfo.bitmap = BitmapInfo.of(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8), 0);
+        return pInfo;
+    }
+
+    private List<WidgetItem> generateWidgetItems(String packageName, int numOfWidgets) {
+        ShadowPackageManager packageManager = shadowOf(mContext.getPackageManager());
+        ArrayList<WidgetItem> widgetItems = new ArrayList<>();
+        for (int i = 0; i < numOfWidgets; i++) {
+            ComponentName cn = ComponentName.createRelative(packageName, ".SampleWidget" + i);
+            AppWidgetProviderInfo widgetInfo = new AppWidgetProviderInfo();
+            widgetInfo.provider = cn;
+            ReflectionHelpers.setField(widgetInfo, "providerInfo",
+                    packageManager.addReceiverIfNotPresent(cn));
+
+            WidgetItem widgetItem = new WidgetItem(
+                    LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, widgetInfo),
+                    mTestProfile, mIconCache);
+            widgetItems.add(widgetItem);
+        }
+        return widgetItems;
     }
 }
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchPipelineTest.java b/robolectric_tests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchPipelineTest.java
deleted file mode 100644
index 17ededd..0000000
--- a/robolectric_tests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchPipelineTest.java
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.widget.picker.search;
-
-import static android.os.Looper.getMainLooper;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.doAnswer;
-import static org.robolectric.Shadows.shadowOf;
-
-import android.appwidget.AppWidgetProviderInfo;
-import android.content.ComponentName;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.os.UserHandle;
-
-import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.icons.BitmapInfo;
-import com.android.launcher3.icons.ComponentWithLabel;
-import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.model.WidgetItem;
-import com.android.launcher3.model.data.PackageItemInfo;
-import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
-import com.android.launcher3.widget.model.WidgetsListContentEntry;
-import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
-import com.android.launcher3.widget.model.WidgetsListSearchHeaderEntry;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.shadows.ShadowPackageManager;
-import org.robolectric.util.ReflectionHelpers;
-
-import java.util.ArrayList;
-import java.util.List;
-
-@RunWith(RobolectricTestRunner.class)
-public class SimpleWidgetsSearchPipelineTest {
-    @Mock private IconCache mIconCache;
-
-    private InvariantDeviceProfile mTestProfile;
-    private WidgetsListHeaderEntry mCalendarHeaderEntry;
-    private WidgetsListContentEntry mCalendarContentEntry;
-    private WidgetsListHeaderEntry mCameraHeaderEntry;
-    private WidgetsListContentEntry mCameraContentEntry;
-    private WidgetsListHeaderEntry mClockHeaderEntry;
-    private WidgetsListContentEntry mClockContentEntry;
-    private Context mContext;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        doAnswer(invocation -> {
-            ComponentWithLabel componentWithLabel = (ComponentWithLabel) invocation.getArgument(0);
-            return componentWithLabel.getComponent().getShortClassName();
-        }).when(mIconCache).getTitleNoCache(any());
-        mTestProfile = new InvariantDeviceProfile();
-        mTestProfile.numRows = 5;
-        mTestProfile.numColumns = 5;
-        mContext = RuntimeEnvironment.application;
-
-        mCalendarHeaderEntry =
-                createWidgetsHeaderEntry("com.example.android.Calendar", "Calendar", 2);
-        mCalendarContentEntry =
-                createWidgetsContentEntry("com.example.android.Calendar", "Calendar", 2);
-        mCameraHeaderEntry = createWidgetsHeaderEntry("com.example.android.Camera", "Camera", 11);
-        mCameraContentEntry = createWidgetsContentEntry("com.example.android.Camera", "Camera", 11);
-        mClockHeaderEntry = createWidgetsHeaderEntry("com.example.android.Clock", "Clock", 3);
-        mClockContentEntry = createWidgetsContentEntry("com.example.android.Clock", "Clock", 3);
-    }
-
-    @Test
-    public void query_shouldMatchOnAppName() {
-        SimpleWidgetsSearchPipeline pipeline = new SimpleWidgetsSearchPipeline(
-                List.of(mCalendarHeaderEntry, mCalendarContentEntry, mCameraHeaderEntry,
-                        mCameraContentEntry, mClockHeaderEntry, mClockContentEntry));
-
-        pipeline.query("Ca", results ->
-                assertEquals(results,
-                        List.of(
-                                new WidgetsListSearchHeaderEntry(
-                                        mCalendarHeaderEntry.mPkgItem,
-                                        mCalendarHeaderEntry.mTitleSectionName,
-                                        mCalendarHeaderEntry.mWidgets),
-                                mCalendarContentEntry,
-                                new WidgetsListSearchHeaderEntry(
-                                        mCameraHeaderEntry.mPkgItem,
-                                        mCameraHeaderEntry.mTitleSectionName,
-                                        mCameraHeaderEntry.mWidgets),
-                                mCameraContentEntry)));
-        shadowOf(getMainLooper()).idle();
-    }
-
-    @Test
-    public void query_shouldMatchOnWidgetLabel() {
-        SimpleWidgetsSearchPipeline pipeline = new SimpleWidgetsSearchPipeline(
-                List.of(mCalendarHeaderEntry, mCalendarContentEntry, mCameraHeaderEntry,
-                        mCameraContentEntry));
-
-        pipeline.query("Widget1", results ->
-                assertEquals(results,
-                        List.of(
-                                new WidgetsListSearchHeaderEntry(
-                                        mCalendarHeaderEntry.mPkgItem,
-                                        mCalendarHeaderEntry.mTitleSectionName,
-                                        mCalendarHeaderEntry.mWidgets.subList(1, 2)),
-                                new WidgetsListContentEntry(
-                                        mCalendarHeaderEntry.mPkgItem,
-                                        mCalendarHeaderEntry.mTitleSectionName,
-                                        mCalendarHeaderEntry.mWidgets.subList(1, 2)),
-                                new WidgetsListSearchHeaderEntry(
-                                        mCameraHeaderEntry.mPkgItem,
-                                        mCameraHeaderEntry.mTitleSectionName,
-                                        mCameraHeaderEntry.mWidgets.subList(1, 3)),
-                                new WidgetsListContentEntry(
-                                        mCameraHeaderEntry.mPkgItem,
-                                        mCameraHeaderEntry.mTitleSectionName,
-                                        mCameraHeaderEntry.mWidgets.subList(1, 3)))));
-        shadowOf(getMainLooper()).idle();
-    }
-
-    private WidgetsListHeaderEntry createWidgetsHeaderEntry(String packageName, String appName,
-            int numOfWidgets) {
-        List<WidgetItem> widgetItems = generateWidgetItems(packageName, numOfWidgets);
-        PackageItemInfo pInfo = createPackageItemInfo(packageName, appName,
-                widgetItems.get(0).user);
-
-        return new WidgetsListHeaderEntry(pInfo, /* titleSectionName= */ "", widgetItems);
-    }
-
-    private WidgetsListContentEntry createWidgetsContentEntry(String packageName, String appName,
-            int numOfWidgets) {
-        List<WidgetItem> widgetItems = generateWidgetItems(packageName, numOfWidgets);
-        PackageItemInfo pInfo = createPackageItemInfo(packageName, appName,
-                widgetItems.get(0).user);
-
-        return new WidgetsListContentEntry(pInfo, /* titleSectionName= */ "", widgetItems);
-    }
-
-    private PackageItemInfo createPackageItemInfo(String packageName, String appName,
-            UserHandle userHandle) {
-        PackageItemInfo pInfo = new PackageItemInfo(packageName);
-        pInfo.title = appName;
-        pInfo.user = userHandle;
-        pInfo.bitmap = BitmapInfo.of(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8), 0);
-        return pInfo;
-    }
-
-    private List<WidgetItem> generateWidgetItems(String packageName, int numOfWidgets) {
-        ShadowPackageManager packageManager = shadowOf(mContext.getPackageManager());
-        ArrayList<WidgetItem> widgetItems = new ArrayList<>();
-        for (int i = 0; i < numOfWidgets; i++) {
-            ComponentName cn = ComponentName.createRelative(packageName, ".SampleWidget" + i);
-            AppWidgetProviderInfo widgetInfo = new AppWidgetProviderInfo();
-            widgetInfo.provider = cn;
-            ReflectionHelpers.setField(widgetInfo, "providerInfo",
-                    packageManager.addReceiverIfNotPresent(cn));
-
-            WidgetItem widgetItem = new WidgetItem(
-                    LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, widgetInfo),
-                    mTestProfile, mIconCache);
-            widgetItems.add(widgetItem);
-        }
-        return widgetItems;
-    }
-}
diff --git a/src/com/android/launcher3/SessionCommitReceiver.java b/src/com/android/launcher3/SessionCommitReceiver.java
index 1bbbb2b..5036104 100644
--- a/src/com/android/launcher3/SessionCommitReceiver.java
+++ b/src/com/android/launcher3/SessionCommitReceiver.java
@@ -24,6 +24,7 @@
 import android.content.pm.PackageManager;
 import android.os.UserHandle;
 import android.text.TextUtils;
+import android.util.Log;
 
 import androidx.annotation.WorkerThread;
 
@@ -36,6 +37,8 @@
  */
 public class SessionCommitReceiver extends BroadcastReceiver {
 
+    private static final String LOG = "SessionCommitReceiver";
+
     // Preference key for automatically adding icon to homescreen.
     public static final String ADD_ICON_PREFERENCE_KEY = "pref_add_icon_to_home";
 
@@ -68,6 +71,11 @@
             return;
         }
 
+        Log.i(LOG,
+                "Adding package name to install queue. Package name: " + info.getAppPackageName()
+                        + ", has app icon: " + (info.getAppIcon() != null)
+                        + ", has app label: " + !TextUtils.isEmpty(info.getAppLabel()));
+
         ItemInstallQueue.INSTANCE.get(context)
                 .queueItem(info.getAppPackageName(), user);
     }
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index f5fbe2b..6331ef2 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -95,6 +95,10 @@
     public static final BooleanFlag ENABLE_DEVICE_SEARCH = new DeviceFlag(
             "ENABLE_DEVICE_SEARCH", true, "Allows on device search in all apps");
 
+    public static final BooleanFlag ENABLE_PEOPLE_TILE_PREVIEW = getDebugFlag(
+            "ENABLE_PEOPLE_TILE_PREVIEW", false,
+            "Experimental: Shows conversation shortcuts on home screen as search results");
+
     public static final BooleanFlag FOLDER_NAME_SUGGEST = new DeviceFlag(
             "FOLDER_NAME_SUGGEST", true,
             "Suggests folder names instead of blank text.");
diff --git a/src/com/android/launcher3/dragndrop/AppWidgetHostViewDrawable.java b/src/com/android/launcher3/dragndrop/AppWidgetHostViewDrawable.java
index 7b32bbf..2135f5d 100644
--- a/src/com/android/launcher3/dragndrop/AppWidgetHostViewDrawable.java
+++ b/src/com/android/launcher3/dragndrop/AppWidgetHostViewDrawable.java
@@ -17,12 +17,8 @@
 
 import android.graphics.Canvas;
 import android.graphics.ColorFilter;
-import android.graphics.Outline;
 import android.graphics.Paint;
-import android.graphics.Path;
 import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.graphics.RectF;
 import android.graphics.drawable.Drawable;
 
 import com.android.launcher3.widget.LauncherAppWidgetHostView;
@@ -36,38 +32,15 @@
 
     private final LauncherAppWidgetHostView mAppWidgetHostView;
     private Paint mPaint = new Paint();
-    private final Path mClipPath;
-    private final boolean mWasAttached;
 
     public AppWidgetHostViewDrawable(LauncherAppWidgetHostView appWidgetHostView) {
         mAppWidgetHostView = appWidgetHostView;
-        mWasAttached = appWidgetHostView.isAttachedToWindow();
-        Path clipPath = null;
-        if (appWidgetHostView.getClipToOutline()) {
-            Outline outline = new Outline();
-            mAppWidgetHostView.getOutlineProvider().getOutline(mAppWidgetHostView, outline);
-            Rect rect = new Rect();
-            if (outline.getRect(rect)) {
-                float radius = outline.getRadius();
-                clipPath = new Path();
-                clipPath.addRoundRect(new RectF(rect), radius, radius, Path.Direction.CCW);
-            }
-        }
-        mClipPath = clipPath;
     }
 
     @Override
     public void draw(Canvas canvas) {
         int saveCount = canvas.saveLayer(0, 0, getIntrinsicWidth(), getIntrinsicHeight(), mPaint);
-        if (mClipPath != null) {
-            canvas.clipPath(mClipPath);
-        }
-        // If the view was never attached, or is current attached, then draw. Otherwise do not try
-        // to draw, or we might trigger bugs with items that get drawn while requiring the view to
-        // be attached.
-        if (!mWasAttached || mAppWidgetHostView.isAttachedToWindow()) {
-            mAppWidgetHostView.draw(canvas);
-        }
+        mAppWidgetHostView.draw(canvas);
         canvas.restoreToCount(saveCount);
     }
 
diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
index 56dbbd3..fd51ba8 100644
--- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
+++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
@@ -20,6 +20,7 @@
 import android.content.pm.LauncherApps;
 import android.content.pm.PackageInstaller.SessionInfo;
 import android.os.UserHandle;
+import android.util.Log;
 import android.util.LongSparseArray;
 import android.util.Pair;
 
@@ -47,6 +48,8 @@
  */
 public class AddWorkspaceItemsTask extends BaseModelUpdateTask {
 
+    private static final String LOG = "AddWorkspaceItemsTask";
+
     private final List<Pair<ItemInfo, Object>> mItemList;
 
     /**
@@ -167,6 +170,8 @@
 
                 // Save the WorkspaceItemInfo for binding in the workspace
                 addedItemsFinal.add(itemInfo);
+
+                Log.i(LOG, "Adding item info to workspace: " + itemInfo);
             }
         }
 
diff --git a/src/com/android/launcher3/model/ItemInstallQueue.java b/src/com/android/launcher3/model/ItemInstallQueue.java
index df8367f..836d804 100644
--- a/src/com/android/launcher3/model/ItemInstallQueue.java
+++ b/src/com/android/launcher3/model/ItemInstallQueue.java
@@ -63,6 +63,8 @@
  */
 public class ItemInstallQueue {
 
+    private static final String LOG = "ItemInstallQueue";
+
     public static final int FLAG_ACTIVITY_PAUSED = 1;
     public static final int FLAG_LOADER_RUNNING = 2;
     public static final int FLAG_DRAG_AND_DROP = 4;
@@ -183,7 +185,17 @@
 
     private void queuePendingShortcutInfo(PendingInstallShortcutInfo info) {
         // Queue the item up for adding if launcher has not loaded properly yet
-        MODEL_EXECUTOR.post(() -> addToQueue(info));
+        MODEL_EXECUTOR.post(() -> {
+            Pair<ItemInfo, Object> itemInfo = info.getItemInfo(mContext);
+            if (itemInfo == null) {
+                Log.i(LOG, "Adding PendingInstallShortcutInfo with no attached info to queue.");
+            } else {
+                Log.i(LOG, "Adding PendingInstallShortcutInfo to queue. Attached info: "
+                        + itemInfo.first);
+            }
+
+            addToQueue(info);
+        });
         flushInstallQueue();
     }
 
diff --git a/src/com/android/launcher3/pm/InstallSessionHelper.java b/src/com/android/launcher3/pm/InstallSessionHelper.java
index 0091af1..72f1c58 100644
--- a/src/com/android/launcher3/pm/InstallSessionHelper.java
+++ b/src/com/android/launcher3/pm/InstallSessionHelper.java
@@ -29,6 +29,7 @@
 import android.os.Process;
 import android.os.UserHandle;
 import android.text.TextUtils;
+import android.util.Log;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
@@ -56,6 +57,8 @@
  */
 public class InstallSessionHelper {
 
+    private static final String LOG = "InstallSessionHelper";
+
     // Set<String> of session ids of promise icons that have been added to the home screen
     // as FLAG_PROMISE_NEW_INSTALLS.
     protected static final String PROMISE_ICON_IDS = "promise_icon_ids";
@@ -219,6 +222,9 @@
                 && !promiseIconAddedForId(sessionInfo.getSessionId())
                 && !new PackageManagerHelper(mAppContext).isAppInstalled(
                         sessionInfo.getAppPackageName(), getUserHandle(sessionInfo))) {
+            Log.i(LOG, "Adding package name to install queue: "
+                    + sessionInfo.getAppPackageName());
+
             ItemInstallQueue.INSTANCE.get(mAppContext)
                     .queueItem(sessionInfo.getAppPackageName(), getUserHandle(sessionInfo));
 
diff --git a/src/com/android/launcher3/views/AbstractSlideInView.java b/src/com/android/launcher3/views/AbstractSlideInView.java
index e08f881..f79313f 100644
--- a/src/com/android/launcher3/views/AbstractSlideInView.java
+++ b/src/com/android/launcher3/views/AbstractSlideInView.java
@@ -216,7 +216,7 @@
         return mLauncher.getDragLayer();
     }
 
-    protected static View createColorScrim(Context context, int bgColor) {
+    protected View createColorScrim(Context context, int bgColor) {
         View view = new View(context);
         view.forceHasOverlappingRendering(false);
         view.setBackgroundColor(bgColor);
diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java
index fc63af0..95b887c 100644
--- a/src/com/android/launcher3/widget/BaseWidgetSheet.java
+++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java
@@ -19,6 +19,7 @@
 
 import android.content.Context;
 import android.graphics.Point;
+import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.View.OnClickListener;
@@ -108,7 +109,7 @@
 
         // If the ImageView doesn't have a drawable yet, the widget preview hasn't been loaded and
         // we abort the drag.
-        if (image.getDrawable() == null) {
+        if (image.getDrawable() == null && v.getAppWidgetHostViewPreview() == null) {
             return false;
         }
 
@@ -116,11 +117,21 @@
         dragHelper.setRemoteViewsPreview(v.getPreview());
         dragHelper.setAppWidgetHostViewPreview(v.getAppWidgetHostViewPreview());
 
-        int[] loc = new int[2];
-        getPopupContainer().getLocationInDragLayer(image, loc);
+        if (image.getDrawable() != null) {
+            int[] loc = new int[2];
+            getPopupContainer().getLocationInDragLayer(image, loc);
 
-        dragHelper.startDrag(image.getBitmapBounds(), image.getDrawable().getIntrinsicWidth(),
-                image.getWidth(), new Point(loc[0], loc[1]), this, new DragOptions());
+            dragHelper.startDrag(image.getBitmapBounds(), image.getDrawable().getIntrinsicWidth(),
+                    image.getWidth(), new Point(loc[0], loc[1]), this, new DragOptions());
+        } else {
+            View preview = v.getAppWidgetHostViewPreview();
+            int[] loc = new int[2];
+            getPopupContainer().getLocationInDragLayer(preview, loc);
+
+            Rect r = new Rect(0, 0, preview.getWidth(), preview.getHeight());
+            dragHelper.startDrag(r, preview.getMeasuredWidth(), preview.getMeasuredWidth(),
+                    new Point(loc[0], loc[1]), this, new DragOptions());
+        }
         close(true);
         return true;
     }
diff --git a/src/com/android/launcher3/widget/PendingItemDragHelper.java b/src/com/android/launcher3/widget/PendingItemDragHelper.java
index 3e61e56..e78d517 100644
--- a/src/com/android/launcher3/widget/PendingItemDragHelper.java
+++ b/src/com/android/launcher3/widget/PendingItemDragHelper.java
@@ -115,6 +115,7 @@
             }
             if (mAppWidgetHostViewPreview != null) {
                 preview = new AppWidgetHostViewDrawable(mAppWidgetHostViewPreview);
+                previewSizeBeforeScale[0] = mAppWidgetHostViewPreview.getMeasuredWidth();
                 launcher.getDragController()
                         .addDragListener(new AppWidgetHostViewDragListener(launcher));
             }
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index 40b256b..5e7c961 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -28,9 +28,11 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.View.OnLayoutChangeListener;
+import android.view.ViewGroup;
 import android.view.ViewPropertyAnimator;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.FrameLayout;
+import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.RemoteViews;
 import android.widget.TextView;
@@ -42,7 +44,6 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
 import com.android.launcher3.WidgetPreviewLoader;
-import com.android.launcher3.dragndrop.AppWidgetHostViewDrawable;
 import com.android.launcher3.icons.BaseIconFactory;
 import com.android.launcher3.icons.BitmapRenderer;
 import com.android.launcher3.icons.FastBitmapDrawable;
@@ -77,7 +78,9 @@
     private int mCellSize;
     private float mPreviewScale = 1f;
 
+    private FrameLayout mWidgetImageContainer;
     private WidgetImageView mWidgetImage;
+    private ImageView mWidgetBadge;
     private TextView mWidgetName;
     private TextView mWidgetDims;
     private TextView mWidgetDescription;
@@ -133,7 +136,9 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
 
+        mWidgetImageContainer = findViewById(R.id.widget_preview_container);
         mWidgetImage = findViewById(R.id.widget_preview);
+        mWidgetBadge = findViewById(R.id.widget_badge);
         mWidgetName = findViewById(R.id.widget_name);
         mWidgetDims = findViewById(R.id.widget_dims);
         mWidgetDescription = findViewById(R.id.widget_description);
@@ -155,7 +160,9 @@
             Log.d(TAG, "reset called on:" + mWidgetName.getText());
         }
         mWidgetImage.animate().cancel();
-        mWidgetImage.setDrawable(null, null);
+        mWidgetImage.setDrawable(null);
+        mWidgetImage.setVisibility(View.VISIBLE);
+        mWidgetBadge.setImageDrawable(null);
         mWidgetName.setText(null);
         mWidgetDims.setText(null);
         mWidgetDescription.setText(null);
@@ -167,6 +174,9 @@
             mActiveRequest = null;
         }
         mPreview = null;
+        if (mAppWidgetHostViewPreview != null) {
+            mWidgetImageContainer.removeView(mAppWidgetHostViewPreview);
+        }
         mAppWidgetHostViewPreview = null;
     }
 
@@ -215,6 +225,13 @@
             mAppWidgetHostViewPreview.setPadding(/* left= */ 0, /* top= */0, /* right= */
                     0, /* bottom= */ 0);
             mAppWidgetHostViewPreview.updateAppWidget(/* remoteViews= */ null);
+            // Gravity 77 = "fill"
+            FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
+                    ViewGroup.LayoutParams.MATCH_PARENT,
+                    ViewGroup.LayoutParams.MATCH_PARENT, /* gravity= */ 77);
+            mAppWidgetHostViewPreview.setLayoutParams(params);
+            mWidgetImageContainer.addView(mAppWidgetHostViewPreview, /* index= */ 0);
+            mWidgetImage.setVisibility(View.GONE);
         }
     }
 
@@ -258,21 +275,36 @@
             return;
         }
         if (drawable != null) {
-            LayoutParams layoutParams = (LayoutParams) mWidgetImage.getLayoutParams();
-            layoutParams.width = (int) (drawable.getIntrinsicWidth() * mPreviewScale);
-            layoutParams.height = (int) (drawable.getIntrinsicHeight() * mPreviewScale);
-            mWidgetImage.setLayoutParams(layoutParams);
-
-            mWidgetImage.setDrawable(drawable, mWidgetPreviewLoader.getBadgeForUser(mItem.user,
-                    BaseIconFactory.getBadgeSizeForIconSize(mDeviceProfile.allAppsIconSizePx)));
-            if (mAnimatePreview) {
-                mWidgetImage.setAlpha(0f);
-                ViewPropertyAnimator anim = mWidgetImage.animate();
-                anim.alpha(1.0f).setDuration(FADE_IN_DURATION_MS);
-            } else {
-                mWidgetImage.setAlpha(1f);
+            setContainerSize(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
+            mWidgetImage.setDrawable(drawable);
+            mWidgetImage.setVisibility(View.VISIBLE);
+            if (mAppWidgetHostViewPreview != null) {
+                removeView(mAppWidgetHostViewPreview);
+                mAppWidgetHostViewPreview = null;
             }
         }
+        Drawable badge = mWidgetPreviewLoader.getBadgeForUser(mItem.user,
+                BaseIconFactory.getBadgeSizeForIconSize(mDeviceProfile.allAppsIconSizePx));
+        if (badge == null) {
+            mWidgetBadge.setVisibility(View.GONE);
+        } else {
+            mWidgetBadge.setVisibility(View.VISIBLE);
+            mWidgetBadge.setImageDrawable(badge);
+        }
+        if (mAnimatePreview) {
+            mWidgetImageContainer.setAlpha(0f);
+            ViewPropertyAnimator anim = mWidgetImageContainer.animate();
+            anim.alpha(1.0f).setDuration(FADE_IN_DURATION_MS);
+        } else {
+            mWidgetImageContainer.setAlpha(1f);
+        }
+    }
+
+    private void setContainerSize(int width, int height) {
+        LayoutParams layoutParams = (LayoutParams) mWidgetImageContainer.getLayoutParams();
+        layoutParams.width = (int) (width * mPreviewScale);
+        layoutParams.height = (int) (height * mPreviewScale);
+        mWidgetImageContainer.setLayoutParams(layoutParams);
     }
 
     public void ensurePreview() {
@@ -290,15 +322,8 @@
             int viewWidth = dp.cellWidthPx * mItem.spanX;
             int viewHeight = dp.cellHeightPx * mItem.spanY;
 
-            mAppWidgetHostViewPreview.measure(
-                    MeasureSpec.makeMeasureSpec(viewWidth, MeasureSpec.EXACTLY),
-                    MeasureSpec.makeMeasureSpec(viewHeight, MeasureSpec.EXACTLY));
-
-            viewWidth = mAppWidgetHostViewPreview.getMeasuredWidth();
-            viewHeight = mAppWidgetHostViewPreview.getMeasuredHeight();
-            mAppWidgetHostViewPreview.layout(0, 0, viewWidth, viewHeight);
-            Drawable drawable = new AppWidgetHostViewDrawable(mAppWidgetHostViewPreview);
-            applyPreview(drawable);
+            setContainerSize(viewWidth, viewHeight);
+            applyPreview((Drawable) null);
             return;
         }
         if (mActiveRequest != null) {
diff --git a/src/com/android/launcher3/widget/WidgetCellPreview.java b/src/com/android/launcher3/widget/WidgetCellPreview.java
new file mode 100644
index 0000000..ad3a61a
--- /dev/null
+++ b/src/com/android/launcher3/widget/WidgetCellPreview.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.widget;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.widget.FrameLayout;
+
+/**
+ * View group managing the widget preview: either using a {@link WidgetImageView} or an actual
+ * {@link LauncherAppWidgetHostView}.
+ */
+public class WidgetCellPreview extends FrameLayout {
+    public WidgetCellPreview(Context context) {
+        this(context, null);
+    }
+
+    public WidgetCellPreview(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public WidgetCellPreview(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        super.onInterceptTouchEvent(ev);
+        return true;
+    }
+
+}
diff --git a/src/com/android/launcher3/widget/WidgetImageView.java b/src/com/android/launcher3/widget/WidgetImageView.java
index 39d701c..11f4485 100644
--- a/src/com/android/launcher3/widget/WidgetImageView.java
+++ b/src/com/android/launcher3/widget/WidgetImageView.java
@@ -25,7 +25,6 @@
 import android.view.View;
 
 import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
 
 /**
  * View that draws a bitmap horizontally centered. If the image width is greater than the view
@@ -37,7 +36,6 @@
     private final int mBadgeMargin;
 
     private Drawable mDrawable;
-    private Drawable mBadge;
 
     public WidgetImageView(Context context) {
         this(context, null);
@@ -54,9 +52,9 @@
                 .getDimensionPixelSize(R.dimen.profile_badge_margin);
     }
 
-    public void setDrawable(Drawable drawable, Drawable badge) {
+    /** Set the drawable to use for this view. */
+    public void setDrawable(Drawable drawable) {
         mDrawable = drawable;
-        mBadge = badge;
         invalidate();
     }
 
@@ -70,11 +68,6 @@
             updateDstRectF();
             mDrawable.setBounds(getBitmapBounds());
             mDrawable.draw(canvas);
-
-            // Only draw the badge if a preview was drawn.
-            if (mBadge != null) {
-                mBadge.draw(canvas);
-            }
         }
     }
 
@@ -105,17 +98,6 @@
             mDstRectF.top = (myHeight - scaledHeight) / 2;
             mDstRectF.bottom = (myHeight + scaledHeight) / 2;
         }
-
-        if (mBadge != null) {
-            Rect bounds = mBadge.getBounds();
-            int left = Utilities.boundToRange(
-                    (int) (mDstRectF.right + mBadgeMargin - bounds.width()),
-                    mBadgeMargin, getWidth() - bounds.width());
-            int top = Utilities.boundToRange(
-                    (int) (mDstRectF.bottom + mBadgeMargin - bounds.height()),
-                    mBadgeMargin, getHeight() - bounds.height());
-            mBadge.setBounds(left, top, bounds.width() + left, bounds.height() + top);
-        }
     }
 
     /**
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index d13884a..a4257a2 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -169,7 +169,7 @@
         onWidgetsBound();
 
         mSearchAndRecommendationViewHolder.mSearchBar.initialize(
-                mLauncher.getPopupDataProvider().getAllWidgets(), /* searchModeListener= */ this);
+                mLauncher.getPopupDataProvider(), /* searchModeListener= */ this);
     }
 
     @Override
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
index c1d64b1..1524ab3 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
@@ -31,7 +31,6 @@
 import com.android.launcher3.model.WidgetItem;
 import com.android.launcher3.recyclerview.ViewHolderBinder;
 import com.android.launcher3.widget.WidgetCell;
-import com.android.launcher3.widget.WidgetImageView;
 import com.android.launcher3.widget.model.WidgetsListContentEntry;
 import com.android.launcher3.widget.util.WidgetsTableUtils;
 
@@ -170,7 +169,7 @@
                     WidgetCell widget = (WidgetCell) mLayoutInflater.inflate(
                             R.layout.widget_cell, tableRow, false);
                     // set up touch.
-                    WidgetImageView preview = widget.findViewById(R.id.widget_preview);
+                    View preview = widget.findViewById(R.id.widget_preview_container);
                     preview.setOnClickListener(mIconClickListener);
                     preview.setOnLongClickListener(mIconLongClickListener);
                     tableRow.addView(widget);
diff --git a/src/com/android/launcher3/widget/picker/search/LauncherWidgetsSearchBar.java b/src/com/android/launcher3/widget/picker/search/LauncherWidgetsSearchBar.java
index 56a08b1..42f1bb2 100644
--- a/src/com/android/launcher3/widget/picker/search/LauncherWidgetsSearchBar.java
+++ b/src/com/android/launcher3/widget/picker/search/LauncherWidgetsSearchBar.java
@@ -26,10 +26,7 @@
 
 import com.android.launcher3.ExtendedEditText;
 import com.android.launcher3.R;
-import com.android.launcher3.search.SearchAlgorithm;
-import com.android.launcher3.widget.model.WidgetsListBaseEntry;
-
-import java.util.List;
+import com.android.launcher3.popup.PopupDataProvider;
 
 /**
  * View for a search bar with an edit text with a cancel button.
@@ -54,12 +51,10 @@
     }
 
     @Override
-    public void initialize(List<WidgetsListBaseEntry> allWidgets,
-            SearchModeListener searchModeListener) {
-        SearchAlgorithm<WidgetsListBaseEntry> algo =
-                new SimpleWidgetsSearchAlgorithm(new SimpleWidgetsSearchPipeline(allWidgets));
+    public void initialize(PopupDataProvider dataProvider, SearchModeListener searchModeListener) {
         mController = new WidgetsSearchBarController(
-                algo, mEditText, mCancelButton, searchModeListener);
+                new SimpleWidgetsSearchAlgorithm(dataProvider),
+                mEditText, mCancelButton, searchModeListener);
     }
 
     @Override
diff --git a/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithm.java b/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithm.java
index 15d2454..9be3b5f 100644
--- a/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithm.java
+++ b/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithm.java
@@ -16,42 +16,41 @@
 
 package com.android.launcher3.widget.picker.search;
 
-import android.os.Handler;
-import android.util.Log;
+import static com.android.launcher3.search.StringMatcherUtility.matches;
 
+import android.os.Handler;
+
+import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.popup.PopupDataProvider;
 import com.android.launcher3.search.SearchAlgorithm;
 import com.android.launcher3.search.SearchCallback;
+import com.android.launcher3.search.StringMatcherUtility.StringMatcher;
 import com.android.launcher3.widget.model.WidgetsListBaseEntry;
+import com.android.launcher3.widget.model.WidgetsListContentEntry;
+import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
+import com.android.launcher3.widget.model.WidgetsListSearchHeaderEntry;
 
 import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
 
 /**
  * Implementation of {@link SearchAlgorithm} that posts a task to query on the main thread.
  */
 public final class SimpleWidgetsSearchAlgorithm implements SearchAlgorithm<WidgetsListBaseEntry> {
 
-    private static final boolean DEBUG = false;
-    private static final String TAG = "SimpleWidgetsSearchAlgo";
-    private static final String DELIM = "\t";
-
     private final Handler mResultHandler;
-    private final WidgetsPickerSearchPipeline mSearchPipeline;
+    private final PopupDataProvider mDataProvider;
 
-    public SimpleWidgetsSearchAlgorithm(WidgetsPickerSearchPipeline searchPipeline) {
+    public SimpleWidgetsSearchAlgorithm(PopupDataProvider dataProvider) {
         mResultHandler = new Handler();
-        mSearchPipeline = searchPipeline;
+        mDataProvider = dataProvider;
     }
 
     @Override
     public void doSearch(String query, SearchCallback<WidgetsListBaseEntry> callback) {
-        long startTime = System.currentTimeMillis();
-        String queryToken = query + DELIM + startTime;
-        if (DEBUG) {
-            Log.d(TAG, "doSearch queryToken:" + queryToken);
-        }
-        mSearchPipeline.query(query,
-                results -> mResultHandler.post(
-                        () -> callback.onSearchResult(queryToken, new ArrayList(results))));
+        ArrayList<WidgetsListBaseEntry> result = getFilteredWidgets(mDataProvider, query);
+        mResultHandler.post(() -> callback.onSearchResult(query, result));
     }
 
     @Override
@@ -60,4 +59,36 @@
             mResultHandler.removeCallbacksAndMessages(/*token= */null);
         }
     }
+
+    /**
+     * Returns entries for all matched widgets
+     */
+    public static ArrayList<WidgetsListBaseEntry> getFilteredWidgets(
+            PopupDataProvider dataProvider, String input) {
+        ArrayList<WidgetsListBaseEntry> results = new ArrayList<>();
+        dataProvider.getAllWidgets().stream()
+                .filter(entry -> entry instanceof WidgetsListHeaderEntry)
+                .forEach(headerEntry -> {
+                    List<WidgetItem> matchedWidgetItems = filterWidgetItems(
+                            input, headerEntry.mPkgItem.title.toString(), headerEntry.mWidgets);
+                    if (matchedWidgetItems.size() > 0) {
+                        results.add(new WidgetsListSearchHeaderEntry(headerEntry.mPkgItem,
+                                headerEntry.mTitleSectionName, matchedWidgetItems));
+                        results.add(new WidgetsListContentEntry(headerEntry.mPkgItem,
+                                headerEntry.mTitleSectionName, matchedWidgetItems));
+                    }
+                });
+        return results;
+    }
+
+    private static List<WidgetItem> filterWidgetItems(String query, String packageTitle,
+            List<WidgetItem> items) {
+        StringMatcher matcher = StringMatcher.getInstance();
+        if (matches(query, packageTitle, matcher)) {
+            return items;
+        }
+        return items.stream()
+                .filter(item -> matches(query, item.label, matcher))
+                .collect(Collectors.toList());
+    }
 }
diff --git a/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchPipeline.java b/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchPipeline.java
deleted file mode 100644
index 5222e8e..0000000
--- a/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchPipeline.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.widget.picker.search;
-
-import static com.android.launcher3.search.StringMatcherUtility.matches;
-
-import com.android.launcher3.model.WidgetItem;
-import com.android.launcher3.search.StringMatcherUtility.StringMatcher;
-import com.android.launcher3.widget.model.WidgetsListBaseEntry;
-import com.android.launcher3.widget.model.WidgetsListContentEntry;
-import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
-import com.android.launcher3.widget.model.WidgetsListSearchHeaderEntry;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.function.Consumer;
-import java.util.stream.Collectors;
-
-/**
- * Implementation of {@link WidgetsPickerSearchPipeline} that performs search by prefix matching on
- * app names and widget labels.
- */
-public final class SimpleWidgetsSearchPipeline implements WidgetsPickerSearchPipeline {
-
-    private final List<WidgetsListBaseEntry> mAllEntries;
-
-    public SimpleWidgetsSearchPipeline(List<WidgetsListBaseEntry> allEntries) {
-        mAllEntries = allEntries;
-    }
-
-    @Override
-    public void query(String input, Consumer<List<WidgetsListBaseEntry>> callback) {
-        ArrayList<WidgetsListBaseEntry> results = new ArrayList<>();
-        mAllEntries.stream().filter(entry -> entry instanceof WidgetsListHeaderEntry)
-                .forEach(headerEntry -> {
-                    List<WidgetItem> matchedWidgetItems = filterWidgetItems(
-                            input, headerEntry.mPkgItem.title.toString(), headerEntry.mWidgets);
-                    if (matchedWidgetItems.size() > 0) {
-                        results.add(new WidgetsListSearchHeaderEntry(headerEntry.mPkgItem,
-                                headerEntry.mTitleSectionName, matchedWidgetItems));
-                        results.add(new WidgetsListContentEntry(headerEntry.mPkgItem,
-                                headerEntry.mTitleSectionName, matchedWidgetItems));
-                    }
-                });
-        callback.accept(results);
-    }
-
-    private List<WidgetItem> filterWidgetItems(String query, String packageTitle,
-            List<WidgetItem> items) {
-        StringMatcher matcher = StringMatcher.getInstance();
-        if (matches(query, packageTitle, matcher)) {
-            return items;
-        }
-        return items.stream()
-                .filter(item -> matches(query, item.label, matcher))
-                .collect(Collectors.toList());
-    }
-}
diff --git a/src/com/android/launcher3/widget/picker/search/WidgetsPickerSearchPipeline.java b/src/com/android/launcher3/widget/picker/search/WidgetsPickerSearchPipeline.java
deleted file mode 100644
index d12782c..0000000
--- a/src/com/android/launcher3/widget/picker/search/WidgetsPickerSearchPipeline.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.widget.picker.search;
-
-import com.android.launcher3.widget.model.WidgetsListBaseEntry;
-
-import java.util.List;
-import java.util.function.Consumer;
-
-/**
- * An interface for a pipeline to handle widgets search.
- */
-public interface WidgetsPickerSearchPipeline {
-
-    /**
-     * Performs a search query asynchronically. Invokes {@code callback} when the search is
-     * complete.
-     */
-    void query(String input, Consumer<List<WidgetsListBaseEntry>> callback);
-
-    /**
-     * Cancels any ongoing search request.
-     */
-    default void cancel() {};
-
-    /**
-     * Cleans up after search is no longer needed.
-     */
-    default void destroy() {};
-}
diff --git a/src/com/android/launcher3/widget/picker/search/WidgetsSearchBar.java b/src/com/android/launcher3/widget/picker/search/WidgetsSearchBar.java
index 3ac82c0..0ac47ce 100644
--- a/src/com/android/launcher3/widget/picker/search/WidgetsSearchBar.java
+++ b/src/com/android/launcher3/widget/picker/search/WidgetsSearchBar.java
@@ -16,9 +16,7 @@
 
 package com.android.launcher3.widget.picker.search;
 
-import com.android.launcher3.widget.model.WidgetsListBaseEntry;
-
-import java.util.List;
+import com.android.launcher3.popup.PopupDataProvider;
 
 /**
  * Interface for a widgets picker search bar.
@@ -27,7 +25,7 @@
     /**
      * Attaches a controller to the search bar which interacts with {@code searchModeListener}.
      */
-    void initialize(List<WidgetsListBaseEntry> allWidgets, SearchModeListener searchModeListener);
+    void initialize(PopupDataProvider dataProvider, SearchModeListener searchModeListener);
 
     /**
      * Clears search bar.