Merge "Import translations. DO NOT MERGE ANYWHERE" into tm-qpr-dev
diff --git a/quickstep/res/layout/task_desktop.xml b/quickstep/res/layout/task_desktop.xml
index 2a9674f..96aabb4 100644
--- a/quickstep/res/layout/task_desktop.xml
+++ b/quickstep/res/layout/task_desktop.xml
@@ -24,6 +24,11 @@
     android:defaultFocusHighlightEnabled="false"
     android:focusable="true">
 
+    <View
+        android:id="@+id/background"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
     <!--
          TODO(b249371338): DesktopTaskView extends from TaskView. TaskView expects TaskThumbnailView
          and IconView with these ids to be present. Need to refactor RecentsView to accept child
@@ -38,10 +43,9 @@
 
     <com.android.quickstep.views.IconView
         android:id="@+id/icon"
-        android:layout_width="0dp"
-        android:layout_height="0dp"
+        android:layout_width="@dimen/task_thumbnail_icon_size"
+        android:layout_height="@dimen/task_thumbnail_icon_size"
         android:focusable="false"
-        android:importantForAccessibility="no"
-        android:visibility="gone" />
+        android:importantForAccessibility="no" />
 
 </com.android.quickstep.views.DesktopTaskView>
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index b880a7e..347c492 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -213,9 +213,9 @@
     private static final int WIDGET_CROSSFADE_DURATION_MILLIS = 125;
 
     protected final QuickstepLauncher mLauncher;
-    private final DragLayer mDragLayer;
+    protected final DragLayer mDragLayer;
 
-    final Handler mHandler;
+    protected final Handler mHandler;
 
     private final float mClosingWindowTransY;
     private final float mMaxShadowRadius;
@@ -1097,32 +1097,38 @@
             return;
         }
         if (hasControlRemoteAppTransitionPermission()) {
-            mWallpaperOpenRunner = createWallpaperOpenRunner(false /* fromUnlock */);
-
             RemoteAnimationDefinition definition = new RemoteAnimationDefinition();
-            definition.addRemoteAnimation(WindowManager.TRANSIT_OLD_WALLPAPER_OPEN,
-                    WindowConfiguration.ACTIVITY_TYPE_STANDARD,
-                    new RemoteAnimationAdapter(
-                            new LauncherAnimationRunner(mHandler, mWallpaperOpenRunner,
-                                    false /* startAtFrontOfQueue */),
-                            CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */));
-
-            if (KEYGUARD_ANIMATION.get()) {
-                mKeyguardGoingAwayRunner = createWallpaperOpenRunner(true /* fromUnlock */);
-                definition.addRemoteAnimation(
-                        WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER,
-                        new RemoteAnimationAdapter(
-                                new LauncherAnimationRunner(
-                                        mHandler, mKeyguardGoingAwayRunner,
-                                        true /* startAtFrontOfQueue */),
-                                CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */));
-            }
-
+            addRemoteAnimations(definition);
             mLauncher.registerRemoteAnimations(definition);
         }
     }
 
     /**
+     * Adds remote animations to a {@link RemoteAnimationDefinition}. May be overridden to add
+     * additional animations.
+     */
+    protected void addRemoteAnimations(RemoteAnimationDefinition definition) {
+        mWallpaperOpenRunner = createWallpaperOpenRunner(false /* fromUnlock */);
+        definition.addRemoteAnimation(WindowManager.TRANSIT_OLD_WALLPAPER_OPEN,
+                WindowConfiguration.ACTIVITY_TYPE_STANDARD,
+                new RemoteAnimationAdapter(
+                        new LauncherAnimationRunner(mHandler, mWallpaperOpenRunner,
+                                false /* startAtFrontOfQueue */),
+                        CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */));
+
+        if (KEYGUARD_ANIMATION.get()) {
+            mKeyguardGoingAwayRunner = createWallpaperOpenRunner(true /* fromUnlock */);
+            definition.addRemoteAnimation(
+                    WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER,
+                    new RemoteAnimationAdapter(
+                            new LauncherAnimationRunner(
+                                    mHandler, mKeyguardGoingAwayRunner,
+                                    true /* startAtFrontOfQueue */),
+                            CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */));
+        }
+    }
+
+    /**
      * Registers remote animations used when closing apps to home screen.
      */
     public void registerRemoteTransitions() {
@@ -1163,7 +1169,7 @@
         SystemUiProxy.INSTANCE.get(mLauncher).setStartingWindowListener(null);
     }
 
-    private void unregisterRemoteAnimations() {
+    protected void unregisterRemoteAnimations() {
         if (SEPARATE_RECENTS_ACTIVITY.get()) {
             return;
         }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
index bf0f8f7..25207d4 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
@@ -124,7 +124,7 @@
         int shadowSize = context.getResources().getDimensionPixelSize(
                 R.dimen.blur_size_thin_outline);
         mShadowFilter = new BlurMaskFilter(shadowSize, BlurMaskFilter.Blur.OUTER);
-        mShapePath = GraphicsUtils.getShapePath(mNormalizedIconSize);
+        mShapePath = GraphicsUtils.getShapePath(context, mNormalizedIconSize);
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 124f15e..2cbb899 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -23,6 +23,7 @@
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SEARCH_ACTION;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
 import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.NORMAL;
@@ -238,7 +239,7 @@
         mActionsView.updateDimension(getDeviceProfile(), overviewPanel.getLastComputedTaskSize());
         mActionsView.updateVerticalMargin(DisplayController.getNavigationMode(this));
 
-        mAppTransitionManager = new QuickstepTransitionManager(this);
+        mAppTransitionManager = buildAppTransitionManager();
         mAppTransitionManager.registerRemoteAnimations();
         mAppTransitionManager.registerRemoteTransitions();
 
@@ -307,6 +308,13 @@
         return mHotseatPredictionController;
     }
 
+    /**
+     * Builds the {@link QuickstepTransitionManager} instance to use for managing transitions.
+     */
+    protected QuickstepTransitionManager buildAppTransitionManager() {
+        return new QuickstepTransitionManager(this);
+    }
+
     @Override
     protected QuickstepOnboardingPrefs createOnboardingPrefs(SharedPreferences sharedPrefs) {
         return new QuickstepOnboardingPrefs(this, sharedPrefs);
@@ -997,7 +1005,12 @@
             activityOptions.options.setSourceInfo(ActivityOptions.SourceInfo.TYPE_LAUNCHER,
                     mLastTouchUpTime);
         }
-        activityOptions.options.setSplashScreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_ICON);
+        if (item != null && item.itemType == ITEM_TYPE_SEARCH_ACTION) {
+            activityOptions.options.setSplashScreenStyle(
+                    SplashScreen.SPLASH_SCREEN_STYLE_SOLID_COLOR);
+        } else {
+            activityOptions.options.setSplashScreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_ICON);
+        }
         activityOptions.options.setLaunchDisplayId(
                 (v != null && v.getDisplay() != null) ? v.getDisplay().getDisplayId()
                         : Display.DEFAULT_DISPLAY);
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 4f10dde..3f6eb94 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -50,6 +50,7 @@
 import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
 import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.CANCEL_RECENTS_ANIMATION;
 import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.EXPECTING_TASK_APPEARED;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.LAUNCHER_DESTROYED;
 import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.ON_SETTLED_ON_END_TARGET;
 import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
@@ -181,6 +182,7 @@
                     if (mActivity != activity) {
                         return;
                     }
+                    ActiveGestureLog.INSTANCE.addLog("Launcher destroyed", LAUNCHER_DESTROYED);
                     mRecentsView = null;
                     mActivity = null;
                 }
diff --git a/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java b/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java
index 60065fb..2964868 100644
--- a/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java
+++ b/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java
@@ -20,7 +20,6 @@
 import androidx.annotation.NonNull;
 
 import java.io.PrintWriter;
-import java.util.List;
 import java.util.Set;
 
 /**
@@ -37,7 +36,7 @@
         ON_SETTLED_ON_END_TARGET, START_RECENTS_ANIMATION, FINISH_RECENTS_ANIMATION,
         CANCEL_RECENTS_ANIMATION, SET_ON_PAGE_TRANSITION_END_CALLBACK, CANCEL_CURRENT_ANIMATION,
         CLEANUP_SCREENSHOT, SCROLLER_ANIMATION_ABORTED, TASK_APPEARED, EXPECTING_TASK_APPEARED,
-        FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER,
+        FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER, LAUNCHER_DESTROYED,
 
         /**
          * These GestureEvents are specifically associated to state flags that get set in
@@ -68,288 +67,288 @@
     protected static void analyseAndDump(
             @NonNull String prefix,
             @NonNull PrintWriter writer,
-            List<ActiveGestureLog.EventLog> eventLogs) {
-        writer.println(prefix + "ActiveGestureErrorDetector:");
-        for (int i = 0; i < eventLogs.size(); i++) {
-            ActiveGestureLog.EventLog eventLog = eventLogs.get(i);
-            if (eventLog == null) {
+            @NonNull ActiveGestureLog.EventLog eventLog) {
+        writer.println(prefix + "Error messages for gesture ID: " + eventLog.logId);
+
+        boolean errorDetected = false;
+        // Use a Set since the order is inherently checked in the loop.
+        final Set<GestureEvent> encounteredEvents = new ArraySet<>();
+        // Set flags and check order of operations.
+        for (ActiveGestureLog.EventEntry eventEntry : eventLog.eventEntries) {
+            GestureEvent gestureEvent = eventEntry.getGestureEvent();
+            if (gestureEvent == null) {
                 continue;
             }
-            int gestureId = eventLog.logId;
-            writer.println(prefix + "\tError messages for gesture ID: " + gestureId);
+            encounteredEvents.add(gestureEvent);
 
-            boolean errorDetected = false;
-            // Use a Set since the order is inherently checked in the loop.
-            final Set<GestureEvent> encounteredEvents = new ArraySet<>();
-            // Set flags and check order of operations.
-            for (ActiveGestureLog.EventEntry eventEntry : eventLog.eventEntries) {
-                GestureEvent gestureEvent = eventEntry.getGestureEvent();
-                if (gestureEvent == null) {
-                    continue;
-                }
-                encounteredEvents.add(gestureEvent);
-                switch (gestureEvent) {
-                    case MOTION_UP:
-                        errorDetected |= printErrorIfTrue(
-                                !encounteredEvents.contains(GestureEvent.MOTION_DOWN),
-                                prefix,
-                                /* errorMessage= */ "Motion up detected before/without"
-                                        + " motion down.",
-                                writer);
-                        break;
-                    case ON_SETTLED_ON_END_TARGET:
-                        errorDetected |= printErrorIfTrue(
-                                !encounteredEvents.contains(GestureEvent.SET_END_TARGET),
-                                prefix,
-                                /* errorMessage= */ "onSettledOnEndTarget called "
-                                        + "before/without setEndTarget.",
-                                writer);
-                        break;
-                    case FINISH_RECENTS_ANIMATION:
-                        errorDetected |= printErrorIfTrue(
-                                !encounteredEvents.contains(GestureEvent.START_RECENTS_ANIMATION),
-                                prefix,
-                                /* errorMessage= */ "finishRecentsAnimation called "
-                                        + "before/without startRecentsAnimation.",
-                                writer);
-                        break;
-                    case CANCEL_RECENTS_ANIMATION:
-                        errorDetected |= printErrorIfTrue(
-                                !encounteredEvents.contains(GestureEvent.START_RECENTS_ANIMATION),
-                                prefix,
-                                /* errorMessage= */ "cancelRecentsAnimation called "
-                                        + "before/without startRecentsAnimation.",
-                                writer);
-                        break;
-                    case CLEANUP_SCREENSHOT:
-                        errorDetected |= printErrorIfTrue(
-                                !encounteredEvents.contains(GestureEvent.STATE_SCREENSHOT_CAPTURED),
-                                prefix,
-                                /* errorMessage= */ "recents activity screenshot was "
-                                        + "cleaned up before/without STATE_SCREENSHOT_CAPTURED "
-                                        + "being set.",
-                                writer);
-                        break;
-                    case SCROLLER_ANIMATION_ABORTED:
-                        errorDetected |= printErrorIfTrue(
-                                encounteredEvents.contains(GestureEvent.SET_END_TARGET_HOME)
-                                        && !encounteredEvents.contains(
-                                                GestureEvent.ON_SETTLED_ON_END_TARGET),
-                                prefix,
-                                /* errorMessage= */ "recents view scroller animation "
-                                        + "aborted after setting end target HOME, but before"
-                                        + " settling on end target.",
-                                writer);
-                        break;
-                    case TASK_APPEARED:
-                        errorDetected |= printErrorIfTrue(
-                                !encounteredEvents.contains(GestureEvent.SET_END_TARGET_NEW_TASK),
-                                prefix,
-                                /* errorMessage= */ "onTasksAppeared called "
-                                        + "before/without setting end target to new task",
-                                writer);
-                        errorDetected |= printErrorIfTrue(
-                                !encounteredEvents.contains(GestureEvent.EXPECTING_TASK_APPEARED),
-                                prefix,
-                                /* errorMessage= */ "onTasksAppeared was not expected to be called",
-                                writer);
-                        break;
-                    case EXPECTING_TASK_APPEARED:
-                        errorDetected |= printErrorIfTrue(
-                                !encounteredEvents.contains(GestureEvent.SET_END_TARGET_NEW_TASK),
-                                prefix,
-                                /* errorMessage= */ "expecting onTasksAppeared to be called "
-                                        + "before/without setting end target to new task",
-                                writer);
-                        break;
-                    case STATE_GESTURE_COMPLETED:
-                        errorDetected |= printErrorIfTrue(
-                                !encounteredEvents.contains(GestureEvent.MOTION_UP),
-                                prefix,
-                                /* errorMessage= */ "STATE_GESTURE_COMPLETED set "
-                                        + "before/without motion up.",
-                                writer);
-                        errorDetected |= printErrorIfTrue(
-                                !encounteredEvents.contains(GestureEvent.STATE_GESTURE_STARTED),
-                                prefix,
-                                /* errorMessage= */ "STATE_GESTURE_COMPLETED set "
-                                        + "before/without STATE_GESTURE_STARTED.",
-                                writer);
-                        break;
-                    case STATE_GESTURE_CANCELLED:
-                        errorDetected |= printErrorIfTrue(
-                                !encounteredEvents.contains(GestureEvent.MOTION_UP),
-                                prefix,
-                                /* errorMessage= */ "STATE_GESTURE_CANCELLED set "
-                                        + "before/without motion up.",
-                                writer);
-                        errorDetected |= printErrorIfTrue(
-                                !encounteredEvents.contains(GestureEvent.STATE_GESTURE_STARTED),
-                                prefix,
-                                /* errorMessage= */ "STATE_GESTURE_CANCELLED set "
-                                        + "before/without STATE_GESTURE_STARTED.",
-                                writer);
-                        break;
-                    case STATE_SCREENSHOT_CAPTURED:
-                        errorDetected |= printErrorIfTrue(
-                                !encounteredEvents.contains(GestureEvent.STATE_CAPTURE_SCREENSHOT),
-                                prefix,
-                                /* errorMessage= */ "STATE_SCREENSHOT_CAPTURED set "
-                                        + "before/without STATE_CAPTURE_SCREENSHOT.",
-                                writer);
-                        break;
-                    case STATE_RECENTS_SCROLLING_FINISHED:
-                        errorDetected |= printErrorIfTrue(
-                                !encounteredEvents.contains(
-                                        GestureEvent.SET_ON_PAGE_TRANSITION_END_CALLBACK),
-                                prefix,
-                                /* errorMessage= */ "STATE_RECENTS_SCROLLING_FINISHED "
-                                        + "set before/without calling "
-                                        + "setOnPageTransitionEndCallback.",
-                                writer);
-                        break;
-                    case STATE_RECENTS_ANIMATION_CANCELED:
-                        errorDetected |= printErrorIfTrue(
-                                !encounteredEvents.contains(
-                                        GestureEvent.START_RECENTS_ANIMATION),
-                                prefix,
-                                /* errorMessage= */ "STATE_RECENTS_ANIMATION_CANCELED "
-                                        + "set before/without startRecentsAnimation.",
-                                writer);
-                        break;
-                    case MOTION_DOWN:
-                    case SET_END_TARGET:
-                    case SET_END_TARGET_HOME:
-                    case SET_END_TARGET_NEW_TASK:
-                    case START_RECENTS_ANIMATION:
-                    case SET_ON_PAGE_TRANSITION_END_CALLBACK:
-                    case CANCEL_CURRENT_ANIMATION:
-                    case FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER:
-                    case STATE_GESTURE_STARTED:
-                    case STATE_END_TARGET_ANIMATION_FINISHED:
-                    case STATE_CAPTURE_SCREENSHOT:
-                    case STATE_HANDLER_INVALIDATED:
-                    case STATE_LAUNCHER_DRAWN:
-                    default:
-                        // No-Op
-                }
+            switch (gestureEvent) {
+                case MOTION_UP:
+                    errorDetected |= printErrorIfTrue(
+                            !encounteredEvents.contains(GestureEvent.MOTION_DOWN),
+                            prefix,
+                            /* errorMessage= */ "Motion up detected before/without"
+                                    + " motion down.",
+                            writer);
+                    break;
+                case ON_SETTLED_ON_END_TARGET:
+                    errorDetected |= printErrorIfTrue(
+                            !encounteredEvents.contains(GestureEvent.SET_END_TARGET),
+                            prefix,
+                            /* errorMessage= */ "onSettledOnEndTarget called "
+                                    + "before/without setEndTarget.",
+                            writer);
+                    break;
+                case FINISH_RECENTS_ANIMATION:
+                    errorDetected |= printErrorIfTrue(
+                            !encounteredEvents.contains(GestureEvent.START_RECENTS_ANIMATION),
+                            prefix,
+                            /* errorMessage= */ "finishRecentsAnimation called "
+                                    + "before/without startRecentsAnimation.",
+                            writer);
+                    break;
+                case CANCEL_RECENTS_ANIMATION:
+                    errorDetected |= printErrorIfTrue(
+                            !encounteredEvents.contains(GestureEvent.START_RECENTS_ANIMATION),
+                            prefix,
+                            /* errorMessage= */ "cancelRecentsAnimation called "
+                                    + "before/without startRecentsAnimation.",
+                            writer);
+                    break;
+                case CLEANUP_SCREENSHOT:
+                    errorDetected |= printErrorIfTrue(
+                            !encounteredEvents.contains(GestureEvent.STATE_SCREENSHOT_CAPTURED),
+                            prefix,
+                            /* errorMessage= */ "recents activity screenshot was "
+                                    + "cleaned up before/without STATE_SCREENSHOT_CAPTURED "
+                                    + "being set.",
+                            writer);
+                    break;
+                case SCROLLER_ANIMATION_ABORTED:
+                    errorDetected |= printErrorIfTrue(
+                            encounteredEvents.contains(GestureEvent.SET_END_TARGET_HOME)
+                                    && !encounteredEvents.contains(
+                                            GestureEvent.ON_SETTLED_ON_END_TARGET),
+                            prefix,
+                            /* errorMessage= */ "recents view scroller animation "
+                                    + "aborted after setting end target HOME, but before"
+                                    + " settling on end target.",
+                            writer);
+                    break;
+                case TASK_APPEARED:
+                    errorDetected |= printErrorIfTrue(
+                            !encounteredEvents.contains(GestureEvent.SET_END_TARGET_NEW_TASK),
+                            prefix,
+                            /* errorMessage= */ "onTasksAppeared called "
+                                    + "before/without setting end target to new task",
+                            writer);
+                    errorDetected |= printErrorIfTrue(
+                            !encounteredEvents.contains(GestureEvent.EXPECTING_TASK_APPEARED),
+                            prefix,
+                            /* errorMessage= */ "onTasksAppeared was not expected to be called",
+                            writer);
+                    break;
+                case EXPECTING_TASK_APPEARED:
+                    errorDetected |= printErrorIfTrue(
+                            !encounteredEvents.contains(GestureEvent.SET_END_TARGET_NEW_TASK),
+                            prefix,
+                            /* errorMessage= */ "expecting onTasksAppeared to be called "
+                                    + "before/without setting end target to new task",
+                            writer);
+                    break;
+                case LAUNCHER_DESTROYED:
+                    errorDetected |= printErrorIfTrue(
+                            true,
+                            prefix,
+                            /* errorMessage= */ "Launcher destroyed mid-gesture",
+                            writer);
+                    break;
+                case STATE_GESTURE_COMPLETED:
+                    errorDetected |= printErrorIfTrue(
+                            !encounteredEvents.contains(GestureEvent.MOTION_UP),
+                            prefix,
+                            /* errorMessage= */ "STATE_GESTURE_COMPLETED set "
+                                    + "before/without motion up.",
+                            writer);
+                    errorDetected |= printErrorIfTrue(
+                            !encounteredEvents.contains(GestureEvent.STATE_GESTURE_STARTED),
+                            prefix,
+                            /* errorMessage= */ "STATE_GESTURE_COMPLETED set "
+                                    + "before/without STATE_GESTURE_STARTED.",
+                            writer);
+                    break;
+                case STATE_GESTURE_CANCELLED:
+                    errorDetected |= printErrorIfTrue(
+                            !encounteredEvents.contains(GestureEvent.MOTION_UP),
+                            prefix,
+                            /* errorMessage= */ "STATE_GESTURE_CANCELLED set "
+                                    + "before/without motion up.",
+                            writer);
+                    errorDetected |= printErrorIfTrue(
+                            !encounteredEvents.contains(GestureEvent.STATE_GESTURE_STARTED),
+                            prefix,
+                            /* errorMessage= */ "STATE_GESTURE_CANCELLED set "
+                                    + "before/without STATE_GESTURE_STARTED.",
+                            writer);
+                    break;
+                case STATE_SCREENSHOT_CAPTURED:
+                    errorDetected |= printErrorIfTrue(
+                            !encounteredEvents.contains(GestureEvent.STATE_CAPTURE_SCREENSHOT),
+                            prefix,
+                            /* errorMessage= */ "STATE_SCREENSHOT_CAPTURED set "
+                                    + "before/without STATE_CAPTURE_SCREENSHOT.",
+                            writer);
+                    break;
+                case STATE_RECENTS_SCROLLING_FINISHED:
+                    errorDetected |= printErrorIfTrue(
+                            !encounteredEvents.contains(
+                                    GestureEvent.SET_ON_PAGE_TRANSITION_END_CALLBACK),
+                            prefix,
+                            /* errorMessage= */ "STATE_RECENTS_SCROLLING_FINISHED "
+                                    + "set before/without calling "
+                                    + "setOnPageTransitionEndCallback.",
+                            writer);
+                    break;
+                case STATE_RECENTS_ANIMATION_CANCELED:
+                    errorDetected |= printErrorIfTrue(
+                            !encounteredEvents.contains(
+                                    GestureEvent.START_RECENTS_ANIMATION),
+                            prefix,
+                            /* errorMessage= */ "STATE_RECENTS_ANIMATION_CANCELED "
+                                    + "set before/without startRecentsAnimation.",
+                            writer);
+                    break;
+                case MOTION_DOWN:
+                case SET_END_TARGET:
+                case SET_END_TARGET_HOME:
+                case SET_END_TARGET_NEW_TASK:
+                case START_RECENTS_ANIMATION:
+                case SET_ON_PAGE_TRANSITION_END_CALLBACK:
+                case CANCEL_CURRENT_ANIMATION:
+                case FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER:
+                case STATE_GESTURE_STARTED:
+                case STATE_END_TARGET_ANIMATION_FINISHED:
+                case STATE_CAPTURE_SCREENSHOT:
+                case STATE_HANDLER_INVALIDATED:
+                case STATE_LAUNCHER_DRAWN:
+                default:
+                    // No-Op
             }
+        }
 
-            // Check that all required events were found.
-            errorDetected |= printErrorIfTrue(
-                    !encounteredEvents.contains(GestureEvent.MOTION_DOWN),
-                    prefix,
-                    /* errorMessage= */ "Motion down never detected.",
-                    writer);
-            errorDetected |= printErrorIfTrue(
-                    !encounteredEvents.contains(GestureEvent.MOTION_UP),
-                    prefix,
-                    /* errorMessage= */ "Motion up never detected.",
-                    writer);
+        // Check that all required events were found.
+        errorDetected |= printErrorIfTrue(
+                !encounteredEvents.contains(GestureEvent.MOTION_DOWN),
+                prefix,
+                /* errorMessage= */ "Motion down never detected.",
+                writer);
+        errorDetected |= printErrorIfTrue(
+                !encounteredEvents.contains(GestureEvent.MOTION_UP),
+                prefix,
+                /* errorMessage= */ "Motion up never detected.",
+                writer);
 
-            errorDetected |= printErrorIfTrue(
-                    /* condition= */ encounteredEvents.contains(GestureEvent.SET_END_TARGET)
-                            && !encounteredEvents.contains(GestureEvent.ON_SETTLED_ON_END_TARGET),
-                    prefix,
-                    /* errorMessage= */ "setEndTarget was called, but "
-                            + "onSettledOnEndTarget wasn't.",
-                    writer);
-            errorDetected |= printErrorIfTrue(
-                    /* condition= */ encounteredEvents.contains(GestureEvent.SET_END_TARGET)
-                            && !encounteredEvents.contains(
-                                    GestureEvent.STATE_END_TARGET_ANIMATION_FINISHED),
-                    prefix,
-                    /* errorMessage= */ "setEndTarget was called, but "
-                            + "STATE_END_TARGET_ANIMATION_FINISHED was never set.",
-                    writer);
-            errorDetected |= printErrorIfTrue(
-                    /* condition= */ encounteredEvents.contains(GestureEvent.SET_END_TARGET)
-                            && !encounteredEvents.contains(
-                                    GestureEvent.STATE_RECENTS_SCROLLING_FINISHED),
-                    prefix,
-                    /* errorMessage= */ "setEndTarget was called, but "
-                            + "STATE_RECENTS_SCROLLING_FINISHED was never set.",
-                    writer);
-            errorDetected |= printErrorIfTrue(
-                    /* condition= */ encounteredEvents.contains(
-                            GestureEvent.STATE_END_TARGET_ANIMATION_FINISHED)
-                            && encounteredEvents.contains(
-                                    GestureEvent.STATE_RECENTS_SCROLLING_FINISHED)
-                            && !encounteredEvents.contains(GestureEvent.ON_SETTLED_ON_END_TARGET),
-                    prefix,
-                    /* errorMessage= */ "STATE_END_TARGET_ANIMATION_FINISHED and "
-                            + "STATE_RECENTS_SCROLLING_FINISHED were set, but onSettledOnEndTarget "
-                            + "wasn't called.",
-                    writer);
+        errorDetected |= printErrorIfTrue(
+                /* condition= */ encounteredEvents.contains(GestureEvent.SET_END_TARGET)
+                        && !encounteredEvents.contains(GestureEvent.ON_SETTLED_ON_END_TARGET),
+                prefix,
+                /* errorMessage= */ "setEndTarget was called, but "
+                        + "onSettledOnEndTarget wasn't.",
+                writer);
+        errorDetected |= printErrorIfTrue(
+                /* condition= */ encounteredEvents.contains(GestureEvent.SET_END_TARGET)
+                        && !encounteredEvents.contains(
+                                GestureEvent.STATE_END_TARGET_ANIMATION_FINISHED),
+                prefix,
+                /* errorMessage= */ "setEndTarget was called, but "
+                        + "STATE_END_TARGET_ANIMATION_FINISHED was never set.",
+                writer);
+        errorDetected |= printErrorIfTrue(
+                /* condition= */ encounteredEvents.contains(GestureEvent.SET_END_TARGET)
+                        && !encounteredEvents.contains(
+                                GestureEvent.STATE_RECENTS_SCROLLING_FINISHED),
+                prefix,
+                /* errorMessage= */ "setEndTarget was called, but "
+                        + "STATE_RECENTS_SCROLLING_FINISHED was never set.",
+                writer);
+        errorDetected |= printErrorIfTrue(
+                /* condition= */ encounteredEvents.contains(
+                        GestureEvent.STATE_END_TARGET_ANIMATION_FINISHED)
+                        && encounteredEvents.contains(
+                                GestureEvent.STATE_RECENTS_SCROLLING_FINISHED)
+                        && !encounteredEvents.contains(GestureEvent.ON_SETTLED_ON_END_TARGET),
+                prefix,
+                /* errorMessage= */ "STATE_END_TARGET_ANIMATION_FINISHED and "
+                        + "STATE_RECENTS_SCROLLING_FINISHED were set, but onSettledOnEndTarget "
+                        + "wasn't called.",
+                writer);
 
-            errorDetected |= printErrorIfTrue(
-                    /* condition= */ encounteredEvents.contains(
-                            GestureEvent.START_RECENTS_ANIMATION)
-                            && !encounteredEvents.contains(GestureEvent.FINISH_RECENTS_ANIMATION)
-                            && !encounteredEvents.contains(GestureEvent.CANCEL_RECENTS_ANIMATION),
-                    prefix,
-                    /* errorMessage= */ "startRecentsAnimation was called, but "
-                            + "finishRecentsAnimation and cancelRecentsAnimation weren't.",
-                    writer);
+        errorDetected |= printErrorIfTrue(
+                /* condition= */ encounteredEvents.contains(
+                        GestureEvent.START_RECENTS_ANIMATION)
+                        && !encounteredEvents.contains(GestureEvent.FINISH_RECENTS_ANIMATION)
+                        && !encounteredEvents.contains(GestureEvent.CANCEL_RECENTS_ANIMATION),
+                prefix,
+                /* errorMessage= */ "startRecentsAnimation was called, but "
+                        + "finishRecentsAnimation and cancelRecentsAnimation weren't.",
+                writer);
 
-            errorDetected |= printErrorIfTrue(
-                    /* condition= */ encounteredEvents.contains(GestureEvent.STATE_GESTURE_STARTED)
-                            && !encounteredEvents.contains(GestureEvent.STATE_GESTURE_COMPLETED)
-                            && !encounteredEvents.contains(GestureEvent.STATE_GESTURE_CANCELLED),
-                    prefix,
-                    /* errorMessage= */ "STATE_GESTURE_STARTED was set, but "
-                            + "STATE_GESTURE_COMPLETED and STATE_GESTURE_CANCELLED weren't.",
-                    writer);
+        errorDetected |= printErrorIfTrue(
+                /* condition= */ encounteredEvents.contains(GestureEvent.STATE_GESTURE_STARTED)
+                        && !encounteredEvents.contains(GestureEvent.STATE_GESTURE_COMPLETED)
+                        && !encounteredEvents.contains(GestureEvent.STATE_GESTURE_CANCELLED),
+                prefix,
+                /* errorMessage= */ "STATE_GESTURE_STARTED was set, but "
+                        + "STATE_GESTURE_COMPLETED and STATE_GESTURE_CANCELLED weren't.",
+                writer);
 
-            errorDetected |= printErrorIfTrue(
-                    /* condition= */ encounteredEvents.contains(
-                            GestureEvent.STATE_CAPTURE_SCREENSHOT)
-                            && !encounteredEvents.contains(GestureEvent.STATE_SCREENSHOT_CAPTURED),
-                    prefix,
-                    /* errorMessage= */ "STATE_CAPTURE_SCREENSHOT was set, but "
-                            + "STATE_SCREENSHOT_CAPTURED wasn't.",
-                    writer);
+        errorDetected |= printErrorIfTrue(
+                /* condition= */ encounteredEvents.contains(
+                        GestureEvent.STATE_CAPTURE_SCREENSHOT)
+                        && !encounteredEvents.contains(GestureEvent.STATE_SCREENSHOT_CAPTURED),
+                prefix,
+                /* errorMessage= */ "STATE_CAPTURE_SCREENSHOT was set, but "
+                        + "STATE_SCREENSHOT_CAPTURED wasn't.",
+                writer);
 
-            errorDetected |= printErrorIfTrue(
-                    /* condition= */ encounteredEvents.contains(
-                            GestureEvent.SET_ON_PAGE_TRANSITION_END_CALLBACK)
-                            && !encounteredEvents.contains(
-                                    GestureEvent.STATE_RECENTS_SCROLLING_FINISHED),
-                    prefix,
-                    /* errorMessage= */ "setOnPageTransitionEndCallback called, but "
-                            + "STATE_RECENTS_SCROLLING_FINISHED wasn't set.",
-                    writer);
+        errorDetected |= printErrorIfTrue(
+                /* condition= */ encounteredEvents.contains(
+                        GestureEvent.SET_ON_PAGE_TRANSITION_END_CALLBACK)
+                        && !encounteredEvents.contains(
+                                GestureEvent.STATE_RECENTS_SCROLLING_FINISHED),
+                prefix,
+                /* errorMessage= */ "setOnPageTransitionEndCallback called, but "
+                        + "STATE_RECENTS_SCROLLING_FINISHED wasn't set.",
+                writer);
 
-            errorDetected |= printErrorIfTrue(
-                    /* condition= */ encounteredEvents.contains(
-                            GestureEvent.FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER)
-                            && !encounteredEvents.contains(GestureEvent.CANCEL_CURRENT_ANIMATION)
-                            && !encounteredEvents.contains(GestureEvent.STATE_HANDLER_INVALIDATED),
-                    prefix,
-                    /* errorMessage= */ "AbsSwipeUpHandler.cancelCurrentAnimation "
-                            + "wasn't called and STATE_HANDLER_INVALIDATED wasn't set.",
-                    writer);
+        errorDetected |= printErrorIfTrue(
+                /* condition= */ encounteredEvents.contains(
+                        GestureEvent.FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER)
+                        && !encounteredEvents.contains(GestureEvent.CANCEL_CURRENT_ANIMATION)
+                        && !encounteredEvents.contains(GestureEvent.STATE_HANDLER_INVALIDATED),
+                prefix,
+                /* errorMessage= */ "AbsSwipeUpHandler.cancelCurrentAnimation "
+                        + "wasn't called and STATE_HANDLER_INVALIDATED wasn't set.",
+                writer);
 
-            errorDetected |= printErrorIfTrue(
-                    /* condition= */ encounteredEvents.contains(
-                            GestureEvent.STATE_RECENTS_ANIMATION_CANCELED)
-                            && !encounteredEvents.contains(GestureEvent.CLEANUP_SCREENSHOT),
-                    prefix,
-                    /* errorMessage= */ "STATE_RECENTS_ANIMATION_CANCELED was set but "
-                            + "the task screenshot wasn't cleaned up.",
-                    writer);
+        errorDetected |= printErrorIfTrue(
+                /* condition= */ encounteredEvents.contains(
+                        GestureEvent.STATE_RECENTS_ANIMATION_CANCELED)
+                        && !encounteredEvents.contains(GestureEvent.CLEANUP_SCREENSHOT),
+                prefix,
+                /* errorMessage= */ "STATE_RECENTS_ANIMATION_CANCELED was set but "
+                        + "the task screenshot wasn't cleaned up.",
+                writer);
 
-            errorDetected |= printErrorIfTrue(
-                    /* condition= */ encounteredEvents.contains(
-                            GestureEvent.EXPECTING_TASK_APPEARED)
-                            && !encounteredEvents.contains(GestureEvent.TASK_APPEARED),
-                    prefix,
-                    /* errorMessage= */ "onTaskAppeared was expected to be called but wasn't.",
-                    writer);
+        errorDetected |= printErrorIfTrue(
+                /* condition= */ encounteredEvents.contains(
+                        GestureEvent.EXPECTING_TASK_APPEARED)
+                        && !encounteredEvents.contains(GestureEvent.TASK_APPEARED),
+                prefix,
+                /* errorMessage= */ "onTaskAppeared was expected to be called but wasn't.",
+                writer);
 
-            if (!errorDetected) {
-                writer.println(prefix + "\t\tNo errors detected.");
-            }
+        if (!errorDetected) {
+            writer.println(prefix + "\tNo errors detected.");
         }
     }
 
@@ -358,7 +357,8 @@
         if (!condition) {
             return false;
         }
-        writer.println(prefix + "\t\t- " + errorMessage);
+        writer.println(prefix + "\t- " + errorMessage);
+
         return true;
     }
 }
diff --git a/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java b/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java
index 23fdd58..e05d85c 100644
--- a/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java
+++ b/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java
@@ -155,19 +155,27 @@
     }
 
     public void dump(String prefix, PrintWriter writer) {
+        if (FeatureFlags.ENABLE_GESTURE_ERROR_DETECTION.get()) {
+            writer.println(prefix + "ActiveGestureErrorDetector:");
+            for (int i = 0; i < logs.length; i++) {
+                EventLog eventLog = logs[(nextIndex + i) % logs.length];
+                if (eventLog == null) {
+                    continue;
+                }
+                ActiveGestureErrorDetector.analyseAndDump(prefix + '\t', writer, eventLog);
+            }
+        }
+
         writer.println(prefix + "ActiveGestureLog history:");
         SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSSZ  ", Locale.US);
         Date date = new Date();
-        ArrayList<EventLog> eventLogs = new ArrayList<>();
-
         for (int i = 0; i < logs.length; i++) {
             EventLog eventLog = logs[(nextIndex + i) % logs.length];
             if (eventLog == null) {
                 continue;
             }
-            eventLogs.add(eventLog);
-            writer.println(prefix + "\tLogs for logId: " + eventLog.logId);
 
+            writer.println(prefix + "\tLogs for logId: " + eventLog.logId);
             for (EventEntry eventEntry : eventLog.eventEntries) {
                 date.setTime(eventEntry.time);
 
@@ -199,10 +207,6 @@
                 writer.println(msg);
             }
         }
-
-        if (FeatureFlags.ENABLE_GESTURE_ERROR_DETECTION.get()) {
-            ActiveGestureErrorDetector.analyseAndDump(prefix + '\t', writer, eventLogs);
-        }
     }
 
     /**
diff --git a/quickstep/src/com/android/quickstep/views/DesktopTaskView.java b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
index dc265e4..0ac682f 100644
--- a/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
@@ -24,6 +24,8 @@
 import android.content.Context;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
 import android.graphics.drawable.ShapeDrawable;
 import android.graphics.drawable.shapes.RoundRectShape;
 import android.os.SystemProperties;
@@ -31,14 +33,16 @@
 import android.util.Log;
 import android.util.SparseArray;
 import android.view.MotionEvent;
+import android.view.View;
+import android.widget.FrameLayout;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.launcher3.util.RunnableList;
 import com.android.quickstep.RecentsModel;
 import com.android.quickstep.SystemUiProxy;
@@ -87,7 +91,7 @@
 
     private final ArrayList<CancellableTask<?>> mPendingThumbnailRequests = new ArrayList<>();
 
-    private ShapeDrawable mBackground;
+    private View mBackgroundView;
 
     public DesktopTaskView(Context context) {
         this(context, null);
@@ -104,14 +108,28 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
+
+        mBackgroundView = findViewById(R.id.background);
+
+        int topMarginPx =
+                mActivity.getDeviceProfile().overviewTaskThumbnailTopMarginPx;
+        FrameLayout.LayoutParams params = (LayoutParams) mBackgroundView.getLayoutParams();
+        params.topMargin = topMarginPx;
+        mBackgroundView.setLayoutParams(params);
+
         float[] outerRadii = new float[8];
         Arrays.fill(outerRadii, getTaskCornerRadius());
         RoundRectShape shape = new RoundRectShape(outerRadii, null, null);
-        mBackground = new ShapeDrawable(shape);
-        mBackground.setTint(getResources().getColor(android.R.color.system_neutral2_300,
+        ShapeDrawable background = new ShapeDrawable(shape);
+        background.setTint(getResources().getColor(android.R.color.system_neutral2_300,
                 getContext().getTheme()));
         // TODO(b/244348395): this should be wallpaper
-        setBackground(mBackground);
+        mBackgroundView.setBackground(background);
+
+        Drawable icon = getResources().getDrawable(R.drawable.ic_desktop, getContext().getTheme());
+        Drawable iconBackground = getResources().getDrawable(R.drawable.bg_circle,
+                getContext().getTheme());
+        mIconView.setDrawable(new LayerDrawable(new Drawable[]{iconBackground, icon}));
     }
 
     @Override
@@ -252,20 +270,9 @@
     }
 
     @Override
-    public void setOrientationState(RecentsOrientedState orientationState) {
-        // TODO(b/249371338): this copies logic from TaskView
-        PagedOrientationHandler orientationHandler = orientationState.getOrientationHandler();
-        boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
+    protected void setThumbnailOrientation(RecentsOrientedState orientationState) {
         DeviceProfile deviceProfile = mActivity.getDeviceProfile();
-
-        LayoutParams iconParams = (LayoutParams) mIconView.getLayoutParams();
-
         int thumbnailTopMargin = deviceProfile.overviewTaskThumbnailTopMarginPx;
-        int taskIconHeight = deviceProfile.overviewTaskIconSizePx;
-        int taskMargin = deviceProfile.overviewTaskMarginPx;
-
-        orientationHandler.setTaskIconParams(iconParams, taskMargin, taskIconHeight,
-                thumbnailTopMargin, isRtl);
 
         LayoutParams snapshotParams = (LayoutParams) mSnapshotView.getLayoutParams();
         snapshotParams.topMargin = thumbnailTopMargin;
@@ -374,6 +381,9 @@
 
         setMeasuredDimension(containerWidth, containerHeight);
 
+        int thumbnailTopMarginPx = mActivity.getDeviceProfile().overviewTaskThumbnailTopMarginPx;
+        containerHeight -= thumbnailTopMarginPx;
+
         int thumbnails = mSnapshotViewMap.size();
         if (thumbnails == 0) {
             return;
@@ -416,6 +426,8 @@
                 }
                 int taskX = (int) (positionInParent.x * scaleWidth);
                 int taskY = (int) (positionInParent.y * scaleHeight);
+                // move task down by margin size
+                taskY += thumbnailTopMarginPx;
                 thumbnailView.setX(taskX);
                 thumbnailView.setY(taskY);
 
@@ -439,9 +451,9 @@
         mFullscreenProgress = progress;
         if (mFullscreenProgress > 0) {
             // Don't show background while we are transitioning to/from fullscreen
-            setBackground(null);
+            mBackgroundView.setVisibility(INVISIBLE);
         } else {
-            setBackground(mBackground);
+            mBackgroundView.setVisibility(VISIBLE);
         }
         for (int i = 0; i < mSnapshotViewMap.size(); i++) {
             TaskThumbnailView thumbnailView = mSnapshotViewMap.valueAt(i);
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 829e72c..d3c7778 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -996,6 +996,11 @@
     }
 
     public void setOrientationState(RecentsOrientedState orientationState) {
+        setIconOrientation(orientationState);
+        setThumbnailOrientation(orientationState);
+    }
+
+    protected void setIconOrientation(RecentsOrientedState orientationState) {
         PagedOrientationHandler orientationHandler = orientationState.getOrientationHandler();
         boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
         DeviceProfile deviceProfile = mActivity.getDeviceProfile();
@@ -1016,6 +1021,11 @@
         int iconDrawableSize = isGridTask ? deviceProfile.overviewTaskIconDrawableSizeGridPx
                 : deviceProfile.overviewTaskIconDrawableSizePx;
         mIconView.setDrawableSize(iconDrawableSize, iconDrawableSize);
+    }
+
+    protected void setThumbnailOrientation(RecentsOrientedState orientationState) {
+        DeviceProfile deviceProfile = mActivity.getDeviceProfile();
+        int thumbnailTopMargin = deviceProfile.overviewTaskThumbnailTopMarginPx;
 
         LayoutParams snapshotParams = (LayoutParams) mSnapshotView.getLayoutParams();
         snapshotParams.topMargin = thumbnailTopMargin;
diff --git a/res/drawable/ic_desktop.xml b/res/drawable/ic_desktop.xml
new file mode 100644
index 0000000..dfaf8b8
--- /dev/null
+++ b/res/drawable/ic_desktop.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2023 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.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="32.0dp"
+    android:height="32.0dp"
+    android:viewportWidth="32.0"
+    android:viewportHeight="32.0"
+    >
+    <group android:scaleX="0.5"
+        android:scaleY="0.5"
+        android:translateX="6.0"
+        android:translateY="6.0">
+        <path
+            android:fillColor="?android:attr/textColorPrimary"
+            android:pathData="M5.958,37.708Q4.458,37.708 3.354,36.604Q2.25,35.5 2.25,34V18.292Q2.25,16.792 3.354,15.688Q4.458,14.583 5.958,14.583H9.5V5.958Q9.5,4.458 10.625,3.354Q11.75,2.25 13.208,2.25H34Q35.542,2.25 36.646,3.354Q37.75,4.458 37.75,5.958V21.667Q37.75,23.167 36.646,24.271Q35.542,25.375 34,25.375H30.5V34Q30.5,35.5 29.396,36.604Q28.292,37.708 26.792,37.708ZM5.958,34H26.792Q26.792,34 26.792,34Q26.792,34 26.792,34V21.542H5.958V34Q5.958,34 5.958,34Q5.958,34 5.958,34ZM30.5,21.667H34Q34,21.667 34,21.667Q34,21.667 34,21.667V9.208H13.208V14.583H26.833Q28.375,14.583 29.438,15.667Q30.5,16.75 30.5,18.25Z"/>
+    </group>
+</vector>
diff --git a/res/drawable/widget_suggestions.xml b/res/drawable/widget_suggestions.xml
new file mode 100644
index 0000000..b090a68
--- /dev/null
+++ b/res/drawable/widget_suggestions.xml
@@ -0,0 +1,27 @@
+<!--
+Copyright (C) 2023 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="@color/widget_picker_background_selected"
+    android:gravity="center"
+    >
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M16.65,13 L11,7.35 16.65,1.7 22.3,7.35ZM3,11V3H11V11ZM13,21V13H21V21ZM3,21V13H11V21ZM5,9H9V5H5ZM16.675,10.2 L19.5,7.375 16.675,4.55 13.85,7.375ZM15,19H19V15H15ZM5,19H9V15H5ZM9,9ZM13.85,7.375ZM9,15ZM15,15Z"/>
+</vector>
diff --git a/res/drawable/widget_suggestions_icon.xml b/res/drawable/widget_suggestions_icon.xml
new file mode 100644
index 0000000..919b5e4
--- /dev/null
+++ b/res/drawable/widget_suggestions_icon.xml
@@ -0,0 +1,30 @@
+<!--
+Copyright (C) 2023 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.
+-->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+  <item>
+    <shape android:shape="oval">
+      <size
+          android:width="48dp"
+          android:height="48dp" />
+      <solid android:color="@color/surface"/>
+    </shape>
+  </item>
+  <item
+      android:width="24dp"
+      android:height="24dp"
+      android:drawable="@drawable/widget_suggestions"
+      android:gravity="center" />
+</layer-list>
diff --git a/res/layout/widgets_full_sheet_large_screen.xml b/res/layout/widgets_full_sheet_large_screen.xml
new file mode 100644
index 0000000..3dbe6f5
--- /dev/null
+++ b/res/layout/widgets_full_sheet_large_screen.xml
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2022 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.
+-->
+<com.android.launcher3.widget.picker.WidgetsFullSheet xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    android:theme="?attr/widgetsTheme">
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/container"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="@drawable/bg_widgets_full_sheet"
+        android:focusable="true"
+        android:importantForAccessibility="no">
+
+        <FrameLayout
+            android:id="@+id/recycler_view_container"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            app:layout_constraintEnd_toStartOf="@id/right_pane"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintTop_toBottomOf="@id/title"
+            app:layout_constraintWidth_percent="0.33">
+
+            <TextView
+                android:id="@+id/no_widgets_text"
+                style="@style/PrimaryHeadline"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:gravity="center"
+                android:textSize="18sp"
+                android:visibility="gone"
+                tools:text="No widgets available" />
+
+            <TextView
+                android:id="@+id/fast_scroller_popup"
+                style="@style/FastScrollerPopup"
+                android:layout_marginEnd="@dimen/fastscroll_popup_margin" />
+
+            <!-- Fast scroller popup -->
+            <com.android.launcher3.views.RecyclerViewFastScroller
+                android:id="@+id/fast_scroller"
+                android:layout_width="@dimen/fastscroll_width"
+                android:layout_height="match_parent"
+                android:layout_marginEnd="@dimen/fastscroll_end_margin" />
+
+            <com.android.launcher3.widget.picker.WidgetsRecyclerView
+                android:id="@+id/search_widgets_list_view"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:clipToPadding="false"
+                android:paddingHorizontal="@dimen/widget_list_horizontal_margin_large_screen"
+                android:visibility="gone" />
+        </FrameLayout>
+
+        <ScrollView
+            android:id="@+id/right_pane"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toEndOf="@id/recycler_view_container"
+            app:layout_constraintTop_toBottomOf="@id/title"
+            app:layout_constraintBottom_toBottomOf="parent"
+            android:paddingEnd="16dp"
+            android:paddingStart="8dp"
+            android:layout_marginTop="26dp"
+            app:layout_constraintWidth_percent="0.67">
+
+            <com.android.launcher3.widget.picker.WidgetsRecommendationTableLayout
+                android:id="@+id/recommended_widget_table"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:background="@drawable/widgets_surface_background"
+                android:paddingHorizontal="@dimen/widget_list_horizontal_margin_large_screen"
+                android:paddingVertical="@dimen/recommended_widgets_table_vertical_padding"
+                android:visibility="gone" />
+        </ScrollView>
+
+        <View
+            android:id="@+id/collapse_handle"
+            android:layout_width="@dimen/bottom_sheet_handle_width"
+            android:layout_height="@dimen/bottom_sheet_handle_height"
+            android:layout_marginTop="@dimen/bottom_sheet_handle_margin"
+            android:background="@drawable/bg_rounded_corner_bottom_sheet_handle"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent" />
+
+        <TextView
+            android:id="@+id/title"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="@id/collapse_handle"
+            android:layout_marginTop="24dp"
+            android:gravity="center_horizontal"
+            android:paddingHorizontal="@dimen/widget_list_horizontal_margin_large_screen"
+            android:text="@string/widget_button_text"
+            android:textColor="?android:attr/textColorSecondary"
+            android:textSize="24sp" />
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+</com.android.launcher3.widget.picker.WidgetsFullSheet>
diff --git a/res/layout/widgets_full_sheet_paged_view_large_screen.xml b/res/layout/widgets_full_sheet_paged_view_large_screen.xml
new file mode 100644
index 0000000..6634345
--- /dev/null
+++ b/res/layout/widgets_full_sheet_paged_view_large_screen.xml
@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2023 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.
+-->
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:launcher="http://schemas.android.com/apk/res-auto"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+
+    <FrameLayout
+        android:id="@+id/widgets_full_sheet_paged_view_large_screen"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        app:layout_constraintEnd_toStartOf="@id/scrollView"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/title"
+        app:layout_constraintWidth_percent="0.33">
+        <com.android.launcher3.widget.picker.WidgetPagedView
+            android:id="@+id/widgets_view_pager"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:clipToPadding="false"
+            android:descendantFocusability="afterDescendants"
+            android:paddingHorizontal="@dimen/widget_list_horizontal_margin_large_screen"
+            launcher:pageIndicator="@+id/tabs" >
+
+            <com.android.launcher3.widget.picker.WidgetsRecyclerView
+                android:id="@+id/primary_widgets_list_view"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:clipToPadding="false" />
+
+            <com.android.launcher3.widget.picker.WidgetsRecyclerView
+                android:id="@+id/work_widgets_list_view"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:clipToPadding="false" />
+
+        </com.android.launcher3.widget.picker.WidgetPagedView>
+
+        <!-- SearchAndRecommendationsView without the tab layout as well -->
+        <com.android.launcher3.views.StickyHeaderLayout
+            android:id="@+id/search_and_recommendations_container"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical">
+
+            <FrameLayout
+                android:id="@+id/search_bar_container"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:background="?android:attr/colorBackground"
+                android:clipToPadding="false"
+                android:elevation="0.1dp"
+                android:paddingBottom="8dp"
+                android:paddingHorizontal="@dimen/widget_list_horizontal_margin_large_screen"
+                launcher:layout_sticky="true">
+
+                <include layout="@layout/widgets_search_bar" />
+            </FrameLayout>
+
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:id="@+id/suggestions_header"
+                android:layout_marginTop="8dp"
+                android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin_large_screen"
+                android:orientation="horizontal">
+            </LinearLayout>
+
+            <com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip
+                android:id="@+id/tabs"
+                android:layout_width="match_parent"
+                android:layout_height="64dp"
+                android:gravity="center_horizontal"
+                android:orientation="horizontal"
+                android:paddingVertical="8dp"
+                android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin_large_screen"
+                android:background="?android:attr/colorBackground"
+                style="@style/TextHeadline"
+                launcher:layout_sticky="true">
+
+                <Button
+                    android:id="@+id/tab_personal"
+                    android:layout_width="0dp"
+                    android:layout_height="match_parent"
+                    android:layout_marginEnd="@dimen/widget_tabs_button_horizontal_padding"
+                    android:layout_marginVertical="@dimen/widget_apps_tabs_vertical_padding"
+                    android:layout_weight="1"
+                    android:background="@drawable/all_apps_tabs_background"
+                    android:text="@string/widgets_full_sheet_personal_tab"
+                    android:textColor="@color/all_apps_tab_text"
+                    android:textSize="14sp"
+                    style="?android:attr/borderlessButtonStyle" />
+
+                <Button
+                    android:id="@+id/tab_work"
+                    android:layout_width="0dp"
+                    android:layout_height="match_parent"
+                    android:layout_marginEnd="@dimen/widget_tabs_button_horizontal_padding"
+                    android:layout_marginVertical="@dimen/widget_apps_tabs_vertical_padding"
+                    android:layout_weight="1"
+                    android:background="@drawable/all_apps_tabs_background"
+                    android:text="@string/widgets_full_sheet_work_tab"
+                    android:textColor="@color/all_apps_tab_text"
+                    android:textSize="14sp"
+                    style="?android:attr/borderlessButtonStyle" />
+
+            </com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip>
+        </com.android.launcher3.views.StickyHeaderLayout>
+    </FrameLayout>
+</merge>
diff --git a/res/layout/widgets_full_sheet_recyclerview_large_screen.xml b/res/layout/widgets_full_sheet_recyclerview_large_screen.xml
new file mode 100644
index 0000000..212cd55
--- /dev/null
+++ b/res/layout/widgets_full_sheet_recyclerview_large_screen.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?><!-- 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.
+-->
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:launcher="http://schemas.android.com/apk/res-auto"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+
+    <FrameLayout
+        android:id="@+id/widgets_full_sheet_recyclerview_large_screen"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        app:layout_constraintEnd_toStartOf="@id/scrollView"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/title"
+        app:layout_constraintWidth_percent="0.33">
+
+        <com.android.launcher3.widget.picker.WidgetsRecyclerView
+            android:id="@+id/primary_widgets_list_view"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin_large_screen"
+            android:clipToPadding="false" />
+
+        <!-- SearchAndRecommendationsView without the tab layout as well -->
+        <com.android.launcher3.views.StickyHeaderLayout
+            android:id="@+id/search_and_recommendations_container"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical">
+
+            <FrameLayout
+                android:id="@+id/search_bar_container"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:background="?android:attr/colorBackground"
+                android:clipToPadding="false"
+                android:elevation="0.1dp"
+                android:paddingBottom="8dp"
+                android:paddingHorizontal="@dimen/widget_list_horizontal_margin_large_screen"
+                launcher:layout_sticky="true">
+
+                <include layout="@layout/widgets_search_bar" />
+            </FrameLayout>
+
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:id="@+id/suggestions_header"
+                android:layout_marginTop="8dp"
+                android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin_large_screen"
+                android:orientation="horizontal">
+            </LinearLayout>
+        </com.android.launcher3.views.StickyHeaderLayout>
+    </FrameLayout>
+</merge>
diff --git a/res/values-sw720dp-land/dimens.xml b/res/values-sw720dp-land/dimens.xml
index 6362960..b89910d 100644
--- a/res/values-sw720dp-land/dimens.xml
+++ b/res/values-sw720dp-land/dimens.xml
@@ -31,6 +31,7 @@
 
 <!-- Widget picker-->
     <dimen name="widget_list_horizontal_margin">49dp</dimen>
+    <dimen name="widget_list_horizontal_margin_large_screen">24dp</dimen>
 
 <!-- Bottom sheet-->
     <dimen name="bottom_sheet_extra_top_padding">0dp</dimen>
diff --git a/res/values-v31/colors.xml b/res/values-v31/colors.xml
index 63a5454..f87d9fc 100644
--- a/res/values-v31/colors.xml
+++ b/res/values-v31/colors.xml
@@ -64,4 +64,6 @@
 
     <color name="all_apps_button_color_light">@android:color/system_neutral2_700</color>
     <color name="all_apps_button_color_dark">@android:color/system_neutral2_200</color>
+
+    <color name="widget_picker_background_selected">@android:color/system_accent2_100</color>
 </resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index a9638f9..9d2aa06 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -192,6 +192,7 @@
     <dimen name="widget_list_header_view_vertical_padding">20dp</dimen>
     <dimen name="widget_list_entry_spacing">2dp</dimen>
     <dimen name="widget_list_horizontal_margin">16dp</dimen>
+    <dimen name="widget_list_horizontal_margin_large_screen">24dp</dimen>
 
     <dimen name="widget_preview_shadow_blur">0.5dp</dimen>
     <dimen name="widget_preview_key_shadow_distance">1dp</dimen>
diff --git a/res/values/id.xml b/res/values/id.xml
index 52a7e98..375750f 100644
--- a/res/values/id.xml
+++ b/res/values/id.xml
@@ -19,7 +19,6 @@
     <item type="id" name="view_type_widgets_space" />
     <item type="id" name="view_type_widgets_list" />
     <item type="id" name="view_type_widgets_header" />
-    <item type="id" name="view_type_widgets_search_header" />
     <!--  Used for A11y actions in staged split to identify each task uniquely  -->
     <item type="id" name="split_topLeft_appInfo" />
     <item type="id" name="split_bottomRight_appInfo" />
diff --git a/res/values/strings.xml b/res/values/strings.xml
index a2ebf16..190a3a5 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -63,6 +63,9 @@
     <!-- Accessibility spoken message announced when a widget gets added to the home screen using a
          button in a dialog. [CHAR_LIMIT=none] -->
     <string name="added_to_home_screen_accessibility_text"><xliff:g id="widget_name" example="Calendar month view">%1$s</xliff:g> widget added to home screen</string>
+    <!-- Widget suggestions header title in the full widgets picker for large screen devices
+    in landscape mode. [CHAR_LIMIT=50] -->
+    <string name="suggested_widgets_header_title">Suggestions</string>
     <!-- Label for showing the number of widgets an app has in the full widgets picker.
          [CHAR_LIMIT=25][ICU SYNTAX] -->
     <string name="widgets_count">
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 086cf05..2c34b3f 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -578,15 +578,16 @@
         dimensionOverrideProvider.accept(this);
 
         // This is done last, after iconSizePx is calculated above.
-        mDotRendererWorkSpace = createDotRenderer(iconSizePx, dotRendererCache);
-        mDotRendererAllApps = createDotRenderer(allAppsIconSizePx, dotRendererCache);
+        mDotRendererWorkSpace = createDotRenderer(context, iconSizePx, dotRendererCache);
+        mDotRendererAllApps = createDotRenderer(context, allAppsIconSizePx, dotRendererCache);
     }
 
     private static DotRenderer createDotRenderer(
-            int size, @NonNull SparseArray<DotRenderer> cache) {
+            @NonNull Context context, int size, @NonNull SparseArray<DotRenderer> cache) {
         DotRenderer renderer = cache.get(size);
         if (renderer == null) {
-            renderer = new DotRenderer(size, getShapePath(DEFAULT_DOT_SIZE), DEFAULT_DOT_SIZE);
+            renderer = new DotRenderer(size, getShapePath(context, DEFAULT_DOT_SIZE),
+                    DEFAULT_DOT_SIZE);
             cache.put(size, renderer);
         }
         return renderer;
diff --git a/src/com/android/launcher3/FastScrollRecyclerView.java b/src/com/android/launcher3/FastScrollRecyclerView.java
index 3504b24..e1a216e 100644
--- a/src/com/android/launcher3/FastScrollRecyclerView.java
+++ b/src/com/android/launcher3/FastScrollRecyclerView.java
@@ -63,6 +63,9 @@
 
     public void bindFastScrollbar() {
         ViewGroup parent = (ViewGroup) getParent().getParent();
+        if (parent.findViewById(R.id.fast_scroller) == null) {
+            parent = (ViewGroup) parent.getParent();
+        }
         mScrollbar = parent.findViewById(R.id.fast_scroller);
         mScrollbar.setRecyclerView(this, parent.findViewById(R.id.fast_scroller_popup));
         onUpdateScrollbar(0);
diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
index c3d24f9..dd37bab 100644
--- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
@@ -1101,15 +1101,26 @@
 
     @Override
     public void drawOnScrimWithScale(Canvas canvas, float scale) {
-        boolean isTablet = mActivityContext.getDeviceProfile().isTablet;
+        final boolean isTablet = mActivityContext.getDeviceProfile().isTablet;
+        final View panel = mBottomSheetBackground;
+        final float translationY = ((View) panel.getParent()).getTranslationY();
 
+        final float horizontalScaleOffset = (1 - scale) * panel.getWidth() / 2;
+        final float verticalScaleOffset = (1 - scale) * (panel.getHeight() - getHeight() / 2);
+
+        final float topNoScale = panel.getTop() + translationY;
+        final float topWithScale = topNoScale + verticalScaleOffset;
+        final float leftWithScale = panel.getLeft() + horizontalScaleOffset;
+        final float rightWithScale = panel.getRight() - horizontalScaleOffset;
         // Draw full background panel for tablets.
         if (isTablet) {
             mHeaderPaint.setColor(mBottomSheetBackgroundColor);
-            View panel = (View) mBottomSheetBackground;
-            float translationY = ((View) panel.getParent()).getTranslationY();
-            mTmpRectF.set(panel.getLeft(), panel.getTop() + translationY,
-                    panel.getRight(), panel.getBottom());
+
+            mTmpRectF.set(
+                    leftWithScale,
+                    topWithScale,
+                    rightWithScale,
+                    panel.getBottom());
             mTmpPath.reset();
             mTmpPath.addRoundRect(mTmpRectF, mBottomSheetCornerRadii, Direction.CW);
             canvas.drawPath(mTmpPath, mHeaderPaint);
@@ -1125,25 +1136,33 @@
         if (mHeaderPaint.getColor() == mScrimColor || mHeaderPaint.getColor() == 0) {
             return;
         }
-        final float offset = (getVisibleContainerView().getHeight() * (1 - scale) / 2);
-        final float bottom =
-                scale * (getHeaderBottom() + getVisibleContainerView().getPaddingTop()) + offset;
-        FloatingHeaderView headerView = getFloatingHeaderView();
+
+        // Draw header on background panel
+        final float headerBottomNoScale =
+                getHeaderBottom() + getVisibleContainerView().getPaddingTop();
+        final float headerHeightNoScale = headerBottomNoScale - topNoScale;
+        final float headerBottomWithScaleOnTablet = topWithScale + headerHeightNoScale * scale;
+        final float headerBottomOffset = (getVisibleContainerView().getHeight() * (1 - scale) / 2);
+        final float headerBottomWithScaleOnPhone = headerBottomNoScale * scale + headerBottomOffset;
+        final FloatingHeaderView headerView = getFloatingHeaderView();
         if (isTablet) {
             // Start adding header protection if search bar or tabs will attach to the top.
-            if (!isSearchBarOnBottom() || mUsingTabs) {
-                View panel = (View) mBottomSheetBackground;
-                float translationY = ((View) panel.getParent()).getTranslationY();
-                mTmpRectF.set(panel.getLeft(), panel.getTop() + translationY, panel.getRight(),
-                        bottom);
+            if (!FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get() || mUsingTabs) {
+                mTmpRectF.set(
+                        leftWithScale,
+                        topWithScale,
+                        rightWithScale,
+                        headerBottomWithScaleOnTablet);
                 mTmpPath.reset();
                 mTmpPath.addRoundRect(mTmpRectF, mBottomSheetCornerRadii, Direction.CW);
                 canvas.drawPath(mTmpPath, mHeaderPaint);
             }
         } else {
-            canvas.drawRect(0, 0, canvas.getWidth(), bottom, mHeaderPaint);
+            canvas.drawRect(0, 0, canvas.getWidth(), headerBottomWithScaleOnPhone, mHeaderPaint);
         }
-        int tabsHeight = headerView.getPeripheralProtectionHeight();
+
+        // If tab exist (such as work profile), extend header with tab height
+        final int tabsHeight = headerView.getPeripheralProtectionHeight();
         if (mTabsProtectionAlpha > 0 && tabsHeight != 0) {
             if (DEBUG_HEADER_PROTECTION) {
                 mHeaderPaint.setColor(Color.BLUE);
@@ -1151,13 +1170,24 @@
             } else {
                 mHeaderPaint.setAlpha((int) (getAlpha() * mTabsProtectionAlpha));
             }
-            int left = 0;
-            int right = canvas.getWidth();
+            float left = 0f;
+            float right = canvas.getWidth();
             if (isTablet) {
-                left = mBottomSheetBackground.getLeft();
-                right = mBottomSheetBackground.getRight();
+                left = mBottomSheetBackground.getLeft() + horizontalScaleOffset;
+                right = mBottomSheetBackground.getRight() - horizontalScaleOffset;
             }
-            canvas.drawRect(left, bottom, right, bottom + tabsHeight, mHeaderPaint);
+
+            final float tabTopWithScale = isTablet
+                    ? headerBottomWithScaleOnTablet
+                    : headerBottomWithScaleOnPhone;
+            final float tabBottomWithScale = tabTopWithScale + tabsHeight * scale;
+
+            canvas.drawRect(
+                    left,
+                    tabTopWithScale,
+                    right,
+                    tabBottomWithScale,
+                    mHeaderPaint);
         }
     }
 
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 2dc3e70..68b33bc 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -96,6 +96,9 @@
             "ENABLE_EXPANDING_PAUSE_WORK_BUTTON", false,
             "Expand and collapse pause work button while scrolling");
 
+    public static final BooleanFlag ENABLE_RECENT_BLOCK = getDebugFlag("ENABLE_RECENT_BLOCK",
+            false, "Show recently tapped search target block in zero state");
+
     public static final BooleanFlag COLLECT_SEARCH_HISTORY = new DeviceFlag(
             "COLLECT_SEARCH_HISTORY", false, "Allow launcher to collect search history for log");
 
@@ -379,6 +382,10 @@
             "Enable the ability to generate monochromatic icons, if it is not provided by the app"
     );
 
+    public static final BooleanFlag ENABLE_DREAM_TRANSITION = getDebugFlag(
+            "ENABLE_DREAM_TRANSITION", true,
+            "Enable the launcher transition when the device enters a dream");
+
     public static final BooleanFlag ENABLE_TASKBAR_EDU_TOOLTIP = getDebugFlag(
             "ENABLE_TASKBAR_EDU_TOOLTIP", true,
             "Enable the tooltip version of the Taskbar education flow.");
diff --git a/src/com/android/launcher3/graphics/PreloadIconDrawable.java b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
index f2fde0e..8efd12a 100644
--- a/src/com/android/launcher3/graphics/PreloadIconDrawable.java
+++ b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
@@ -138,7 +138,8 @@
                 IconPalette.getPreloadProgressColor(context, info.bitmap.color),
                 getPreloadColors(context),
                 Utilities.isDarkTheme(context),
-                getRefreshRateMillis(context));
+                getRefreshRateMillis(context),
+                GraphicsUtils.getShapePath(context, DEFAULT_PATH_SIZE));
     }
 
     public PreloadIconDrawable(
@@ -146,10 +147,11 @@
             int indicatorColor,
             int[] preloadColors,
             boolean isDarkMode,
-            int refreshRateMillis) {
+            int refreshRateMillis,
+            Path shapePath) {
         super(info.bitmap);
         mItem = info;
-        mShapePath = GraphicsUtils.getShapePath(DEFAULT_PATH_SIZE);
+        mShapePath = shapePath;
         mScaledTrackPath = new Path();
         mScaledProgressPath = new Path();
 
@@ -386,7 +388,8 @@
                 mIndicatorColor,
                 new int[] {mSystemAccentColor, mSystemBackgroundColor},
                 mIsDarkMode,
-                mRefreshRateMillis);
+                mRefreshRateMillis,
+                mShapePath);
     }
 
     @Override
@@ -442,6 +445,7 @@
         protected final boolean mIsDarkMode;
         protected final int mLevel;
         protected final int mRefreshRateMillis;
+        private final Path mShapePath;
 
         public PreloadIconConstantState(
                 Bitmap bitmap,
@@ -450,7 +454,8 @@
                 int indicatorColor,
                 int[] preloadColors,
                 boolean isDarkMode,
-                int refreshRateMillis) {
+                int refreshRateMillis,
+                Path shapePath) {
             super(bitmap, iconColor);
             mInfo = info;
             mIndicatorColor = indicatorColor;
@@ -458,6 +463,7 @@
             mIsDarkMode = isDarkMode;
             mLevel = info.getProgressLevel();
             mRefreshRateMillis = refreshRateMillis;
+            mShapePath = shapePath;
         }
 
         @Override
@@ -467,7 +473,8 @@
                     mIndicatorColor,
                     mPreloadColors,
                     mIsDarkMode,
-                    mRefreshRateMillis);
+                    mRefreshRateMillis,
+                    mShapePath);
         }
     }
 }
diff --git a/src/com/android/launcher3/popup/PopupDataProvider.java b/src/com/android/launcher3/popup/PopupDataProvider.java
index 80ffecc..69c96ff 100644
--- a/src/com/android/launcher3/popup/PopupDataProvider.java
+++ b/src/com/android/launcher3/popup/PopupDataProvider.java
@@ -238,6 +238,15 @@
                 .collect(Collectors.toList());
     }
 
+    /** Gets the WidgetsListContentEntry for the currently selected header. */
+    public WidgetsListContentEntry getSelectedAppWidgets(PackageUserKey packageUserKey) {
+        return (WidgetsListContentEntry) mAllWidgets.stream()
+                .filter(row -> row instanceof WidgetsListContentEntry
+                        && row.mPkgItem.packageName.equals(packageUserKey.mPackageName))
+                .findAny()
+                .orElse(null);
+    }
+
     /**
      * Returns a list of notifications that are relevant to given ItemInfo.
      */
diff --git a/src/com/android/launcher3/widget/model/WidgetsListBaseEntry.java b/src/com/android/launcher3/widget/model/WidgetsListBaseEntry.java
index 1d1c9dc..f09f4c6 100644
--- a/src/com/android/launcher3/widget/model/WidgetsListBaseEntry.java
+++ b/src/com/android/launcher3/widget/model/WidgetsListBaseEntry.java
@@ -59,27 +59,12 @@
     @Rank
     public abstract int getRank();
 
-    /**
-     * Marker interface for subclasses that are headers for widget list items.
-     *
-     * @param <T> The type of this class.
-     */
-    public interface Header<T extends WidgetsListBaseEntry & Header<T>> {
-        /** Returns whether the widget list is currently expanded. */
-        boolean isWidgetListShown();
-
-        /** Returns a copy of the item with the widget list shown. */
-        T withWidgetListShown();
-    }
-
     @Retention(SOURCE)
-    @IntDef({RANK_WIDGETS_TOP_SPACE, RANK_WIDGETS_LIST_HEADER, RANK_WIDGETS_LIST_SEARCH_HEADER,
-            RANK_WIDGETS_LIST_CONTENT})
+    @IntDef({RANK_WIDGETS_TOP_SPACE, RANK_WIDGETS_LIST_HEADER, RANK_WIDGETS_LIST_CONTENT})
     public @interface Rank {
     }
 
     public static final int RANK_WIDGETS_TOP_SPACE = 1;
     public static final int RANK_WIDGETS_LIST_HEADER = 2;
-    public static final int RANK_WIDGETS_LIST_SEARCH_HEADER = 3;
-    public static final int RANK_WIDGETS_LIST_CONTENT = 4;
+    public static final int RANK_WIDGETS_LIST_CONTENT = 3;
 }
diff --git a/src/com/android/launcher3/widget/model/WidgetsListHeaderEntry.java b/src/com/android/launcher3/widget/model/WidgetsListHeaderEntry.java
index 5b3ea94..bb0cf92 100644
--- a/src/com/android/launcher3/widget/model/WidgetsListHeaderEntry.java
+++ b/src/com/android/launcher3/widget/model/WidgetsListHeaderEntry.java
@@ -15,35 +15,67 @@
  */
 package com.android.launcher3.widget.model;
 
+import android.content.Context;
+import android.content.res.Resources;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.R;
 import com.android.launcher3.model.WidgetItem;
 import com.android.launcher3.model.data.PackageItemInfo;
+import com.android.launcher3.util.PluralMessageFormat;
 
 import java.util.List;
+import java.util.function.BiFunction;
+import java.util.stream.Collectors;
 
 /** An information holder for an app which has widgets or/and shortcuts. */
-public final class WidgetsListHeaderEntry extends WidgetsListBaseEntry
-        implements WidgetsListBaseEntry.Header<WidgetsListHeaderEntry> {
+public final class WidgetsListHeaderEntry extends WidgetsListBaseEntry {
 
-    public final int widgetsCount;
-    public final int shortcutsCount;
+    private static final BiFunction<Context, WidgetsListHeaderEntry, String> SUBTITLE_SEARCH =
+            (context, entry) -> entry.mWidgets.stream()
+                    .map(item -> item.label).sorted().collect(Collectors.joining(", "));
+
+    private static final BiFunction<Context, WidgetsListHeaderEntry, String> SUBTITLE_DEFAULT =
+            (context, entry) -> {
+                List<WidgetItem> items = entry.mWidgets;
+                int wc = (int) items.stream().filter(item -> item.widgetInfo != null).count();
+                int sc = Math.max(0, items.size() - wc);
+
+                Resources resources = context.getResources();
+                if (wc == 0 && sc == 0) {
+                    return null;
+                }
+
+                String subtitle;
+                if (wc > 0 && sc > 0) {
+                    String widgetsCount = PluralMessageFormat.getIcuPluralString(context,
+                            R.string.widgets_count, wc);
+                    String shortcutsCount = PluralMessageFormat.getIcuPluralString(context,
+                            R.string.shortcuts_count, sc);
+                    subtitle = resources.getString(R.string.widgets_and_shortcuts_count,
+                            widgetsCount, shortcutsCount);
+                } else if (wc > 0) {
+                    subtitle = PluralMessageFormat.getIcuPluralString(context,
+                            R.string.widgets_count, wc);
+                } else {
+                    subtitle = PluralMessageFormat.getIcuPluralString(context,
+                            R.string.shortcuts_count, sc);
+                }
+                return subtitle;
+            };
 
     private final boolean mIsWidgetListShown;
-
-    public WidgetsListHeaderEntry(PackageItemInfo pkgItem, String titleSectionName,
-            List<WidgetItem> items) {
-        this(pkgItem, titleSectionName, items, /* isWidgetListShown= */ false);
-    }
+    private final boolean mIsSearchEntry;
 
     private WidgetsListHeaderEntry(PackageItemInfo pkgItem, String titleSectionName,
-            List<WidgetItem> items, boolean isWidgetListShown) {
+            List<WidgetItem> items, boolean isSearchEntry, boolean isWidgetListShown) {
         super(pkgItem, titleSectionName, items);
-        widgetsCount = (int) items.stream().filter(item -> item.widgetInfo != null).count();
-        shortcutsCount = Math.max(0, items.size() - widgetsCount);
+        mIsSearchEntry = isSearchEntry;
         mIsWidgetListShown = isWidgetListShown;
     }
 
     /** Returns {@code true} if the widgets list associated with this header is shown. */
-    @Override
     public boolean isWidgetListShown() {
         return mIsWidgetListShown;
     }
@@ -59,23 +91,54 @@
         return RANK_WIDGETS_LIST_HEADER;
     }
 
+    public boolean isSearchEntry() {
+        return mIsSearchEntry;
+    }
+
+    @Nullable
+    public String getSubtitle(Context context) {
+        return mIsSearchEntry
+                ? SUBTITLE_SEARCH.apply(context, this) : SUBTITLE_DEFAULT.apply(context, this);
+    }
+
     @Override
     public boolean equals(Object obj) {
         if (!(obj instanceof WidgetsListHeaderEntry)) return false;
         WidgetsListHeaderEntry otherEntry = (WidgetsListHeaderEntry) obj;
         return mWidgets.equals(otherEntry.mWidgets) && mPkgItem.equals(otherEntry.mPkgItem)
                 && mTitleSectionName.equals(otherEntry.mTitleSectionName)
-                && mIsWidgetListShown == otherEntry.mIsWidgetListShown;
+                && mIsWidgetListShown == otherEntry.mIsWidgetListShown
+                && mIsSearchEntry == otherEntry.mIsSearchEntry;
     }
 
     /** Returns a copy of this {@link WidgetsListHeaderEntry} with the widget list shown. */
-    @Override
     public WidgetsListHeaderEntry withWidgetListShown() {
         if (mIsWidgetListShown) return this;
         return new WidgetsListHeaderEntry(
                 mPkgItem,
                 mTitleSectionName,
                 mWidgets,
+                mIsSearchEntry,
                 /* isWidgetListShown= */ true);
     }
+
+    public static WidgetsListHeaderEntry create(PackageItemInfo pkgItem, String titleSectionName,
+            List<WidgetItem> items) {
+        return new WidgetsListHeaderEntry(
+                pkgItem,
+                titleSectionName,
+                items,
+                /* forSearch */ false,
+                /* isWidgetListShown= */ false);
+    }
+
+    public static WidgetsListHeaderEntry createForSearch(PackageItemInfo pkgItem,
+            String titleSectionName, List<WidgetItem> items) {
+        return new WidgetsListHeaderEntry(
+                pkgItem,
+                titleSectionName,
+                items,
+                /* forSearch */ true,
+                /* isWidgetListShown= */ false);
+    }
 }
diff --git a/src/com/android/launcher3/widget/model/WidgetsListSearchHeaderEntry.java b/src/com/android/launcher3/widget/model/WidgetsListSearchHeaderEntry.java
deleted file mode 100644
index 055e4ec..0000000
--- a/src/com/android/launcher3/widget/model/WidgetsListSearchHeaderEntry.java
+++ /dev/null
@@ -1,76 +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.model;
-
-import com.android.launcher3.model.WidgetItem;
-import com.android.launcher3.model.data.PackageItemInfo;
-
-import java.util.List;
-
-/** An information holder for an app which has widgets or/and shortcuts, to be shown in search. */
-public final class WidgetsListSearchHeaderEntry extends WidgetsListBaseEntry
-        implements WidgetsListBaseEntry.Header<WidgetsListSearchHeaderEntry> {
-
-    private final boolean mIsWidgetListShown;
-
-    public WidgetsListSearchHeaderEntry(PackageItemInfo pkgItem, String titleSectionName,
-            List<WidgetItem> items) {
-        this(pkgItem, titleSectionName, items, /* isWidgetListShown= */ false);
-    }
-
-    private WidgetsListSearchHeaderEntry(PackageItemInfo pkgItem, String titleSectionName,
-            List<WidgetItem> items, boolean isWidgetListShown) {
-        super(pkgItem, titleSectionName, items);
-        mIsWidgetListShown = isWidgetListShown;
-    }
-
-    /** Returns {@code true} if the widgets list associated with this header is shown. */
-    @Override
-    public boolean isWidgetListShown() {
-        return mIsWidgetListShown;
-    }
-
-    @Override
-    public String toString() {
-        return "SearchHeader:" + mPkgItem.packageName + ":" + mWidgets.size();
-    }
-
-    @Override
-    @Rank
-    public int getRank() {
-        return RANK_WIDGETS_LIST_SEARCH_HEADER;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (!(obj instanceof WidgetsListSearchHeaderEntry)) return false;
-        WidgetsListSearchHeaderEntry otherEntry = (WidgetsListSearchHeaderEntry) obj;
-        return mWidgets.equals(otherEntry.mWidgets) && mPkgItem.equals(otherEntry.mPkgItem)
-                && mTitleSectionName.equals(otherEntry.mTitleSectionName)
-                && mIsWidgetListShown == otherEntry.mIsWidgetListShown;
-    }
-
-    /** Returns a copy of this {@link WidgetsListSearchHeaderEntry} with the widget list shown. */
-    @Override
-    public WidgetsListSearchHeaderEntry withWidgetListShown() {
-        if (mIsWidgetListShown) return this;
-        return new WidgetsListSearchHeaderEntry(
-                mPkgItem,
-                mTitleSectionName,
-                mWidgets,
-                /* isWidgetListShown= */ true);
-    }
-}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsDiffReporter.java b/src/com/android/launcher3/widget/picker/WidgetsDiffReporter.java
index 99374f5..d09fe84 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsDiffReporter.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsDiffReporter.java
@@ -25,7 +25,6 @@
 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 com.android.launcher3.widget.picker.WidgetsListAdapter.WidgetListBaseRowEntryComparator;
 
 import java.util.ArrayList;
@@ -175,12 +174,8 @@
      */
     private boolean hasHeaderUpdated(WidgetsListBaseEntry curRow, WidgetsListBaseEntry newRow) {
         if (newRow instanceof WidgetsListHeaderEntry && curRow instanceof WidgetsListHeaderEntry) {
-            return !curRow.equals(newRow);
-        }
-        if (newRow instanceof WidgetsListSearchHeaderEntry
-                && curRow instanceof WidgetsListSearchHeaderEntry) {
             // Always refresh search header entries to reset rounded corners in their view holder.
-            return true;
+            return !curRow.equals(newRow) || ((WidgetsListHeaderEntry) curRow).isSearchEntry();
         }
         return false;
     }
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index c78ecf5..7c7cdf4 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -20,6 +20,7 @@
 import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
 import static com.android.launcher3.allapps.AllAppsTransitionController.SWIPE_ALL_APPS_TO_HOME_MIN_SCALE;
+import static com.android.launcher3.config.FeatureFlags.LARGE_SCREEN_WIDGET_PICKER;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGETSTRAY_SEARCHED;
 import static com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL;
 
@@ -45,14 +46,18 @@
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.ScrollView;
 import android.widget.TextView;
 
 import androidx.annotation.FloatRange;
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 import androidx.recyclerview.widget.DefaultItemAnimator;
 import androidx.recyclerview.widget.RecyclerView;
 
+import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
@@ -62,7 +67,9 @@
 import com.android.launcher3.compat.AccessibilityManagerCompat;
 import com.android.launcher3.model.UserManagerState;
 import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.model.data.PackageItemInfo;
 import com.android.launcher3.pm.UserCache;
+import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.views.ArrowTipView;
 import com.android.launcher3.views.RecyclerViewFastScroller;
 import com.android.launcher3.views.SpringRelativeLayout;
@@ -71,6 +78,8 @@
 import com.android.launcher3.widget.BaseWidgetSheet;
 import com.android.launcher3.widget.LauncherWidgetHolder.ProviderChangedListener;
 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.picker.search.SearchModeListener;
 import com.android.launcher3.widget.picker.search.WidgetsSearchBar;
 import com.android.launcher3.widget.util.WidgetsTableUtils;
@@ -78,6 +87,7 @@
 import com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip.OnActivePageChangedListener;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.function.Predicate;
 import java.util.stream.IntStream;
@@ -93,6 +103,9 @@
     private static final long EDUCATION_TIP_DELAY_MS = 200;
     private static final long EDUCATION_DIALOG_DELAY_MS = 500;
     private static final float VERTICAL_START_POSITION = 0.3f;
+    private static final int PERSONAL_TAB = 0;
+    private static final int WORK_TAB = 1;
+    private static final String SUGGESTIONS_PACKAGE_NAME = "widgets_list_suggestions_entry";
     // The widget recommendation table can easily take over the entire screen on devices with small
     // resolution or landscape on phone. This ratio defines the max percentage of content area that
     // the table can display.
@@ -169,14 +182,23 @@
 
     private StickyHeaderLayout mSearchScrollView;
     private WidgetsRecommendationTableLayout mRecommendedWidgetsTable;
+    private LinearLayout mSuggestedWidgetsContainer;
+    private WidgetsListHeader mSuggestedWidgetsHeader;
     private View mTabBar;
     private View mSearchBarContainer;
     private WidgetsSearchBar mSearchBar;
     private TextView mHeaderTitle;
+    private ScrollView mRightPane;
+    private WidgetsListTableViewHolderBinder mWidgetsListTableViewHolderBinder;
+    private final boolean mIsTwoPane;
+
+    private int mOrientation;
     private @Nullable WidgetsRecyclerView mCurrentTouchEventRecyclerView;
 
     public WidgetsFullSheet(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
+        DeviceProfile dp = Launcher.getLauncher(context).getDeviceProfile();
+        mIsTwoPane = dp.isTablet && dp.isLandscape && LARGE_SCREEN_WIDGET_PICKER.get();
         mHasWorkProfile = context.getSystemService(LauncherApps.class).getProfiles().size() > 1;
         mAdapters.put(AdapterHolder.PRIMARY, new AdapterHolder(AdapterHolder.PRIMARY));
         mAdapters.put(AdapterHolder.WORK, new AdapterHolder(AdapterHolder.WORK));
@@ -205,9 +227,16 @@
         LayoutInflater layoutInflater = LayoutInflater.from(getContext());
         int contentLayoutRes = mHasWorkProfile ? R.layout.widgets_full_sheet_paged_view
                 : R.layout.widgets_full_sheet_recyclerview;
+        if (mIsTwoPane) {
+            contentLayoutRes = mHasWorkProfile ? R.layout.widgets_full_sheet_paged_view_large_screen
+                    : R.layout.widgets_full_sheet_recyclerview_large_screen;
+        }
         layoutInflater.inflate(contentLayoutRes, mContent, true);
 
         RecyclerViewFastScroller fastScroller = findViewById(R.id.fast_scroller);
+        if (mIsTwoPane) {
+            fastScroller.setVisibility(GONE);
+        }
         mAdapters.get(AdapterHolder.PRIMARY).setup(findViewById(R.id.primary_widgets_list_view));
         mAdapters.get(AdapterHolder.SEARCH).setup(findViewById(R.id.search_widgets_list_view));
         if (mHasWorkProfile) {
@@ -230,15 +259,61 @@
         mSearchScrollView = findViewById(R.id.search_and_recommendations_container);
         mSearchScrollView.setCurrentRecyclerView(findViewById(R.id.primary_widgets_list_view));
 
-        mRecommendedWidgetsTable = mSearchScrollView.findViewById(R.id.recommended_widget_table);
+        mRecommendedWidgetsTable = mIsTwoPane
+                ? mContent.findViewById(R.id.recommended_widget_table)
+                : mSearchScrollView.findViewById(R.id.recommended_widget_table);
+
         mRecommendedWidgetsTable.setWidgetCellLongClickListener(this);
         mRecommendedWidgetsTable.setWidgetCellOnClickListener(this);
 
+        // Add suggested widgets.
+        if (mIsTwoPane) {
+            mSuggestedWidgetsContainer = mSearchScrollView.findViewById(R.id.suggestions_header);
+
+            // Inflate the suggestions header.
+            mSuggestedWidgetsHeader = (WidgetsListHeader) layoutInflater.inflate(
+                    R.layout.widgets_list_row_header, mSuggestedWidgetsContainer, false);
+            mSuggestedWidgetsHeader.setExpanded(true);
+            mSuggestedWidgetsHeader.setBackground(
+                    new WidgetsListDrawableFactory(getContext()).createHeaderBackgroundDrawable());
+
+            PackageItemInfo packageItemInfo =  new PackageItemInfo(
+                    /* packageName= */ SUGGESTIONS_PACKAGE_NAME,
+                    Process.myUserHandle()) {
+                @Override
+                public boolean usingLowResIcon() {
+                    return false;
+                }
+            };
+            packageItemInfo.title = getContext().getString(R.string.suggested_widgets_header_title);
+            WidgetsListHeaderEntry widgetsListHeaderEntry = WidgetsListHeaderEntry.create(
+                    packageItemInfo,
+                    getContext().getString(R.string.suggested_widgets_header_title),
+                    mActivityContext.getPopupDataProvider().getRecommendedWidgets())
+                    .withWidgetListShown();
+
+            mSuggestedWidgetsHeader.applyFromItemInfoWithIcon(widgetsListHeaderEntry);
+            mSuggestedWidgetsHeader.setIcon(
+                    getContext().getDrawable(R.drawable.widget_suggestions_icon));
+            mSuggestedWidgetsHeader.setOnExpandChangeListener(isExpanded -> {
+                mSuggestedWidgetsHeader.setExpanded(isExpanded);
+                resetExpandedHeaders();
+                mRightPane.removeAllViews();
+                mRightPane.addView(mRecommendedWidgetsTable);
+            });
+            mSuggestedWidgetsContainer.addView(mSuggestedWidgetsHeader);
+        }
+
         mTabBar = mSearchScrollView.findViewById(R.id.tabs);
         mSearchBarContainer = mSearchScrollView.findViewById(R.id.search_bar_container);
         mSearchBar = mSearchScrollView.findViewById(R.id.widgets_search_bar);
-        mHeaderTitle = mSearchScrollView.findViewById(R.id.title);
-
+        mHeaderTitle = mIsTwoPane
+                ? mContent.findViewById(R.id.title)
+                : mSearchScrollView.findViewById(R.id.title);
+        mRightPane = mIsTwoPane ? mContent.findViewById(R.id.right_pane) : null;
+        mWidgetsListTableViewHolderBinder =  new WidgetsListTableViewHolderBinder(
+                layoutInflater, this, this,
+                new WidgetsListDrawableFactory(getContext()));
         onRecommendedWidgetsBound();
         onWidgetsBound();
 
@@ -260,6 +335,13 @@
 
     @Override
     public void onActivePageChanged(int currentActivePage) {
+
+        // if the current active page changes to personal or work we set suggestions
+        // to be the selected widget
+        if (mIsTwoPane && (currentActivePage == PERSONAL_TAB || currentActivePage == WORK_TAB)) {
+            mSuggestedWidgetsHeader.callOnClick();
+        }
+
         AdapterHolder currentAdapterHolder = mAdapters.get(currentActivePage);
         WidgetsRecyclerView currentRecyclerView =
                 mAdapters.get(currentActivePage).mWidgetsRecyclerView;
@@ -428,6 +510,11 @@
         View content = mHasWorkProfile
                 ? mViewPager
                 : mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView;
+
+        if (mIsTwoPane && mRightPane != null) {
+            content = mRightPane;
+        }
+
         int maxHorizontalSpans = computeMaxHorizontalSpans(content,
                 mWidgetSheetContentHorizontalPadding);
         if (mMaxSpansPerRow != maxHorizontalSpans) {
@@ -520,6 +607,9 @@
     public void onSearchResults(List<WidgetsListBaseEntry> entries) {
         mAdapters.get(AdapterHolder.SEARCH).mWidgetsListAdapter.setWidgetsOnSearch(entries);
         updateRecyclerViewVisibility(mAdapters.get(AdapterHolder.SEARCH));
+        if (mIsTwoPane) {
+            mAdapters.get(AdapterHolder.SEARCH).mWidgetsListAdapter.selectFirstHeaderEntry();
+        }
         mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView.scrollToTop();
     }
 
@@ -527,6 +617,9 @@
         mIsInSearchMode = isInSearchMode;
         if (isInSearchMode) {
             mRecommendedWidgetsTable.setVisibility(GONE);
+            if (mIsTwoPane) {
+                mSuggestedWidgetsContainer.setVisibility(GONE);
+            }
             if (mHasWorkProfile) {
                 mViewPager.setVisibility(GONE);
                 mTabBar.setVisibility(GONE);
@@ -538,6 +631,10 @@
             mNoWidgetsView.setVisibility(GONE);
         } else {
             mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView.setVisibility(GONE);
+            if (mIsTwoPane) {
+                mSuggestedWidgetsContainer.setVisibility(VISIBLE);
+                mSuggestedWidgetsHeader.callOnClick();
+            }
             // Visibility of recommended widgets, recycler views and headers are handled in methods
             // below.
             onRecommendedWidgetsBound();
@@ -572,7 +669,7 @@
                             MeasureSpec.EXACTLY),
                     makeMeasureSpec(mActivityContext.getDeviceProfile().availableHeightPx,
                             MeasureSpec.EXACTLY));
-            float maxTableHeight = (mContent.getMeasuredHeight()
+            float maxTableHeight = mIsTwoPane ? Float.MAX_VALUE : (mContent.getMeasuredHeight()
                     - mTabsHeight - getHeaderViewHeight()
                     - noWidgetsViewHeight) * RECOMMENDATION_TABLE_HEIGHT_RATIO;
 
@@ -626,7 +723,7 @@
 
     @Override
     public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
-        // Disable swipe down when recycler view is scrolling
+        // Disable swipe down when recycler view is scrolling or scroll view is scrolling
         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
             mNoIntercept = false;
             WidgetsRecyclerView recyclerView = getRecyclerView();
@@ -636,6 +733,8 @@
                 mNoIntercept = true;
             } else if (getPopupContainer().isEventOverView(recyclerView, ev)) {
                 mNoIntercept = !recyclerView.shouldContainerScroll(ev, getPopupContainer());
+            } else if (mIsTwoPane && getPopupContainer().isEventOverView(mRightPane, ev)) {
+                mNoIntercept = mRightPane.getScrollY() > 0;
             }
 
             if (mSearchBar.isSearchBarFocused()
@@ -649,7 +748,11 @@
     /** Shows the {@link WidgetsFullSheet} on the launcher. */
     public static WidgetsFullSheet show(Launcher launcher, boolean animate) {
         WidgetsFullSheet sheet = (WidgetsFullSheet) launcher.getLayoutInflater()
-                .inflate(R.layout.widgets_full_sheet, launcher.getDragLayer(), false);
+                .inflate(LARGE_SCREEN_WIDGET_PICKER.get()
+                        && launcher.getDeviceProfile().isTablet
+                        && launcher.getDeviceProfile().isLandscape
+                        ? R.layout.widgets_full_sheet_large_screen
+                        : R.layout.widgets_full_sheet, launcher.getDragLayer(), false);
         sheet.attachToContainer();
         sheet.mIsOpen = true;
         sheet.open(animate);
@@ -746,6 +849,13 @@
         if (mIsInSearchMode) {
             mSearchBar.reset();
         }
+
+        // Checks the orientation of the screen
+        if (LARGE_SCREEN_WIDGET_PICKER.get() && mOrientation != newConfig.orientation) {
+            mOrientation = newConfig.orientation;
+            handleClose(false);
+            show(Launcher.getLauncher(getContext()), false);
+        }
     }
 
     @Override
@@ -835,16 +945,44 @@
 
         AdapterHolder(int adapterType) {
             mAdapterType = adapterType;
-
             Context context = getContext();
             LauncherAppState apps = LauncherAppState.getInstance(context);
+
+            HeaderChangeListener headerChangeListener = new HeaderChangeListener() {
+                @Override
+                public void onHeaderChanged(@NonNull PackageUserKey selectedHeader) {
+                    WidgetsListContentEntry contentEntry = mActivityContext.getPopupDataProvider()
+                            .getSelectedAppWidgets(selectedHeader);
+
+                    if (contentEntry == null || mRightPane == null) {
+                        return;
+                    }
+
+                    if (mSuggestedWidgetsHeader != null) {
+                        mSuggestedWidgetsHeader.setExpanded(false);
+                    }
+                    WidgetsRowViewHolder widgetsRowViewHolder =
+                            mWidgetsListTableViewHolderBinder.newViewHolder(mRightPane);
+                    mWidgetsListTableViewHolderBinder.bindViewHolder(widgetsRowViewHolder,
+                            contentEntry,
+                            0, Collections.EMPTY_LIST);
+                    widgetsRowViewHolder.mDataCallback = data -> {
+                        mWidgetsListTableViewHolderBinder.bindViewHolder(widgetsRowViewHolder,
+                                contentEntry,
+                                0, Collections.singletonList(data));
+                    };
+                    mRightPane.removeAllViews();
+                    mRightPane.addView(widgetsRowViewHolder.itemView);
+                }
+            };
             mWidgetsListAdapter = new WidgetsListAdapter(
                     context,
                     LayoutInflater.from(context),
                     apps.getIconCache(),
                     this::getEmptySpaceHeight,
                     /* iconClickListener= */ WidgetsFullSheet.this,
-                    /* iconLongClickListener= */ WidgetsFullSheet.this);
+                    /* iconLongClickListener= */ WidgetsFullSheet.this,
+                    mIsTwoPane ? headerChangeListener : null);
             mWidgetsListAdapter.setHasStableIds(true);
             switch (mAdapterType) {
                 case PRIMARY:
@@ -871,8 +1009,10 @@
             mWidgetsRecyclerView.setAdapter(mWidgetsListAdapter);
             mWidgetsRecyclerView.setItemAnimator(mWidgetsListItemAnimator);
             mWidgetsRecyclerView.setHeaderViewDimensionsProvider(WidgetsFullSheet.this);
-            mWidgetsRecyclerView.setEdgeEffectFactory(
-                    ((SpringRelativeLayout) mContent).createEdgeEffectFactory());
+            if (!mIsTwoPane) {
+                mWidgetsRecyclerView.setEdgeEffectFactory(
+                        ((SpringRelativeLayout) mContent).createEdgeEffectFactory());
+            }
             // Recycler view binds to fast scroller when it is attached to screen. Make sure
             // search recycler view is bound to fast scroller if user is in search mode at the time
             // of attachment.
@@ -882,4 +1022,15 @@
             mWidgetsListAdapter.setMaxHorizontalSpansPerRow(mMaxSpansPerRow);
         }
     }
+
+    /**
+     * This is a listener for when the selected header gets changed in the left pane.
+     */
+    public interface HeaderChangeListener {
+        /**
+         * Sets the right pane to have the widgets for the currently selected header from
+         * the left pane.
+         */
+        void onHeaderChanged(@NonNull PackageUserKey selectedHeader);
+    }
 }
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
index e6b9dca..549ac42 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
@@ -48,7 +48,6 @@
 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.Arrays;
@@ -81,13 +80,13 @@
     public static final int VIEW_TYPE_WIDGETS_SPACE = R.id.view_type_widgets_space;
     public static final int VIEW_TYPE_WIDGETS_LIST = R.id.view_type_widgets_list;
     public static final int VIEW_TYPE_WIDGETS_HEADER = R.id.view_type_widgets_header;
-    public static final int VIEW_TYPE_WIDGETS_SEARCH_HEADER = R.id.view_type_widgets_search_header;
 
     private final Context mContext;
     private final WidgetsDiffReporter mDiffReporter;
     private final SparseArray<ViewHolderBinder> mViewHolderBinders = new SparseArray<>();
     private final WidgetListBaseRowEntryComparator mRowComparator =
             new WidgetListBaseRowEntryComparator();
+    @Nullable private final WidgetsFullSheet.HeaderChangeListener mHeaderChangeListener;
 
     private final List<WidgetsListBaseEntry> mAllEntries = new ArrayList<>();
     private ArrayList<WidgetsListBaseEntry> mVisibleEntries = new ArrayList<>();
@@ -95,7 +94,6 @@
 
     private Predicate<WidgetsListBaseEntry> mHeaderAndSelectedContentFilter = entry ->
             entry instanceof WidgetsListHeaderEntry
-                    || entry instanceof WidgetsListSearchHeaderEntry
                     || PackageUserKey.fromPackageItemInfo(entry.mPkgItem)
                             .equals(mWidgetsContentVisiblePackageUserKey);
     @Nullable private Predicate<WidgetsListBaseEntry> mFilter = null;
@@ -105,7 +103,9 @@
 
     public WidgetsListAdapter(Context context, LayoutInflater layoutInflater,
             IconCache iconCache, IntSupplier emptySpaceHeightProvider,
-            OnClickListener iconClickListener, OnLongClickListener iconLongClickListener) {
+            OnClickListener iconClickListener, OnLongClickListener iconLongClickListener,
+            WidgetsFullSheet.HeaderChangeListener headerChangeListener) {
+        mHeaderChangeListener = headerChangeListener;
         mContext = context;
         mDiffReporter = new WidgetsDiffReporter(iconCache, this);
         WidgetsListDrawableFactory listDrawableFactory = new WidgetsListDrawableFactory(context);
@@ -122,12 +122,6 @@
                         /* onHeaderClickListener= */ this,
                         listDrawableFactory));
         mViewHolderBinders.put(
-                VIEW_TYPE_WIDGETS_SEARCH_HEADER,
-                new WidgetsListSearchHeaderViewHolderBinder(
-                        layoutInflater,
-                        /* onHeaderClickListener= */ this,
-                        listDrawableFactory));
-        mViewHolderBinders.put(
                 VIEW_TYPE_WIDGETS_SPACE,
                 new WidgetsSpaceViewHolderBinder(emptySpaceHeightProvider));
     }
@@ -196,14 +190,16 @@
                 getOffsetForPosition(previousPositionForPackageUserKey);
 
         List<WidgetsListBaseEntry> newVisibleEntries = mAllEntries.stream()
-                .filter(entry -> ((mFilter == null || mFilter.test(entry))
+                .filter(entry -> (((mFilter == null || mFilter.test(entry))
                         && mHeaderAndSelectedContentFilter.test(entry))
                         || entry instanceof WidgetListSpaceEntry)
+                        && (mHeaderChangeListener == null
+                        || !(entry instanceof WidgetsListContentEntry)))
                 .map(entry -> {
-                    if (entry instanceof WidgetsListBaseEntry.Header<?>
+                    if (entry instanceof WidgetsListHeaderEntry
                             && matchesKey(entry, mWidgetsContentVisiblePackageUserKey)) {
                         // Adjust the original entries to expand headers for the selected content.
-                        return ((WidgetsListBaseEntry.Header<?>) entry).withWidgetListShown();
+                        return ((WidgetsListHeaderEntry) entry).withWidgetListShown();
                     } else if (entry instanceof WidgetsListContentEntry) {
                         // Adjust the original content entries to accommodate for the current
                         // maxSpanSize.
@@ -225,11 +221,10 @@
         }
     }
 
-
     /** Returns whether {@code entry} matches {@code key}. */
     private static boolean isHeaderForPackageUserKey(
             @NonNull WidgetsListBaseEntry entry, @Nullable PackageUserKey key) {
-        return entry instanceof WidgetsListBaseEntry.Header && matchesKey(entry, key);
+        return entry instanceof WidgetsListHeaderEntry && matchesKey(entry, key);
     }
 
     private static boolean matchesKey(@NonNull WidgetsListBaseEntry entry,
@@ -258,7 +253,6 @@
     @Override
     public void onBindViewHolder(ViewHolder holder, int pos, List<Object> payloads) {
         ViewHolderBinder viewHolderBinder = mViewHolderBinders.get(getItemViewType(pos));
-        WidgetsListBaseEntry entry = mVisibleEntries.get(pos);
 
         // The first entry has an empty space, count from second entries.
         int listPos = (pos > 1) ? POSITION_DEFAULT : POSITION_FIRST;
@@ -268,6 +262,18 @@
         viewHolderBinder.bindViewHolder(holder, mVisibleEntries.get(pos), listPos, payloads);
     }
 
+    /**
+     * Selects the first visible header. This is used in search as we want to always select the
+     * first header in the new list that gets generated as we search.
+     */
+    void selectFirstHeaderEntry() {
+        mVisibleEntries.stream()
+                .filter(entry -> entry instanceof WidgetsListHeaderEntry)
+                .findFirst()
+                .ifPresent(entry ->
+                        onHeaderClicked(true, PackageUserKey.fromPackageItemInfo(entry.mPkgItem)));
+    }
+
     @Override
     public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
         if (DEBUG) {
@@ -305,8 +311,6 @@
             return VIEW_TYPE_WIDGETS_LIST;
         } else if (entry instanceof WidgetsListHeaderEntry) {
             return VIEW_TYPE_WIDGETS_HEADER;
-        } else if (entry instanceof WidgetsListSearchHeaderEntry) {
-            return VIEW_TYPE_WIDGETS_SEARCH_HEADER;
         } else if (entry instanceof WidgetListSpaceEntry) {
             return VIEW_TYPE_WIDGETS_SPACE;
         }
@@ -318,6 +322,9 @@
         // Ignore invalid clicks, such as collapsing a package that isn't currently expanded.
         if (!showWidgets && !packageUserKey.equals(mWidgetsContentVisiblePackageUserKey)) return;
 
+        if (mHeaderChangeListener != null
+                && packageUserKey.equals(mWidgetsContentVisiblePackageUserKey)) return;
+
         if (showWidgets) {
             mWidgetsContentVisiblePackageUserKey = packageUserKey;
             ActivityContext.lookupContext(mContext)
@@ -331,6 +338,10 @@
         mPendingClickHeader = packageUserKey;
 
         updateVisibleEntries();
+
+        if (mHeaderChangeListener != null && mWidgetsContentVisiblePackageUserKey != null) {
+            mHeaderChangeListener.onHeaderChanged(mWidgetsContentVisiblePackageUserKey);
+        }
     }
 
     /**
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListHeader.java b/src/com/android/launcher3/widget/picker/WidgetsListHeader.java
index 48df04f..a6ef89f 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListHeader.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListHeader.java
@@ -15,13 +15,17 @@
  */
 package com.android.launcher3.widget.picker;
 
+import static com.android.launcher3.config.FeatureFlags.LARGE_SCREEN_WIDGET_PICKER;
 
 import android.content.Context;
-import android.content.res.Resources;
 import android.content.res.TypedArray;
+import android.graphics.Color;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
 import android.os.Bundle;
+import android.text.TextUtils;
 import android.util.AttributeSet;
+import android.util.TypedValue;
 import android.view.View;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.CheckBox;
@@ -35,17 +39,15 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
 import com.android.launcher3.icons.PlaceHolderIconDrawable;
 import com.android.launcher3.icons.cache.HandlerRunnable;
 import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.model.data.PackageItemInfo;
-import com.android.launcher3.util.PluralMessageFormat;
+import com.android.launcher3.util.Themes;
 import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
-import com.android.launcher3.widget.model.WidgetsListSearchHeaderEntry;
-
-import java.util.stream.Collectors;
 
 /**
  * A UI represents a header of an app shown in the full widgets tray.
@@ -55,19 +57,19 @@
  */
 public final class WidgetsListHeader extends LinearLayout implements ItemInfoUpdateReceiver {
 
-    private boolean mEnableIconUpdateAnimation = false;
+    private final int mIconSize;
+    private final boolean mIsTwoPane;
 
     @Nullable private HandlerRunnable mIconLoadRequest;
     @Nullable private Drawable mIconDrawable;
-    private final int mIconSize;
-
+    @Nullable private WidgetsListDrawableState mListDrawableState;
     private ImageView mAppIcon;
     private TextView mTitle;
     private TextView mSubtitle;
-
+    private GradientDrawable mBackground;
     private CheckBox mExpandToggle;
+    private boolean mEnableIconUpdateAnimation = false;
     private boolean mIsExpanded = false;
-    @Nullable private WidgetsListDrawableState mListDrawableState;
 
     public WidgetsListHeader(Context context) {
         this(context, /* attrs= */ null);
@@ -86,6 +88,11 @@
                 R.styleable.WidgetsListRowHeader, defStyleAttr, /* defStyleRes= */ 0);
         mIconSize = a.getDimensionPixelSize(R.styleable.WidgetsListRowHeader_appIconSize,
                 grid.iconSizePx);
+
+        mIsTwoPane = grid.isLandscape && grid.isTablet && LARGE_SCREEN_WIDGET_PICKER.get();
+        if (mIsTwoPane) {
+            setLargeScreenTheme();
+        }
     }
 
     @Override
@@ -95,6 +102,9 @@
         mTitle = findViewById(R.id.app_title);
         mSubtitle = findViewById(R.id.app_subtitle);
         mExpandToggle = findViewById(R.id.toggle);
+        if (mIsTwoPane) {
+            mExpandToggle.setVisibility(GONE);
+        }
         setAccessibilityDelegate(new AccessibilityDelegate() {
 
             @Override
@@ -132,7 +142,7 @@
             @Nullable OnExpansionChangeListener onExpandChangeListener) {
         // Use the entire touch area of this view to expand / collapse an app widgets section.
         setOnClickListener(view -> {
-            setExpanded(!mIsExpanded);
+            setExpanded(mIsTwoPane || !mIsExpanded);
             if (onExpandChangeListener != null) {
                 onExpandChangeListener.onExpansionChange(mIsExpanded);
             }
@@ -144,8 +154,38 @@
     public void setExpanded(boolean isExpanded) {
         this.mIsExpanded = isExpanded;
         mExpandToggle.setChecked(isExpanded);
+        if (mIsTwoPane) {
+            if (Utilities.isDarkTheme(getContext())) {
+                if (mIsExpanded) {
+                    mTitle.setTextColor(Color.BLACK);
+                    mSubtitle.setTextColor(Color.BLACK);
+                } else {
+                    mTitle.setTextColor(Color.WHITE);
+                    mSubtitle.setTextColor(Themes.getAttrColor(getContext(),
+                            android.R.attr.textColorSecondary));
+                }
+            }
+            setLargeScreenTheme();
+        }
     }
 
+    /**
+     * Sets the style for the header when we are using large screens in landscape.
+     */
+    private void setLargeScreenTheme() {
+        if (mBackground == null) {
+            mBackground = new GradientDrawable();
+            mBackground.setCornerRadius((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+                    28,
+                    getContext().getResources().getDisplayMetrics()));
+        }
+        mBackground.setColor(mIsExpanded
+                ? getResources().getColor(R.color.widget_picker_background_selected)
+                : Color.TRANSPARENT);
+        this.setBackground(mBackground);
+    }
+
+
     /** Sets the {@link WidgetsListDrawableState} and refreshes the background drawable. */
     @UiThread
     public void setListDrawableState(WidgetsListDrawableState state) {
@@ -157,13 +197,8 @@
     /** Apply app icon, labels and tag using a generic {@link WidgetsListHeaderEntry}. */
     @UiThread
     public void applyFromItemInfoWithIcon(WidgetsListHeaderEntry entry) {
-        applyIconAndLabel(entry);
-    }
-
-    @UiThread
-    private void applyIconAndLabel(WidgetsListHeaderEntry entry) {
         PackageItemInfo info = entry.mPkgItem;
-        setIcon(info);
+        setIcon(info.newIcon(getContext()));
         setTitles(entry);
         setExpanded(entry.isWidgetListShown());
 
@@ -172,9 +207,7 @@
         verifyHighRes();
     }
 
-    private void setIcon(PackageItemInfo info) {
-        Drawable icon;
-        icon = info.newIcon(getContext());
+    void setIcon(Drawable icon) {
         applyDrawables(icon);
         mIconDrawable = icon;
         if (mIconDrawable != null) {
@@ -205,55 +238,13 @@
     private void setTitles(WidgetsListHeaderEntry entry) {
         mTitle.setText(entry.mPkgItem.title);
 
-        Resources resources = getContext().getResources();
-        if (entry.widgetsCount == 0 && entry.shortcutsCount == 0) {
+        String subtitle = entry.getSubtitle(getContext());
+        if (TextUtils.isEmpty(subtitle)) {
             mSubtitle.setVisibility(GONE);
-            return;
-        }
-
-        String subtitle;
-        if (entry.widgetsCount > 0 && entry.shortcutsCount > 0) {
-            String widgetsCount = PluralMessageFormat.getIcuPluralString(getContext(),
-                    R.string.widgets_count, entry.widgetsCount);
-            String shortcutsCount = PluralMessageFormat.getIcuPluralString(getContext(),
-                    R.string.shortcuts_count, entry.shortcutsCount);
-            subtitle = resources.getString(R.string.widgets_and_shortcuts_count, widgetsCount,
-                    shortcutsCount);
-        } else if (entry.widgetsCount > 0) {
-            subtitle = PluralMessageFormat.getIcuPluralString(getContext(),
-                    R.string.widgets_count, entry.widgetsCount);
         } else {
-            subtitle = PluralMessageFormat.getIcuPluralString(getContext(),
-                    R.string.shortcuts_count, entry.shortcutsCount);
+            mSubtitle.setText(subtitle);
+            mSubtitle.setVisibility(VISIBLE);
         }
-        mSubtitle.setText(subtitle);
-        mSubtitle.setVisibility(VISIBLE);
-    }
-
-    /** Apply app icon, labels and tag using a generic {@link WidgetsListSearchHeaderEntry}. */
-    @UiThread
-    public void applyFromItemInfoWithIcon(WidgetsListSearchHeaderEntry entry) {
-        applyIconAndLabel(entry);
-    }
-
-    @UiThread
-    private void applyIconAndLabel(WidgetsListSearchHeaderEntry entry) {
-        PackageItemInfo info = entry.mPkgItem;
-        setIcon(info);
-        setTitles(entry);
-        setExpanded(entry.isWidgetListShown());
-
-        super.setTag(info);
-
-        verifyHighRes();
-    }
-
-    private void setTitles(WidgetsListSearchHeaderEntry entry) {
-        mTitle.setText(entry.mPkgItem.title);
-
-        mSubtitle.setText(entry.mWidgets.stream()
-                .map(item -> item.label).sorted().collect(Collectors.joining(", ")));
-        mSubtitle.setVisibility(VISIBLE);
     }
 
     @Override
@@ -265,7 +256,7 @@
             // Optimization: Starting in N, pre-uploads the bitmap to RenderThread.
             info.bitmap.icon.prepareToDraw();
 
-            setIcon((PackageItemInfo) info);
+            setIcon(info.newIcon(getContext()));
 
             mEnableIconUpdateAnimation = false;
         }
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinder.java
deleted file mode 100644
index 2b27fc2..0000000
--- a/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinder.java
+++ /dev/null
@@ -1,69 +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;
-
-import android.view.LayoutInflater;
-import android.view.ViewGroup;
-
-import com.android.launcher3.R;
-import com.android.launcher3.recyclerview.ViewHolderBinder;
-import com.android.launcher3.util.PackageUserKey;
-import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
-import com.android.launcher3.widget.model.WidgetsListSearchHeaderEntry;
-
-import java.util.List;
-
-/**
- * Binds data from {@link WidgetsListHeaderEntry} to UI elements in {@link WidgetsListHeaderHolder}.
- */
-public final class WidgetsListSearchHeaderViewHolderBinder implements
-        ViewHolderBinder<WidgetsListSearchHeaderEntry, WidgetsListSearchHeaderHolder> {
-    private final LayoutInflater mLayoutInflater;
-    private final OnHeaderClickListener mOnHeaderClickListener;
-    private final WidgetsListDrawableFactory mListDrawableFactory;
-
-    public WidgetsListSearchHeaderViewHolderBinder(LayoutInflater layoutInflater,
-            OnHeaderClickListener onHeaderClickListener,
-            WidgetsListDrawableFactory listDrawableFactory) {
-        mLayoutInflater = layoutInflater;
-        mOnHeaderClickListener = onHeaderClickListener;
-        mListDrawableFactory = listDrawableFactory;
-    }
-
-    @Override
-    public WidgetsListSearchHeaderHolder newViewHolder(ViewGroup parent) {
-        WidgetsListHeader header = (WidgetsListHeader) mLayoutInflater.inflate(
-                R.layout.widgets_list_row_header, parent, false);
-        header.setBackground(mListDrawableFactory.createHeaderBackgroundDrawable());
-        return new WidgetsListSearchHeaderHolder(header);
-    }
-
-    @Override
-    public void bindViewHolder(WidgetsListSearchHeaderHolder viewHolder,
-            WidgetsListSearchHeaderEntry data, @ListPosition int position, List<Object> payloads) {
-        WidgetsListHeader widgetsListHeader = viewHolder.mWidgetsListHeader;
-        widgetsListHeader.applyFromItemInfoWithIcon(data);
-        widgetsListHeader.setExpanded(data.isWidgetListShown());
-        widgetsListHeader.setListDrawableState(
-                WidgetsListDrawableState.obtain(
-                        (position & POSITION_FIRST) != 0,
-                        (position & POSITION_LAST) != 0,
-                        /* isExpanded= */ data.isWidgetListShown()));
-        widgetsListHeader.setOnExpandChangeListener(isExpanded ->
-                mOnHeaderClickListener.onHeaderClicked(isExpanded,
-                        PackageUserKey.fromPackageItemInfo(data.mPkgItem)));
-    }
-}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
index 05e26ad..8500b9a 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
@@ -110,13 +110,8 @@
 
                 // When preview loads, notify adapter to rebind the item and possibly animate
                 widget.applyFromCellItem(widgetItem, 1f,
-                        bitmap -> {
-                        if (holder.getBindingAdapter() != null) {
-                            holder.getBindingAdapter().notifyItemChanged(
-                                    holder.getBindingAdapterPosition(),
-                                    Pair.create(widgetItem, bitmap));
-                            }
-                        }, holder.previewCache.get(widgetItem));
+                        bitmap -> holder.onPreviewLoaded(Pair.create(widgetItem, bitmap)),
+                        holder.previewCache.get(widgetItem));
             }
         }
     }
diff --git a/src/com/android/launcher3/widget/picker/WidgetsRowViewHolder.java b/src/com/android/launcher3/widget/picker/WidgetsRowViewHolder.java
index fe2d84b..7411459 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsRowViewHolder.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsRowViewHolder.java
@@ -16,6 +16,7 @@
 package com.android.launcher3.widget.picker;
 
 import android.graphics.Bitmap;
+import android.util.Pair;
 import android.view.View;
 
 import androidx.recyclerview.widget.RecyclerView.ViewHolder;
@@ -25,16 +26,33 @@
 
 import java.util.HashMap;
 import java.util.Map;
+import java.util.function.Consumer;
 
 /** A {@link ViewHolder} for showing widgets of an app in the full widget picker. */
 public final class WidgetsRowViewHolder extends ViewHolder {
 
     public final WidgetsListTableView tableContainer;
     public final Map<WidgetItem, Bitmap> previewCache = new HashMap<>();
+    Consumer<Pair<WidgetItem, Bitmap>> mDataCallback;
 
     public WidgetsRowViewHolder(View v) {
         super(v);
 
         tableContainer = v.findViewById(R.id.widgets_table);
     }
+
+    /**
+     * When the preview is loaded we callback to notify that the preview loaded and we rebind the
+     * view.
+     *
+     * @param data is the payload which is needed when binding the view.
+     */
+    public void onPreviewLoaded(Pair<WidgetItem, Bitmap> data) {
+        if (mDataCallback != null) {
+            mDataCallback.accept(data);
+        }
+        if (getBindingAdapter() != null) {
+            getBindingAdapter().notifyItemChanged(getBindingAdapterPosition(), data);
+        }
+    }
 }
diff --git a/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithm.java b/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithm.java
index 9be3b5f..613066a 100644
--- a/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithm.java
+++ b/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithm.java
@@ -28,7 +28,6 @@
 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;
@@ -72,7 +71,7 @@
                     List<WidgetItem> matchedWidgetItems = filterWidgetItems(
                             input, headerEntry.mPkgItem.title.toString(), headerEntry.mWidgets);
                     if (matchedWidgetItems.size() > 0) {
-                        results.add(new WidgetsListSearchHeaderEntry(headerEntry.mPkgItem,
+                        results.add(WidgetsListHeaderEntry.createForSearch(headerEntry.mPkgItem,
                                 headerEntry.mTitleSectionName, matchedWidgetItems));
                         results.add(new WidgetsListContentEntry(headerEntry.mPkgItem,
                                 headerEntry.mTitleSectionName, matchedWidgetItems));
diff --git a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
index 702f343..f490333 100644
--- a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
+++ b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
@@ -87,7 +87,7 @@
             List<WidgetItem> widgetItems = entry.getValue();
             String sectionName = (pkgItem.title == null) ? "" :
                     indexer.computeSectionName(pkgItem.title);
-            result.add(new WidgetsListHeaderEntry(pkgItem, sectionName, widgetItems));
+            result.add(WidgetsListHeaderEntry.create(pkgItem, sectionName, widgetItems));
             result.add(new WidgetsListContentEntry(pkgItem, sectionName, widgetItems));
         }
         return result;
diff --git a/tests/src/com/android/launcher3/widget/picker/WidgetsDiffReporterTest.java b/tests/src/com/android/launcher3/widget/picker/WidgetsDiffReporterTest.java
index b480a4c..8c87957 100644
--- a/tests/src/com/android/launcher3/widget/picker/WidgetsDiffReporterTest.java
+++ b/tests/src/com/android/launcher3/widget/picker/WidgetsDiffReporterTest.java
@@ -229,7 +229,7 @@
                 List.of(mHeaderA, mHeaderB, mContentE));
         // GIVEN the new list has one of the headers widgets list modified.
         List<WidgetsListBaseEntry> newList = List.of(
-                new WidgetsListHeaderEntry(
+                WidgetsListHeaderEntry.create(
                         mHeaderA.mPkgItem, mHeaderA.mTitleSectionName,
                         mHeaderA.mWidgets.subList(0, 1)),
                 mHeaderB, mContentE);
@@ -274,7 +274,7 @@
         PackageItemInfo pInfo = createPackageItemInfo(packageName, appName,
                 widgetItems.get(0).user);
 
-        return new WidgetsListHeaderEntry(pInfo, /* titleSectionName= */ "", widgetItems);
+        return WidgetsListHeaderEntry.create(pInfo, /* titleSectionName= */ "", widgetItems);
     }
 
     private WidgetsListContentEntry createWidgetsContentEntry(String packageName, String appName,
diff --git a/tests/src/com/android/launcher3/widget/picker/WidgetsListAdapterTest.java b/tests/src/com/android/launcher3/widget/picker/WidgetsListAdapterTest.java
index 4e0bdda..0044d04 100644
--- a/tests/src/com/android/launcher3/widget/picker/WidgetsListAdapterTest.java
+++ b/tests/src/com/android/launcher3/widget/picker/WidgetsListAdapterTest.java
@@ -86,7 +86,7 @@
         mTestProfile.numColumns = 5;
         mUserHandle = Process.myUserHandle();
         mAdapter = new WidgetsListAdapter(mContext, mMockLayoutInflater,
-                mIconCache, () -> 0, null, null);
+                mIconCache, () -> 0, null, null, null);
         mAdapter.registerAdapterDataObserver(mListener);
 
         doAnswer(invocation -> ((ComponentWithLabel) invocation.getArgument(0))
@@ -270,7 +270,8 @@
             pInfo.title = pInfo.packageName;
             pInfo.bitmap = BitmapInfo.of(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8), 0);
 
-            result.add(new WidgetsListHeaderEntry(pInfo, /* titleSectionName= */ "", widgetItems));
+            result.add(WidgetsListHeaderEntry.create(
+                    pInfo, /* titleSectionName= */ "", widgetItems));
             result.add(new WidgetsListContentEntry(pInfo, /* titleSectionName= */ "", widgetItems));
         }
 
diff --git a/tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java b/tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java
index 211318c..f53d15b 100644
--- a/tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java
+++ b/tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java
@@ -134,7 +134,7 @@
         appInfo.title = appName;
         appInfo.bitmap = BitmapInfo.of(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8), 0);
 
-        return new WidgetsListHeaderEntry(appInfo,
+        return WidgetsListHeaderEntry.create(appInfo,
                 /* titleSectionName= */ "",
                 generateWidgetItems(packageName, numOfWidgets));
     }
diff --git a/tests/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinderTest.java b/tests/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinderTest.java
deleted file mode 100644
index 66c2f36..0000000
--- a/tests/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinderTest.java
+++ /dev/null
@@ -1,154 +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;
-
-import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.verify;
-
-import static java.util.Collections.EMPTY_LIST;
-
-import android.appwidget.AppWidgetProviderInfo;
-import android.content.ComponentName;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.os.UserHandle;
-import android.view.LayoutInflater;
-import android.widget.FrameLayout;
-import android.widget.TextView;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.R;
-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.util.ActivityContextWrapper;
-import com.android.launcher3.util.PackageUserKey;
-import com.android.launcher3.util.WidgetUtils;
-import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
-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 java.util.ArrayList;
-import java.util.List;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public final class WidgetsListSearchHeaderViewHolderBinderTest {
-    private static final String TEST_PACKAGE = "com.google.test";
-    private static final String APP_NAME = "Test app";
-
-    private Context mContext;
-    private WidgetsListSearchHeaderViewHolderBinder mViewHolderBinder;
-    private InvariantDeviceProfile mTestProfile;
-
-    @Mock
-    private IconCache mIconCache;
-    @Mock
-    private OnHeaderClickListener mOnHeaderClickListener;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        mContext = new ActivityContextWrapper(getApplicationContext());
-        mTestProfile = new InvariantDeviceProfile();
-        mTestProfile.numRows = 5;
-        mTestProfile.numColumns = 5;
-
-        doAnswer(invocation -> {
-            ComponentWithLabel componentWithLabel = (ComponentWithLabel) invocation.getArgument(0);
-            return componentWithLabel.getComponent().getShortClassName();
-        }).when(mIconCache).getTitleNoCache(any());
-        mViewHolderBinder = new WidgetsListSearchHeaderViewHolderBinder(
-                LayoutInflater.from(mContext),
-                mOnHeaderClickListener,
-                new WidgetsListDrawableFactory(mContext));
-    }
-
-    @Test
-    public void bindViewHolder_appWith3Widgets_shouldShowTheCorrectAppNameAndSubtitle() {
-        WidgetsListSearchHeaderHolder viewHolder = mViewHolderBinder.newViewHolder(
-                new FrameLayout(mContext));
-        WidgetsListHeader widgetsListHeader = viewHolder.mWidgetsListHeader;
-        WidgetsListSearchHeaderEntry entry = generateSampleSearchHeader(
-                APP_NAME,
-                TEST_PACKAGE,
-                /* numOfWidgets= */ 3);
-        mViewHolderBinder.bindViewHolder(viewHolder, entry, /* position= */ 0, EMPTY_LIST);
-
-        TextView appTitle = widgetsListHeader.findViewById(R.id.app_title);
-        TextView appSubtitle = widgetsListHeader.findViewById(R.id.app_subtitle);
-        assertThat(appTitle.getText()).isEqualTo(APP_NAME);
-        assertThat(appSubtitle.getText())
-                .isEqualTo(".SampleWidget0, .SampleWidget1, .SampleWidget2");
-    }
-
-    @Test
-    public void bindViewHolder_shouldAttachOnHeaderClickListener() {
-        WidgetsListSearchHeaderHolder viewHolder = mViewHolderBinder.newViewHolder(
-                new FrameLayout(mContext));
-        WidgetsListHeader widgetsListHeader = viewHolder.mWidgetsListHeader;
-        WidgetsListSearchHeaderEntry entry = generateSampleSearchHeader(
-                APP_NAME,
-                TEST_PACKAGE,
-                /* numOfWidgets= */ 3);
-
-        mViewHolderBinder.bindViewHolder(viewHolder, entry, /* position= */ 0, EMPTY_LIST);
-        widgetsListHeader.callOnClick();
-
-        verify(mOnHeaderClickListener).onHeaderClicked(eq(true),
-                eq(PackageUserKey.fromPackageItemInfo(entry.mPkgItem)));
-    }
-
-    private WidgetsListSearchHeaderEntry generateSampleSearchHeader(String appName,
-            String packageName, int numOfWidgets) {
-        PackageItemInfo appInfo = new PackageItemInfo(packageName, UserHandle.CURRENT);
-        appInfo.title = appName;
-        appInfo.bitmap = BitmapInfo.of(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8), 0);
-
-        return new WidgetsListSearchHeaderEntry(appInfo,
-                /* titleSectionName= */ "",
-                generateWidgetItems(packageName, numOfWidgets));
-    }
-
-    private List<WidgetItem> generateWidgetItems(String packageName, int numOfWidgets) {
-        ArrayList<WidgetItem> widgetItems = new ArrayList<>();
-        for (int i = 0; i < numOfWidgets; i++) {
-            ComponentName cn = ComponentName.createRelative(packageName, ".SampleWidget" + i);
-            AppWidgetProviderInfo widgetInfo = WidgetUtils.createAppWidgetProviderInfo(cn);
-
-            widgetItems.add(new WidgetItem(
-                    LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, widgetInfo),
-                    mTestProfile, mIconCache));
-        }
-        return widgetItems;
-    }
-}
diff --git a/tests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithmTest.java b/tests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithmTest.java
index d812ab0..0124f73 100644
--- a/tests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithmTest.java
+++ b/tests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithmTest.java
@@ -50,7 +50,6 @@
 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;
@@ -117,12 +116,12 @@
                 .getAllWidgets();
 
         assertEquals(List.of(
-                new WidgetsListSearchHeaderEntry(
+                WidgetsListHeaderEntry.createForSearch(
                         mCalendarHeaderEntry.mPkgItem,
                         mCalendarHeaderEntry.mTitleSectionName,
                         mCalendarHeaderEntry.mWidgets),
                 mCalendarContentEntry,
-                new WidgetsListSearchHeaderEntry(
+                WidgetsListHeaderEntry.createForSearch(
                         mCameraHeaderEntry.mPkgItem,
                         mCameraHeaderEntry.mTitleSectionName,
                         mCameraHeaderEntry.mWidgets),
@@ -138,7 +137,7 @@
                 .getAllWidgets();
 
         assertEquals(List.of(
-                new WidgetsListSearchHeaderEntry(
+                WidgetsListHeaderEntry.createForSearch(
                         mCalendarHeaderEntry.mPkgItem,
                         mCalendarHeaderEntry.mTitleSectionName,
                         mCalendarHeaderEntry.mWidgets.subList(1, 2)),
@@ -146,7 +145,7 @@
                         mCalendarHeaderEntry.mPkgItem,
                         mCalendarHeaderEntry.mTitleSectionName,
                         mCalendarHeaderEntry.mWidgets.subList(1, 2)),
-                new WidgetsListSearchHeaderEntry(
+                WidgetsListHeaderEntry.createForSearch(
                         mCameraHeaderEntry.mPkgItem,
                         mCameraHeaderEntry.mTitleSectionName,
                         mCameraHeaderEntry.mWidgets.subList(1, 3)),
@@ -175,7 +174,7 @@
         PackageItemInfo pInfo = createPackageItemInfo(packageName, appName,
                 widgetItems.get(0).user);
 
-        return new WidgetsListHeaderEntry(pInfo, /* titleSectionName= */ "", widgetItems);
+        return WidgetsListHeaderEntry.create(pInfo, /* titleSectionName= */ "", widgetItems);
     }
 
     private WidgetsListContentEntry createWidgetsContentEntry(String packageName, String appName,