Merge "[LSC] Add LOCAL_LICENSE_KINDS to packages/apps/Launcher3" into sc-dev
diff --git a/go/quickstep/overview_ui_overrides/res/layout/overview_actions_container.xml b/go/quickstep/overview_ui_overrides/res/layout/overview_actions_container.xml
index b438da3..e6af848 100644
--- a/go/quickstep/overview_ui_overrides/res/layout/overview_actions_container.xml
+++ b/go/quickstep/overview_ui_overrides/res/layout/overview_actions_container.xml
@@ -17,14 +17,14 @@
 <com.android.quickstep.views.GoOverviewActionsView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="@dimen/overview_actions_height"
+    android:layout_height="wrap_content"
     android:layout_gravity="center_horizontal|bottom">
 
     <LinearLayout
         android:id="@+id/action_buttons"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center"
+        android:layout_height="@dimen/overview_actions_height"
+        android:layout_gravity="bottom|center_horizontal"
         android:orientation="horizontal">
 
         <Space
diff --git a/quickstep/overview_ui_overrides/res/layout/overview_actions_container.xml b/quickstep/overview_ui_overrides/res/layout/overview_actions_container.xml
index d15a2d2..1da25e7 100644
--- a/quickstep/overview_ui_overrides/res/layout/overview_actions_container.xml
+++ b/quickstep/overview_ui_overrides/res/layout/overview_actions_container.xml
@@ -16,14 +16,14 @@
 -->
 <com.android.quickstep.views.OverviewActionsView xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="@dimen/overview_actions_height"
+    android:layout_height="wrap_content"
     android:layout_gravity="center_horizontal|bottom">
 
     <LinearLayout
         android:id="@+id/action_buttons"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center"
+        android:layout_height="@dimen/overview_actions_height"
+        android:layout_gravity="bottom|center_horizontal"
         android:orientation="horizontal">
 
         <Space
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index 20a645e..c4becf1 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -95,9 +95,6 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        mAppTransitionManager = new QuickstepTransitionManager(this);
-        mAppTransitionManager.registerRemoteAnimations();
-
         SysUINavigationMode.INSTANCE.get(this).addModeChangeListener(this);
         addMultiWindowModeChangedListener(mDepthController);
     }
@@ -225,6 +222,9 @@
         overviewPanel.init(mActionsView, splitPlaceholderView);
         mActionsView.updateVerticalMargin(SysUINavigationMode.getMode(this));
 
+        mAppTransitionManager = new QuickstepTransitionManager(this);
+        mAppTransitionManager.registerRemoteAnimations();
+
         addTaskbarIfNecessary();
         addOnDeviceProfileChangeListener(newDp -> addTaskbarIfNecessary());
     }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java
index a2b2ddd..f652961 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java
@@ -472,7 +472,7 @@
     public void alignRealHotseatWithTaskbar() {
         Rect hotseatBounds = new Rect();
         DeviceProfile grid = mLauncher.getDeviceProfile();
-        int hotseatHeight = grid.workspacePadding.bottom + grid.getInsets().bottom;
+        int hotseatHeight = grid.workspacePadding.bottom + grid.taskbarSize;
         int hotseatTopDiff = hotseatHeight - grid.taskbarSize;
 
         mTaskbarView.getHotseatBoundsAtScale(getTaskbarScaleOnHome()).roundOut(hotseatBounds);
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 6da2201..3094500 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -1334,7 +1334,11 @@
     }
 
     private void invalidateHandler() {
-        mInputConsumerProxy.destroy();
+        if (!LIVE_TILE.get() || !mActivityInterface.isInLiveTileMode()
+                || mGestureState.getEndTarget() != RECENTS) {
+            mInputConsumerProxy.destroy();
+            mTaskAnimationManager.setLiveTileCleanUpHandler(null);
+        }
         endRunningWindowAnim(false /* cancel */);
 
         if (mGestureEndCallback != null) {
@@ -1526,6 +1530,7 @@
                         apps[apps.length - 1] = appearedTaskTarget;
                         launchOtherTaskInLiveTileMode(appearedTaskTarget.taskId, apps);
                     });
+            mTaskAnimationManager.setLiveTileCleanUpHandler(mInputConsumerProxy::destroy);
             ActivityManagerWrapper.getInstance().registerTaskStackListener(
                     mLiveTileRestartListener);
         }
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index 02c2763..9a454f2 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -50,6 +50,7 @@
     private GestureState mLastGestureState;
     private RemoteAnimationTargetCompat mLastAppearedTaskTarget;
     private Consumer<RemoteAnimationTargetCompat> mLaunchOtherTaskHandler;
+    private Runnable mLiveTileCleanUpHandler;
     private Context mCtx;
 
     TaskAnimationManager(Context ctx) {
@@ -169,6 +170,10 @@
         mLaunchOtherTaskHandler = handler;
     }
 
+    public void setLiveTileCleanUpHandler(Runnable runnable) {
+        mLiveTileCleanUpHandler = runnable;
+    }
+
     /**
      * Finishes the running recents animation.
      */
@@ -206,6 +211,11 @@
      * Cleans up the recents animation entirely.
      */
     private void cleanUpRecentsAnimation() {
+        if (mLiveTileCleanUpHandler != null) {
+            mLiveTileCleanUpHandler.run();
+            mLiveTileCleanUpHandler = null;
+        }
+
         // Release all the target leashes
         if (mTargets != null) {
             mTargets.release();
diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
index 17d075f..2b54f95 100644
--- a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -550,23 +550,26 @@
 
                 // Update the clip hints. Align to 0,0, crop the remaining.
                 if (isRtl) {
+                    thumbnailClipHint.left += availableWidth - croppedWidth;
                     if (thumbnailClipHint.right < 0) {
                         thumbnailClipHint.left += thumbnailClipHint.right;
+                        thumbnailClipHint.right = 0;
                     }
-                    thumbnailClipHint.right = 0;
-                    thumbnailClipHint.left += availableWidth - croppedWidth;
                 } else {
+                    thumbnailClipHint.right += availableWidth - croppedWidth;
                     if (thumbnailClipHint.left < 0) {
                         thumbnailClipHint.right += thumbnailClipHint.left;
+                        thumbnailClipHint.left = 0;
                     }
-                    thumbnailClipHint.left = 0;
-                    thumbnailClipHint.right += availableWidth - croppedWidth;
                 }
+                thumbnailClipHint.bottom += availableHeight - croppedHeight;
                 if (thumbnailClipHint.top < 0) {
                     thumbnailClipHint.bottom += thumbnailClipHint.top;
+                    thumbnailClipHint.top = 0;
+                } else if (thumbnailClipHint.bottom < 0) {
+                    thumbnailClipHint.top += thumbnailClipHint.bottom;
+                    thumbnailClipHint.bottom = 0;
                 }
-                thumbnailClipHint.top = 0;
-                thumbnailClipHint.bottom += availableHeight - croppedHeight;
 
                 thumbnailScale = targetW / (croppedWidth * scale);
             }
diff --git a/res/drawable/bg_widgets_searchbox.xml b/res/drawable/bg_widgets_searchbox.xml
index 81dd2aa..2a50a51 100644
--- a/res/drawable/bg_widgets_searchbox.xml
+++ b/res/drawable/bg_widgets_searchbox.xml
@@ -14,6 +14,6 @@
      limitations under the License.
 -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
-    <solid android:color="#FFFFF7" />
+    <solid android:color="?android:attr/textColorPrimaryInverse" />
     <corners android:radius="24dp" />
 </shape>
\ No newline at end of file
diff --git a/res/drawable/middle_item_primary.xml b/res/drawable/middle_item_primary.xml
new file mode 100644
index 0000000..c975714
--- /dev/null
+++ b/res/drawable/middle_item_primary.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <solid android:color="?attr/popupColorPrimary"/>
+    <corners android:radius="@dimen/popup_middle_item_radius" />
+</shape>
\ No newline at end of file
diff --git a/res/drawable/single_item_primary.xml b/res/drawable/single_item_primary.xml
new file mode 100644
index 0000000..1c0889b
--- /dev/null
+++ b/res/drawable/single_item_primary.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <solid android:color="?attr/popupColorPrimary"/>
+    <corners android:radius="@dimen/popup_single_item_radius" />
+</shape>
\ No newline at end of file
diff --git a/res/drawable/single_item_secondary.xml b/res/drawable/single_item_secondary.xml
new file mode 100644
index 0000000..4edc481
--- /dev/null
+++ b/res/drawable/single_item_secondary.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <solid android:color="?attr/popupColorSecondary"/>
+    <corners android:radius="@dimen/popup_single_item_radius" />
+</shape>
\ No newline at end of file
diff --git a/res/layout/deep_shortcut.xml b/res/layout/deep_shortcut.xml
index 840a8b7..d6b4a37 100644
--- a/res/layout/deep_shortcut.xml
+++ b/res/layout/deep_shortcut.xml
@@ -19,6 +19,7 @@
     xmlns:launcher="http://schemas.android.com/apk/res-auto"
     android:layout_width="@dimen/bg_popup_item_width"
     android:layout_height="@dimen/bg_popup_item_height"
+    android:background="@drawable/middle_item_primary"
     android:theme="@style/PopupItem" >
 
     <com.android.launcher3.shortcuts.DeepShortcutTextView
@@ -45,12 +46,4 @@
         android:layout_gravity="start|center_vertical"
         android:background="@drawable/ic_deepshortcut_placeholder"/>
 
-    <View
-        android:id="@+id/divider"
-        android:layout_width="@dimen/deep_shortcuts_divider_width"
-        android:layout_height="@dimen/popup_item_divider_height"
-        android:layout_gravity="end|bottom"
-        android:visibility="gone"
-        android:background="?attr/popupColorTertiary" />
-
 </com.android.launcher3.shortcuts.DeepShortcutView>
diff --git a/res/layout/longpress_options_menu.xml b/res/layout/longpress_options_menu.xml
index 20bb5b8..3898365 100644
--- a/res/layout/longpress_options_menu.xml
+++ b/res/layout/longpress_options_menu.xml
@@ -18,7 +18,6 @@
     android:id="@+id/deep_shortcuts_container"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:background="?attr/popupColorPrimary"
     android:clipToPadding="false"
     android:clipChildren="false"
     android:elevation="@dimen/deep_shortcuts_elevation"
diff --git a/res/layout/notification_content.xml b/res/layout/notification_content.xml
index d01be01..147aa30 100644
--- a/res/layout/notification_content.xml
+++ b/res/layout/notification_content.xml
@@ -96,14 +96,6 @@
 
     </com.android.launcher3.notification.NotificationMainView>
 
-    <!-- Divider -->
-    <View
-        android:id="@+id/divider"
-        android:layout_width="match_parent"
-        android:layout_height="@dimen/popup_item_divider_height"
-        android:layout_below="@id/main_view"
-        android:background="?attr/popupColorTertiary" />
-
     <!-- Footer -->
     <com.android.launcher3.notification.NotificationFooterLayout
         android:id="@+id/footer"
diff --git a/res/layout/notification_gutter.xml b/res/layout/notification_gutter.xml
index 10e7f7d..9a3e55a 100644
--- a/res/layout/notification_gutter.xml
+++ b/res/layout/notification_gutter.xml
@@ -16,6 +16,5 @@
 <View
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="4dp"
-    android:layout_marginTop="4dp"
-    android:background="@drawable/bg_notification_content" />
\ No newline at end of file
+    android:layout_height="0dp"
+    android:layout_marginTop="@dimen/popup_margin" />
\ No newline at end of file
diff --git a/res/layout/popup_container.xml b/res/layout/popup_container.xml
index c737407..04822fd 100644
--- a/res/layout/popup_container.xml
+++ b/res/layout/popup_container.xml
@@ -19,8 +19,15 @@
     android:id="@+id/deep_shortcuts_container"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:background="?attr/popupColorPrimary"
     android:clipToPadding="false"
     android:clipChildren="false"
     android:elevation="@dimen/deep_shortcuts_elevation"
-    android:orientation="vertical" />
\ No newline at end of file
+    android:orientation="vertical">
+    <LinearLayout
+        android:id="@+id/notification_container"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:visibility="gone"
+        android:background="?attr/popupColorPrimary"
+        android:orientation="vertical"/>
+</com.android.launcher3.popup.PopupContainerWithArrow>
\ No newline at end of file
diff --git a/res/layout/system_shortcut.xml b/res/layout/system_shortcut.xml
index c620e2a..68251e4 100644
--- a/res/layout/system_shortcut.xml
+++ b/res/layout/system_shortcut.xml
@@ -19,6 +19,7 @@
     xmlns:launcher="http://schemas.android.com/apk/res-auto"
     android:layout_width="@dimen/bg_popup_item_width"
     android:layout_height="@dimen/bg_popup_item_height"
+    android:background="@drawable/middle_item_primary"
     android:theme="@style/PopupItem" >
 
     <com.android.launcher3.BubbleTextView
@@ -44,12 +45,4 @@
         android:layout_gravity="start|center_vertical"
         android:backgroundTint="?android:attr/textColorTertiary"/>
 
-    <View
-        android:id="@+id/divider"
-        android:layout_width="@dimen/deep_shortcuts_divider_width"
-        android:layout_height="@dimen/popup_item_divider_height"
-        android:layout_gravity="end|bottom"
-        android:visibility="gone"
-        android:background="?attr/popupColorTertiary" />
-
 </com.android.launcher3.shortcuts.DeepShortcutView>
diff --git a/res/layout/system_shortcut_icons.xml b/res/layout/system_shortcut_icons.xml
index a340f4f..f992248 100644
--- a/res/layout/system_shortcut_icons.xml
+++ b/res/layout/system_shortcut_icons.xml
@@ -21,7 +21,7 @@
     android:layout_height="@dimen/system_shortcut_header_height"
     android:orientation="horizontal"
     android:gravity="end|center_vertical"
-    android:background="?attr/popupColorSecondary"
+    android:background="@drawable/single_item_secondary"
     android:clipToPadding="true">
 
     <Space android:layout_width="0dp"
diff --git a/res/layout/user_folder_icon_normalized.xml b/res/layout/user_folder_icon_normalized.xml
index d38a77a..8b18857 100644
--- a/res/layout/user_folder_icon_normalized.xml
+++ b/res/layout/user_folder_icon_normalized.xml
@@ -19,7 +19,6 @@
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:background="@drawable/round_rect_folder"
-    android:elevation="5dp"
     android:orientation="vertical" >
 
     <com.android.launcher3.folder.FolderPagedView
diff --git a/res/layout/widget_cell_content.xml b/res/layout/widget_cell_content.xml
index 50908a4..a3d0070 100644
--- a/res/layout/widget_cell_content.xml
+++ b/res/layout/widget_cell_content.xml
@@ -18,12 +18,14 @@
     android:layout_height="wrap_content">
 
     <!-- The image of the widget. This view does not support padding. Any placement adjustment
-         should be done using margins. -->
+         should be done using margins.
+          width & height are set at runtime after scaling the preview image. -->
     <com.android.launcher3.widget.WidgetImageView
         android:id="@+id/widget_preview"
-        android:layout_width="wrap_content"
+        android:layout_width="0dp"
         android:layout_height="0dp"
         android:layout_weight="1"
+        android:importantForAccessibility="no"
         android:layout_marginVertical="8dp" />
 
     <!-- The name of the widget. -->
@@ -37,7 +39,7 @@
         android:singleLine="true"
         android:maxLines="1"
         android:textColor="?android:attr/textColorPrimary"
-        android:textSize="14sp" />
+        android:textSize="@dimen/widget_cell_font_size" />
 
     <!-- The original dimensions of the widget (can't be the same text as above due to different
          style. -->
@@ -47,7 +49,7 @@
         android:layout_height="wrap_content"
         android:gravity="center_horizontal"
         android:textColor="?android:attr/textColorTertiary"
-        android:textSize="14sp"
+        android:textSize="@dimen/widget_cell_font_size"
         android:alpha="0.8" />
 
     <TextView
@@ -55,7 +57,7 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:gravity="center_horizontal"
-        android:textSize="14sp"
+        android:textSize="@dimen/widget_cell_font_size"
         android:textColor="?android:attr/textColorTertiary"
         android:maxLines="2"
         android:ellipsize="end"
diff --git a/res/layout/widgets_full_sheet_paged_view.xml b/res/layout/widgets_full_sheet_paged_view.xml
index 8125db8..ae877d4 100644
--- a/res/layout/widgets_full_sheet_paged_view.xml
+++ b/res/layout/widgets_full_sheet_paged_view.xml
@@ -16,11 +16,6 @@
 <merge xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:launcher="http://schemas.android.com/apk/res-auto">
 
-    <include layout="@layout/personal_work_tabs"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginHorizontal="16dp" />
-
     <com.android.launcher3.workprofile.PersonalWorkPagedView
         android:id="@+id/widgets_view_pager"
         android:layout_width="match_parent"
@@ -43,4 +38,8 @@
 
     </com.android.launcher3.workprofile.PersonalWorkPagedView>
 
+    <include layout="@layout/personal_work_tabs"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginHorizontal="16dp" />
 </merge>
\ No newline at end of file
diff --git a/res/layout/widgets_full_sheet_search_and_recommendations.xml b/res/layout/widgets_full_sheet_search_and_recommendations.xml
index 1219f57..e5df175 100644
--- a/res/layout/widgets_full_sheet_search_and_recommendations.xml
+++ b/res/layout/widgets_full_sheet_search_and_recommendations.xml
@@ -18,12 +18,15 @@
     android:id="@+id/search_and_recommendations_container"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:padding="16dp"
+    android:paddingHorizontal="16dp"
+    android:layout_marginBottom="16dp"
     android:orientation="vertical">
     <View
         android:id="@+id/collapse_handle"
         android:layout_width="48dp"
         android:layout_height="2dp"
+        android:layout_marginTop="16dp"
+        android:elevation="2dp"
         android:layout_gravity="center_horizontal"
         android:background="?android:attr/textColorSecondary"/>
     <TextView
@@ -36,4 +39,11 @@
         android:textColor="?android:attr/textColorSecondary"
         android:text="@string/widget_button_text"/>
     <include layout="@layout/widgets_search_bar"/>
+
+    <com.android.launcher3.widget.picker.WidgetsRecommendationTableLayout
+        android:id="@+id/recommended_widget_table"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="16dp"
+        android:visibility="gone" />
 </LinearLayout>
diff --git a/res/layout/widgets_list_row_header.xml b/res/layout/widgets_list_row_header.xml
index 62345b3..ed3a042 100644
--- a/res/layout/widgets_list_row_header.xml
+++ b/res/layout/widgets_list_row_header.xml
@@ -71,6 +71,7 @@
         android:layout_gravity="center_vertical"
         android:layout_alignParentEnd="true"
         android:clickable="false"
+        android:importantForAccessibility="no"
         android:button="@drawable/widgets_tray_expand_button"/>
 
 </com.android.launcher3.widget.picker.WidgetsListHeader>
\ No newline at end of file
diff --git a/res/layout/widgets_search_bar.xml b/res/layout/widgets_search_bar.xml
index cf693bb..1db7462 100644
--- a/res/layout/widgets_search_bar.xml
+++ b/res/layout/widgets_search_bar.xml
@@ -6,7 +6,8 @@
     android:layout_height="wrap_content"
     android:orientation="horizontal"
     android:layout_marginTop="16dp"
-    android:background="@drawable/bg_widgets_searchbox">
+    android:background="@drawable/bg_widgets_searchbox"
+    android:elevation="2dp">
 
     <com.android.launcher3.ExtendedEditText
         android:id="@+id/widgets_search_bar_edit_text"
@@ -19,12 +20,15 @@
         android:hint="@string/widgets_full_sheet_search_bar_hint"
         android:maxLines="1"
         android:layout_weight="1"
-        android:inputType="text"/>
+        android:inputType="text"
+        android:textColor="?android:attr/textColorSecondary"
+        android:textColorHint="?android:attr/textColorTertiary"/>
 
     <ImageButton
         android:id="@+id/widgets_search_cancel_button"
         android:layout_height="wrap_content"
         android:layout_width="wrap_content"
+        android:padding="8dp"
         android:src="@drawable/ic_gm_close_24"
         android:background="?android:selectableItemBackground"
         android:layout_gravity="center"
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 1bace48..64c07d0 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -109,6 +109,7 @@
 <!-- Widget tray -->
     <dimen name="widget_cell_vertical_padding">8dp</dimen>
     <dimen name="widget_cell_horizontal_padding">16dp</dimen>
+    <dimen name="widget_cell_font_size">14sp</dimen>
 
 
     <dimen name="widget_list_top_bottom_corner_radius">28dp</dimen>
@@ -183,16 +184,17 @@
     <dimen name="pending_widget_elevation">2dp</dimen>
 
 <!-- Deep shortcuts -->
-    <dimen name="deep_shortcuts_elevation">9dp</dimen>
-    <!-- also update deep_shortcuts_divider_width -->
+    <dimen name="deep_shortcuts_elevation">0dp</dimen>
     <dimen name="bg_popup_item_width">234dp</dimen>
     <dimen name="bg_popup_item_height">56dp</dimen>
-    <dimen name="bg_popup_item_condensed_height">48dp</dimen>
     <dimen name="pre_drag_view_scale">6dp</dimen>
     <!-- an icon with shortcuts must be dragged this far before the container is removed. -->
     <dimen name="deep_shortcuts_start_drag_threshold">16dp</dimen>
-    <dimen name="deep_shortcut_icon_size">36dp</dimen>
-    <dimen name="deep_shortcut_drawable_padding">8dp</dimen>
+    <dimen name="deep_shortcut_icon_size">32dp</dimen>
+    <dimen name="popup_margin">2dp</dimen>
+    <dimen name="popup_single_item_radius">100dp</dimen>
+    <dimen name="popup_middle_item_radius">4dp</dimen>
+    <dimen name="deep_shortcut_drawable_padding">12dp</dimen>
     <dimen name="deep_shortcut_drag_handle_size">16dp</dimen>
     <dimen name="popup_padding_start">10dp</dimen>
     <dimen name="popup_padding_end">16dp</dimen>
@@ -201,16 +203,14 @@
     <dimen name="popup_arrow_height">10dp</dimen>
     <dimen name="popup_arrow_vertical_offset">-1dp</dimen>
     <!-- popup_padding_start + deep_shortcut_icon_size / 2 -->
-    <dimen name="popup_arrow_horizontal_center_offset">28dp</dimen>
+    <dimen name="popup_arrow_horizontal_center_offset">26dp</dimen>
     <dimen name="popup_arrow_corner_radius">2dp</dimen>
     <!-- popup_padding_start + icon_size + 10dp -->
-    <dimen name="deep_shortcuts_text_padding_start">56dp</dimen>
-    <!-- popup_item_width - deep_shortcuts_text_padding_start -->
-    <dimen name="deep_shortcuts_divider_width">178dp</dimen>
+    <dimen name="deep_shortcuts_text_padding_start">52dp</dimen>
     <dimen name="system_shortcut_icon_size">24dp</dimen>
-    <!-- popup_arrow_center_start - system_shortcut_icon_size / 2 -->
+    <!-- popup_arrow_horizontal_center_offset - system_shortcut_icon_size / 2 -->
     <dimen name="system_shortcut_margin_start">16dp</dimen>
-    <dimen name="system_shortcut_header_height">48dp</dimen>
+    <dimen name="system_shortcut_header_height">56dp</dimen>
     <dimen name="system_shortcut_header_icon_touch_size">48dp</dimen>
     <!-- (touch_size - icon_size) / 2 -->
     <dimen name="system_shortcut_header_icon_padding">12dp</dimen>
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index cc4bfe8..9df8d44 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -181,8 +181,6 @@
     private final ArrayList<View> mIntersectingViews = new ArrayList<>();
     private final Rect mOccupiedRect = new Rect();
     private final int[] mDirectionVector = new int[2];
-    private final Workspace mWorkspace;
-    private final DeviceProfile mDeviceProfile;
 
     final int[] mPreviousReorderDirection = new int[2];
     private static final int INVALID_DIRECTION = -100;
@@ -213,15 +211,14 @@
         setWillNotDraw(false);
         setClipToPadding(false);
         mActivity = ActivityContext.lookupContext(context);
-        mWorkspace = Launcher.cast(mActivity).getWorkspace();
-        mDeviceProfile = mActivity.getDeviceProfile();
+        DeviceProfile deviceProfile = mActivity.getDeviceProfile();
 
-        mBorderSpacing = mDeviceProfile.cellLayoutBorderSpacingPx;
+        mBorderSpacing = deviceProfile.cellLayoutBorderSpacingPx;
         mCellWidth = mCellHeight = -1;
         mFixedCellWidth = mFixedCellHeight = -1;
 
-        mCountX = mDeviceProfile.inv.numColumns;
-        mCountY = mDeviceProfile.inv.numRows;
+        mCountX = deviceProfile.inv.numColumns;
+        mCountY = deviceProfile.inv.numRows;
         mOccupied =  new GridOccupancy(mCountX, mCountY);
         mTmpOccupied = new GridOccupancy(mCountX, mCountY);
 
@@ -238,7 +235,7 @@
         mBackground.setCallback(this);
         mBackground.setAlpha(0);
 
-        mReorderPreviewAnimationMagnitude = (REORDER_PREVIEW_MAGNITUDE * mDeviceProfile.iconSizePx);
+        mReorderPreviewAnimationMagnitude = (REORDER_PREVIEW_MAGNITUDE * deviceProfile.iconSizePx);
 
         // Initialize the data structures used for the drag visualization.
         mEaseOutInterpolator = Interpolators.DEACCEL_2_5; // Quint ease out
@@ -1024,8 +1021,10 @@
         // Apply local extracted color if the DragView is an AppWidgetHostViewDrawable.
         Drawable drawable = dragObject.dragView.getDrawable();
         if (drawable instanceof AppWidgetHostViewDrawable) {
-            int screenId = mWorkspace.getIdForScreen(this);
-            int pageId = mWorkspace.getPageIndexForScreenId(screenId);
+            Workspace workspace =
+                    Launcher.getLauncher(dragObject.dragView.getContext()).getWorkspace();
+            int screenId = workspace.getIdForScreen(this);
+            int pageId = workspace.getPageIndexForScreenId(screenId);
             AppWidgetHostViewDrawable hostViewDrawable = ((AppWidgetHostViewDrawable) drawable);
             cellToRect(targetCell[0], targetCell[1], spanX, spanY, mTempRect);
             hostViewDrawable.getAppWidgetHostView().handleDrag(mTempRect, pageId);
@@ -2097,7 +2096,7 @@
     private void commitTempPlacement() {
         mTmpOccupied.copyTo(mOccupied);
 
-        int screenId = mWorkspace.getIdForScreen(this);
+        int screenId = Launcher.cast(mActivity).getWorkspace().getIdForScreen(this);
         int container = Favorites.CONTAINER_DESKTOP;
 
         if (mContainerType == HOTSEAT) {
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index 0e9de45..af4a843 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -29,7 +29,6 @@
 
 import androidx.annotation.Nullable;
 
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.util.MultiValueAlpha;
 
 import java.util.function.Consumer;
@@ -96,7 +95,7 @@
         if (hasVerticalHotseat) {
             setGridSize(1, idp.numHotseatIcons);
         } else {
-            setGridSize(idp.numHotseatIcons, FeatureFlags.ENABLE_DEVICE_SEARCH.get() ? 2 : 1);
+            setGridSize(idp.numHotseatIcons, 1);
         }
         showInlineQsb();
     }
@@ -123,18 +122,14 @@
             lp.height = (grid.isTaskbarPresent
                         ? grid.workspacePadding.bottom
                         : grid.hotseatBarSizePx)
-                    + insets.bottom;
+                    + (grid.isTaskbarPresent ? grid.taskbarSize : insets.bottom);
         }
 
         if (!grid.isTaskbarPresent) {
             // When taskbar is present, we set the padding separately to ensure a seamless visual
             // handoff between taskbar and hotseat during drag and drop.
             Rect padding = grid.getHotseatLayoutPadding();
-            int paddingBottom = padding.bottom;
-            if (FeatureFlags.ENABLE_DEVICE_SEARCH.get() && !grid.isVerticalBarLayout()) {
-                paddingBottom -= grid.hotseatBarBottomPaddingPx;
-            }
-            setPadding(padding.left, padding.top, padding.right, paddingBottom);
+            setPadding(padding.left, padding.top, padding.right, padding.bottom);
         }
 
         setLayoutParams(lp);
@@ -214,7 +209,7 @@
                 : dp.hotseatBarSizePx - dp.hotseatCellHeightPx - mQsbHeight;
         int bottom = b - t
                 - (int) (freeSpace * QSB_CENTER_FACTOR)
-                - dp.getInsets().bottom;
+                - (dp.isTaskbarPresent ? dp.taskbarSize : dp.getInsets().bottom);
         int top = bottom - mQsbHeight;
         mQsb.layout(left, top, right, bottom);
     }
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index c84724f..6a16da9 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -1455,7 +1455,7 @@
             // TAPL can work only if UIDevice is set up as setCompressedLayoutHeirarchy(false).
             // Hiding workspace from the tests when it's
             // IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS.
-            return null;
+            return AccessibilityNodeInfo.obtain();
         }
         return super.createAccessibilityNodeInfo();
     }
diff --git a/src/com/android/launcher3/allapps/search/SearchWidgetInfoContainer.java b/src/com/android/launcher3/allapps/search/SearchWidgetInfoContainer.java
deleted file mode 100644
index 8e5f8cb..0000000
--- a/src/com/android/launcher3/allapps/search/SearchWidgetInfoContainer.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2020 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.allapps.search;
-
-import android.appwidget.AppWidgetHostView;
-import android.appwidget.AppWidgetProviderInfo;
-import android.content.Context;
-import android.widget.RemoteViews;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A placeholder {@link AppWidgetHostView} used for managing widget search results
- */
-public class SearchWidgetInfoContainer extends AppWidgetHostView {
-    private int mAppWidgetId;
-    private AppWidgetProviderInfo mProviderInfo;
-    private RemoteViews mViews;
-    private List<AppWidgetHostView> mListeners = new ArrayList<>();
-
-    public SearchWidgetInfoContainer(Context context) {
-        super(context);
-    }
-
-    @Override
-    public void setAppWidget(int appWidgetId, AppWidgetProviderInfo info) {
-        mAppWidgetId = appWidgetId;
-        mProviderInfo = info;
-        for (AppWidgetHostView listener : mListeners) {
-            listener.setAppWidget(mAppWidgetId, mProviderInfo);
-        }
-    }
-
-    @Override
-    public void updateAppWidget(RemoteViews remoteViews) {
-        mViews = remoteViews;
-        for (AppWidgetHostView listener : mListeners) {
-            listener.updateAppWidget(remoteViews);
-        }
-    }
-
-    /**
-     * Create a live {@link AppWidgetHostView} from placeholder
-     */
-    public void attachWidget(AppWidgetHostView hv) {
-        hv.setTag(getTag());
-        hv.setAppWidget(mAppWidgetId, mProviderInfo);
-        hv.updateAppWidget(mViews);
-        mListeners.add(hv);
-    }
-
-    /**
-     * stops AppWidgetHostView from getting updates
-     */
-    public void detachWidget(AppWidgetHostView hostView) {
-        mListeners.remove(hostView);
-    }
-
-}
diff --git a/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java b/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
index 5954efa..c67efef 100644
--- a/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
+++ b/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
@@ -97,7 +97,7 @@
 
         double thetaShift = 0;
         if (curNumItems == 3) {
-            thetaShift = Math.PI / 6;
+            thetaShift = Math.PI / 2;
         } else if (curNumItems == 4) {
             thetaShift = Math.PI / 4;
         }
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index bcb3a54..ec7155c 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -26,6 +26,7 @@
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_CONVERTED_TO_ICON;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_LABEL_UPDATED;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED;
+import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -695,6 +696,9 @@
 
         mPageIndicator.stopAllAnimations();
         startAnimation(anim);
+        // Because t=0 has the folder match the folder icon, we can skip the
+        // first frame and have the same movement one frame earlier.
+        anim.setCurrentPlayTime(Math.min(getSingleFrameMs(getContext()), anim.getTotalDuration()));
 
         // Make sure the folder picks up the last drag move even if the finger doesn't move.
         if (mDragController.isDragging()) {
diff --git a/src/com/android/launcher3/notification/NotificationItemView.java b/src/com/android/launcher3/notification/NotificationItemView.java
index 0320aa3..e954480 100644
--- a/src/com/android/launcher3/notification/NotificationItemView.java
+++ b/src/com/android/launcher3/notification/NotificationItemView.java
@@ -21,10 +21,13 @@
 import android.app.Notification;
 import android.content.Context;
 import android.graphics.Color;
+import android.graphics.Outline;
 import android.graphics.Rect;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewGroup;
 import android.view.ViewGroup.MarginLayoutParams;
+import android.view.ViewOutlineProvider;
 import android.widget.TextView;
 
 import com.android.launcher3.R;
@@ -43,7 +46,8 @@
     private static final Rect sTempRect = new Rect();
 
     private final Context mContext;
-    private final PopupContainerWithArrow mContainer;
+    private final PopupContainerWithArrow mPopupContainer;
+    private final ViewGroup mRootView;
 
     private final TextView mHeaderText;
     private final TextView mHeaderCount;
@@ -53,7 +57,6 @@
     private final View mIconView;
 
     private final View mHeader;
-    private final View mDivider;
 
     private View mGutter;
 
@@ -61,8 +64,9 @@
     private boolean mAnimatingNextIcon;
     private int mNotificationHeaderTextColor = Notification.COLOR_DEFAULT;
 
-    public NotificationItemView(PopupContainerWithArrow container) {
-        mContainer = container;
+    public NotificationItemView(PopupContainerWithArrow container, ViewGroup rootView) {
+        mPopupContainer = container;
+        mRootView = rootView;
         mContext = container.getContext();
 
         mHeaderText = container.findViewById(R.id.notification_text);
@@ -72,17 +76,29 @@
         mIconView = container.findViewById(R.id.popup_item_icon);
 
         mHeader = container.findViewById(R.id.header);
-        mDivider = container.findViewById(R.id.divider);
 
         mSwipeDetector = new SingleAxisSwipeDetector(mContext, mMainView, HORIZONTAL);
         mSwipeDetector.setDetectableScrollConditions(SingleAxisSwipeDetector.DIRECTION_BOTH, false);
         mMainView.setSwipeDetector(mSwipeDetector);
         mFooter.setContainer(this);
+
+        float radius = Themes.getDialogCornerRadius(mContext);
+        rootView.setClipToOutline(true);
+        rootView.setOutlineProvider(new ViewOutlineProvider() {
+            @Override
+            public void getOutline(View view, Outline outline) {
+                outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), radius);
+            }
+        });
+    }
+
+    public void updateBackgroundColor(int color) {
+        mMainView.updateBackgroundColor(color);
     }
 
     public void addGutter() {
         if (mGutter == null) {
-            mGutter = mContainer.inflateAndAdd(R.layout.notification_gutter, mContainer);
+            mGutter = mPopupContainer.inflateAndAdd(R.layout.notification_gutter, mRootView);
         }
     }
 
@@ -94,9 +110,8 @@
     }
 
     public void removeFooter() {
-        if (mContainer.indexOfChild(mFooter) >= 0) {
-            mContainer.removeView(mFooter);
-            mContainer.removeView(mDivider);
+        if (mRootView.indexOfChild(mFooter) >= 0) {
+            mRootView.removeView(mFooter);
         }
     }
 
@@ -108,16 +123,15 @@
     }
 
     public void removeAllViews() {
-        mContainer.removeView(mMainView);
-        mContainer.removeView(mHeader);
+        mRootView.removeView(mMainView);
+        mRootView.removeView(mHeader);
 
-        if (mContainer.indexOfChild(mFooter) >= 0) {
-            mContainer.removeView(mFooter);
-            mContainer.removeView(mDivider);
+        if (mRootView.indexOfChild(mFooter) >= 0) {
+            mRootView.removeView(mFooter);
         }
 
         if (mGutter != null) {
-            mContainer.removeView(mGutter);
+            mRootView.removeView(mGutter);
         }
     }
 
@@ -136,11 +150,11 @@
 
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
-            sTempRect.set(mMainView.getLeft(), mMainView.getTop(),
-                    mMainView.getRight(), mMainView.getBottom());
+            sTempRect.set(mRootView.getLeft(), mRootView.getTop(),
+                    mRootView.getRight(), mRootView.getBottom());
             mIgnoreTouch = !sTempRect.contains((int) ev.getX(), (int) ev.getY());
             if (!mIgnoreTouch) {
-                mContainer.getParent().requestDisallowInterceptTouchEvent(true);
+                mPopupContainer.getParent().requestDisallowInterceptTouchEvent(true);
             }
         }
         if (mIgnoreTouch) {
diff --git a/src/com/android/launcher3/notification/NotificationMainView.java b/src/com/android/launcher3/notification/NotificationMainView.java
index 9b06523..c995666 100644
--- a/src/com/android/launcher3/notification/NotificationMainView.java
+++ b/src/com/android/launcher3/notification/NotificationMainView.java
@@ -97,15 +97,24 @@
         super.onFinishInflate();
 
         mTextAndBackground = findViewById(R.id.text_and_background);
-        ColorDrawable colorBackground = (ColorDrawable) mTextAndBackground.getBackground();
-        mBackgroundColor = colorBackground.getColor();
-        RippleDrawable rippleBackground = new RippleDrawable(ColorStateList.valueOf(
-                Themes.getAttrColor(getContext(), android.R.attr.colorControlHighlight)),
-                colorBackground, null);
-        mTextAndBackground.setBackground(rippleBackground);
         mTitleView = mTextAndBackground.findViewById(R.id.title);
         mTextView = mTextAndBackground.findViewById(R.id.text);
         mIconView = findViewById(R.id.popup_item_icon);
+
+        ColorDrawable colorBackground = (ColorDrawable) mTextAndBackground.getBackground();
+        updateBackgroundColor(colorBackground.getColor());
+    }
+
+    public void updateBackgroundColor(int color) {
+        mBackgroundColor = color;
+        RippleDrawable rippleBackground = new RippleDrawable(ColorStateList.valueOf(
+                Themes.getAttrColor(getContext(), android.R.attr.colorControlHighlight)),
+                new ColorDrawable(color), null);
+        mTextAndBackground.setBackground(rippleBackground);
+        if (mNotificationInfo != null) {
+            mIconView.setBackground(mNotificationInfo.getIconForBackground(getContext(),
+                    mBackgroundColor));
+        }
     }
 
     public void setSwipeDetector(SingleAxisSwipeDetector swipeDetector) {
diff --git a/src/com/android/launcher3/popup/ArrowPopup.java b/src/com/android/launcher3/popup/ArrowPopup.java
index 15915e5..a53fe1f 100644
--- a/src/com/android/launcher3/popup/ArrowPopup.java
+++ b/src/com/android/launcher3/popup/ArrowPopup.java
@@ -17,10 +17,12 @@
 package com.android.launcher3.popup;
 
 import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
+import static com.android.launcher3.popup.PopupPopulator.MAX_SHORTCUTS;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
+import android.animation.ArgbEvaluator;
 import android.animation.ObjectAnimator;
 import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
@@ -28,6 +30,9 @@
 import android.content.res.Resources;
 import android.graphics.Outline;
 import android.graphics.Rect;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
 import android.util.AttributeSet;
 import android.util.Pair;
 import android.view.Gravity;
@@ -48,6 +53,7 @@
 import com.android.launcher3.anim.RevealOutlineAnimation;
 import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
 import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.shortcuts.DeepShortcutView;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.views.BaseDragLayer;
 
@@ -61,6 +67,9 @@
  */
 public abstract class ArrowPopup<T extends BaseDraggingActivity> extends AbstractFloatingView {
 
+    // +1 for system shortcut view
+    private static final int MAX_NUM_CHILDREN = MAX_SHORTCUTS + 1;
+
     private final Rect mTempRect = new Rect();
 
     protected final LayoutInflater mInflater;
@@ -75,6 +84,8 @@
     private final int mArrowPointRadius;
     private final View mArrow;
 
+    private final int mMargin;
+
     protected boolean mIsLeftAligned;
     protected boolean mIsAboveIcon;
     private int mGravity;
@@ -84,8 +95,14 @@
     private final Rect mStartRect = new Rect();
     private final Rect mEndRect = new Rect();
 
+    private final GradientDrawable mRoundedTop;
+    private final GradientDrawable mRoundedBottom;
+
     private Runnable mOnCloseCallback = () -> { };
 
+    private int mArrowColor;
+    private final int[] mColors;
+
     public ArrowPopup(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
         mInflater = LayoutInflater.from(context);
@@ -103,6 +120,7 @@
 
         // Initialize arrow view
         final Resources resources = getResources();
+        mMargin = resources.getDimensionPixelSize(R.dimen.popup_margin);
         mArrowWidth = resources.getDimensionPixelSize(R.dimen.popup_arrow_width);
         mArrowHeight = resources.getDimensionPixelSize(R.dimen.popup_arrow_height);
         mArrow = new View(context);
@@ -111,6 +129,24 @@
         mArrowOffsetHorizontal = resources.getDimensionPixelSize(
                 R.dimen.popup_arrow_horizontal_center_offset) - (mArrowWidth / 2);
         mArrowPointRadius = resources.getDimensionPixelSize(R.dimen.popup_arrow_corner_radius);
+
+        mRoundedTop = new GradientDrawable();
+        mRoundedTop.setCornerRadii(new float[] { mOutlineRadius, mOutlineRadius, mOutlineRadius,
+                mOutlineRadius, 0, 0, 0, 0});
+
+        mRoundedBottom = new GradientDrawable();
+        mRoundedBottom.setCornerRadii(new float[] { 0, 0, 0, 0, mOutlineRadius, mOutlineRadius,
+                mOutlineRadius, mOutlineRadius});
+
+        int primaryColor = Themes.getAttrColor(context, R.attr.popupColorPrimary);
+        int secondaryColor = Themes.getAttrColor(context, R.attr.popupColorSecondary);
+        ArgbEvaluator argb = new ArgbEvaluator();
+        mColors = new int[MAX_NUM_CHILDREN];
+        // Interpolate between the two colors, exclusive.
+        float step = 1f / (MAX_NUM_CHILDREN + 1);
+        for (int i = 0; i < mColors.length; ++i) {
+            mColors[i] = (int) argb.evaluate((i + 1) * step, primaryColor, secondaryColor);
+        }
     }
 
     public ArrowPopup(Context context, AttributeSet attrs) {
@@ -154,6 +190,77 @@
     protected void onInflationComplete(boolean isReversed) { }
 
     /**
+     * Set the margins and radius of backgrounds after views are properly ordered.
+     */
+    protected void assignMarginsAndBackgrounds() {
+        int count = getChildCount();
+        int totalVisibleShortcuts = 0;
+        for (int i = 0; i < count; i++) {
+            View view = getChildAt(i);
+            if (view.getVisibility() == VISIBLE && view instanceof DeepShortcutView) {
+                totalVisibleShortcuts++;
+            }
+        }
+
+        int numVisibleShortcut = 0;
+        View lastView = null;
+        int numVisibleChild = 0;
+        for (int i = 0; i < count; i++) {
+            View view = getChildAt(i);
+            boolean isShortcut = view instanceof DeepShortcutView;
+            if (view.getVisibility() == VISIBLE) {
+                if (lastView != null) {
+                    MarginLayoutParams mlp = (MarginLayoutParams) lastView.getLayoutParams();
+                    mlp.bottomMargin = mMargin;
+                }
+                lastView = view;
+                MarginLayoutParams mlp = (MarginLayoutParams) lastView.getLayoutParams();
+                mlp.bottomMargin = 0;
+
+                if (isShortcut) {
+                    if (totalVisibleShortcuts == 1) {
+                        view.setBackgroundResource(R.drawable.single_item_primary);
+                    } else if (totalVisibleShortcuts > 1) {
+                        if (numVisibleShortcut == 0) {
+                            view.setBackground(mRoundedTop);
+                        } else if (numVisibleShortcut == (totalVisibleShortcuts - 1)) {
+                            view.setBackground(mRoundedBottom);
+                        } else {
+                            view.setBackgroundResource(R.drawable.middle_item_primary);
+                        }
+                        numVisibleShortcut++;
+                    }
+                }
+
+                int color = mColors[numVisibleChild % mColors.length];
+                setChildColor(view, color);
+
+                // Arrow color matches the first child or the last child.
+                if (!mIsAboveIcon && numVisibleChild == 0) {
+                    mArrowColor = color;
+                } else if (mIsAboveIcon) {
+                    mArrowColor = color;
+                }
+
+                numVisibleChild++;
+            }
+        }
+        measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+    }
+
+    /**
+     * Sets the background color of the child.
+     */
+    protected void setChildColor(View view, int color) {
+        Drawable bg = view.getBackground();
+        if (bg instanceof GradientDrawable) {
+            ((GradientDrawable) bg.mutate()).setColor(color);
+        } else if (bg instanceof ColorDrawable) {
+            ((ColorDrawable) bg.mutate()).setColor(color);
+        }
+    }
+
+    /**
      * Shows the popup at the desired location, optionally reversing the children.
      * @param viewsToFlip number of views from the top to to flip in case of reverse order
      */
@@ -164,6 +271,8 @@
             reverseOrder(viewsToFlip);
         }
         onInflationComplete(reverseOrder);
+        assignMarginsAndBackgrounds();
+        orientAboutObject();
         if (shouldAddArrow()) {
             addArrow();
         }
@@ -176,6 +285,8 @@
     protected void show() {
         setupForDisplay();
         onInflationComplete(false);
+        assignMarginsAndBackgrounds();
+        orientAboutObject();
         if (shouldAddArrow()) {
             addArrow();
         }
@@ -203,8 +314,6 @@
         for (int i = 0; i < count; i++) {
             addView(allViews.get(i));
         }
-
-        orientAboutObject();
     }
 
     private int getArrowLeft() {
@@ -228,7 +337,7 @@
                     mOutlineRadius, getMeasuredWidth(), getMeasuredHeight(),
                     mArrowOffsetHorizontal, -mArrowOffsetVertical,
                     !mIsAboveIcon, mIsLeftAligned,
-                    Themes.getAttrColor(getContext(), R.attr.popupColorPrimary)));
+                    mArrowColor));
             mArrow.setElevation(getElevation());
         }
 
@@ -408,6 +517,7 @@
                 ? getResources().getInteger(R.integer.config_popupArrowOpenCloseDuration)
                 : 0;
     }
+
     private void animateOpen() {
         setVisibility(View.VISIBLE);
 
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index a1ba747..c282ae8 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -60,6 +60,7 @@
 import com.android.launcher3.notification.NotificationInfo;
 import com.android.launcher3.notification.NotificationItemView;
 import com.android.launcher3.notification.NotificationKeyData;
+import com.android.launcher3.notification.NotificationMainView;
 import com.android.launcher3.popup.PopupDataProvider.PopupDataChangeListener;
 import com.android.launcher3.shortcuts.DeepShortcutView;
 import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
@@ -91,6 +92,7 @@
     private BubbleTextView mOriginalIcon;
     private NotificationItemView mNotificationItemView;
     private int mNumNotifications;
+    private ViewGroup mNotificationContainer;
 
     private ViewGroup mSystemShortcutContainer;
 
@@ -169,6 +171,14 @@
         return false;
     }
 
+    @Override
+    protected void setChildColor(View v, int color) {
+        super.setChildColor(v, color);
+        if (v.getId() == R.id.notification_container && mNotificationItemView != null) {
+            mNotificationItemView.updateBackgroundColor(color);
+        }
+    }
+
     /**
      * Returns true if we can show the container.
      */
@@ -222,20 +232,6 @@
         if (isReversed && mNotificationItemView != null) {
             mNotificationItemView.inverseGutterMargin();
         }
-
-        // Update dividers
-        int count = getChildCount();
-        DeepShortcutView lastView = null;
-        for (int i = 0; i < count; i++) {
-            View view = getChildAt(i);
-            if (view.getVisibility() == VISIBLE && view instanceof DeepShortcutView) {
-                if (lastView != null) {
-                    lastView.setDividerVisibility(VISIBLE);
-                }
-                lastView = (DeepShortcutView) view;
-                lastView.setDividerVisibility(INVISIBLE);
-            }
-        }
     }
 
     @TargetApi(Build.VERSION_CODES.P)
@@ -257,8 +253,12 @@
         // Add views
         if (mNumNotifications > 0) {
             // Add notification entries
-            View.inflate(getContext(), R.layout.notification_content, this);
-            mNotificationItemView = new NotificationItemView(this);
+            if (mNotificationContainer == null) {
+                mNotificationContainer = findViewById(R.id.notification_container);
+                mNotificationContainer.setVisibility(VISIBLE);
+            }
+            View.inflate(getContext(), R.layout.notification_content, mNotificationContainer);
+            mNotificationItemView = new NotificationItemView(this, mNotificationContainer);
             if (mNumNotifications == 1) {
                 mNotificationItemView.removeFooter();
             }
@@ -342,34 +342,11 @@
     private void updateHiddenShortcuts() {
         int allowedCount = mNotificationItemView != null
                 ? MAX_SHORTCUTS_IF_NOTIFICATIONS : MAX_SHORTCUTS;
-        int originalHeight = getResources().getDimensionPixelSize(R.dimen.bg_popup_item_height);
-        int itemHeight = mNotificationItemView != null ?
-                getResources().getDimensionPixelSize(R.dimen.bg_popup_item_condensed_height)
-                : originalHeight;
-        float iconScale = ((float) itemHeight) / originalHeight;
 
         int total = mShortcuts.size();
         for (int i = 0; i < total; i++) {
             DeepShortcutView view = mShortcuts.get(i);
             view.setVisibility(i >= allowedCount ? GONE : VISIBLE);
-            view.getLayoutParams().height = itemHeight;
-            view.getIconView().setScaleX(iconScale);
-            view.getIconView().setScaleY(iconScale);
-        }
-    }
-
-    private void updateDividers() {
-        int count = getChildCount();
-        DeepShortcutView lastView = null;
-        for (int i = 0; i < count; i++) {
-            View view = getChildAt(i);
-            if (view.getVisibility() == VISIBLE && view instanceof DeepShortcutView) {
-                if (lastView != null) {
-                    lastView.setDividerVisibility(VISIBLE);
-                }
-                lastView = (DeepShortcutView) view;
-                lastView.setDividerVisibility(INVISIBLE);
-            }
         }
     }
 
@@ -591,8 +568,9 @@
                 // No more notifications, remove the notification views and expand all shortcuts.
                 mNotificationItemView.removeAllViews();
                 mNotificationItemView = null;
+                mNotificationContainer.setVisibility(GONE);
                 updateHiddenShortcuts();
-                updateDividers();
+                assignMarginsAndBackgrounds();
             } else {
                 mNotificationItemView.trimNotifications(
                         NotificationKeyData.extractKeysOnly(dotInfo.getNotificationKeys()));
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutView.java b/src/com/android/launcher3/shortcuts/DeepShortcutView.java
index e9b92e2..1c1418c 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutView.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutView.java
@@ -41,7 +41,6 @@
 
     private BubbleTextView mBubbleText;
     private View mIconView;
-    private View mDivider;
 
     private WorkspaceItemInfo mInfo;
     private ShortcutInfo mDetail;
@@ -63,11 +62,6 @@
         super.onFinishInflate();
         mBubbleText = findViewById(R.id.bubble_text);
         mIconView = findViewById(R.id.icon);
-        mDivider = findViewById(R.id.divider);
-    }
-
-    public void setDividerVisibility(int visibility) {
-        mDivider.setVisibility(visibility);
     }
 
     public BubbleTextView getBubbleText() {
diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java
index 1a114f3..9a2db10 100644
--- a/src/com/android/launcher3/views/OptionsPopupView.java
+++ b/src/com/android/launcher3/views/OptionsPopupView.java
@@ -134,7 +134,6 @@
                     (DeepShortcutView) popup.inflateAndAdd(R.layout.system_shortcut, popup);
             view.getIconView().setBackgroundResource(item.iconRes);
             view.getBubbleText().setText(item.labelRes);
-            view.setDividerVisibility(View.INVISIBLE);
             view.setOnClickListener(popup);
             view.setOnLongClickListener(popup);
             popup.mItemMap.put(view, item);
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
index 8df70fb..5c18faf 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
@@ -19,6 +19,7 @@
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.Context;
 import android.content.res.Configuration;
+import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.os.Handler;
@@ -120,8 +121,12 @@
         } else {
             super.setColorResources(colors);
         }
+    }
 
-        if (mDragListener != null) {
+    @Override
+    protected void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
+        if (mIsInDragMode && mDragListener != null) {
             mDragListener.onDragContentChanged();
         }
     }
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index 5328041..1b0e1ce 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -74,6 +74,7 @@
     protected int mPreviewHeight;
     protected int mPresetPreviewSize;
     private int mCellSize;
+    private float mPreviewScale = 1f;
 
     private WidgetImageView mWidgetImage;
     private TextView mWidgetName;
@@ -254,8 +255,8 @@
         }
         if (drawable != null) {
             LayoutParams layoutParams = (LayoutParams) mWidgetImage.getLayoutParams();
-            layoutParams.width = drawable.getIntrinsicWidth();
-            layoutParams.height = drawable.getIntrinsicHeight();
+            layoutParams.width = (int) (drawable.getIntrinsicWidth() * mPreviewScale);
+            layoutParams.height = (int) (drawable.getIntrinsicHeight() * mPreviewScale);
             mWidgetImage.setLayoutParams(layoutParams);
 
             mWidgetImage.setDrawable(drawable, mWidgetPreviewLoader.getBadgeForUser(mItem.user,
@@ -305,10 +306,16 @@
 
     /** Sets the widget preview image size in number of cells. */
     public void setPreviewSize(int spanX, int spanY) {
+        setPreviewSize(spanX, spanY, 1f);
+    }
+
+    /** Sets the widget preview image size, in number of cells, and preview scale. */
+    public void setPreviewSize(int spanX, int spanY, float previewScale) {
         int padding = 2 * getResources()
                 .getDimensionPixelSize(R.dimen.widget_preview_shortcut_padding);
         mPreviewWidth = mDeviceProfile.cellWidthPx * spanX + padding;
         mPreviewHeight = mDeviceProfile.cellHeightPx * spanY + padding;
+        mPreviewScale = previewScale;
     }
 
     @Override
diff --git a/src/com/android/launcher3/widget/dragndrop/AppWidgetHostViewDragListener.java b/src/com/android/launcher3/widget/dragndrop/AppWidgetHostViewDragListener.java
index c5e6fbd..66bb363 100644
--- a/src/com/android/launcher3/widget/dragndrop/AppWidgetHostViewDragListener.java
+++ b/src/com/android/launcher3/widget/dragndrop/AppWidgetHostViewDragListener.java
@@ -54,6 +54,8 @@
 
     /** Notifies when there is a content change in the drag view. */
     public void onDragContentChanged() {
-        mDragObject.dragView.invalidate();
+        if (mDragObject.dragView != null) {
+            mDragObject.dragView.invalidate();
+        }
     }
 }
diff --git a/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java b/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java
index 7eb5b83..7f84077 100644
--- a/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java
+++ b/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java
@@ -35,6 +35,7 @@
     private final SearchAndRecommendationViewHolder mViewHolder;
     private final WidgetsRecyclerView mPrimaryRecyclerView;
     private final WidgetsRecyclerView mSearchRecyclerView;
+    private final int mTabsHeight;
 
     // The following are only non null if mHasWorkProfile is true.
     @Nullable private final WidgetsRecyclerView mWorkRecyclerView;
@@ -42,10 +43,28 @@
     @Nullable private final PersonalWorkPagedView mPrimaryWorkViewPager;
 
     private WidgetsRecyclerView mCurrentRecyclerView;
-    private int mMaxCollapsibleHeight = 0;
+
+    /**
+     * The vertical distance, in pixels, until the search is pinned at the top of the screen when
+     * the user scrolls down the recycler view.
+     */
+    private int mCollapsibleHeightForSearch = 0;
+    /**
+     * The vertical distance, in pixels, until the recommendation table disappears from the top of
+     * the screen when the user scrolls down the recycler view.
+     */
+    private int mCollapsibleHeightForRecommendation = 0;
+    /**
+     * The vertical distance, in pixels, until the tabs is pinned at the top of the screen when the
+     * user scrolls down the recycler view.
+     *
+     * <p>Always 0 if there is no work profile.
+     */
+    private int mCollapsibleHeightForTabs = 0;
 
     SearchAndRecommendationsScrollController(
             boolean hasWorkProfile,
+            int tabsHeight,
             SearchAndRecommendationViewHolder viewHolder,
             WidgetsRecyclerView primaryRecyclerView,
             @Nullable WidgetsRecyclerView workRecyclerView,
@@ -55,46 +74,63 @@
         mHasWorkProfile = hasWorkProfile;
         mViewHolder = viewHolder;
         mPrimaryRecyclerView = primaryRecyclerView;
+        mCurrentRecyclerView = mPrimaryRecyclerView;
         mWorkRecyclerView = workRecyclerView;
         mSearchRecyclerView = searchRecyclerView;
         mPrimaryWorkTabsView = personalWorkTabsView;
         mPrimaryWorkViewPager = primaryWorkViewPager;
         mCurrentRecyclerView = mPrimaryRecyclerView;
+        mTabsHeight = tabsHeight;
     }
 
     /** Sets the current active {@link WidgetsRecyclerView}. */
     public void setCurrentRecyclerView(WidgetsRecyclerView currentRecyclerView) {
         mCurrentRecyclerView = currentRecyclerView;
+        mCurrentRecyclerView = currentRecyclerView;
+        mViewHolder.mHeaderTitle.setTranslationY(0);
+        mViewHolder.mRecommendedWidgetsTable.setTranslationY(0);
+        mViewHolder.mSearchBar.setTranslationY(0);
+
+        if (mHasWorkProfile) {
+            mPrimaryWorkTabsView.setTranslationY(0);
+        }
     }
 
     /**
      * Updates the margin and padding of {@link WidgetsFullSheet} to accumulate collapsible views.
+     *
+     * @return {@code true} if margins or/and padding of views in the search and recommendations
+     * container have been updated.
      */
-    public void updateMarginAndPadding() {
-        // The maximum vertical distance, in pixels, until the last collapsible element is not
-        // visible from the screen when the user scrolls down the recycler view.
-        mMaxCollapsibleHeight = mViewHolder.mContainer.getPaddingTop()
-                + measureHeightWithVerticalMargins(mViewHolder.mCollapseHandle)
-                + measureHeightWithVerticalMargins(mViewHolder.mHeaderTitle);
+    public boolean updateMarginAndPadding() {
+        boolean hasMarginOrPaddingUpdated = false;
+        mCollapsibleHeightForSearch = measureHeightWithVerticalMargins(mViewHolder.mHeaderTitle);
+        mCollapsibleHeightForRecommendation =
+                measureHeightWithVerticalMargins(mViewHolder.mHeaderTitle)
+                        + measureHeightWithVerticalMargins(mViewHolder.mCollapseHandle)
+                        + measureHeightWithVerticalMargins((View) mViewHolder.mSearchBar)
+                        + measureHeightWithVerticalMargins(mViewHolder.mRecommendedWidgetsTable);
 
         int topContainerHeight = measureHeightWithVerticalMargins(mViewHolder.mContainer);
         if (mHasWorkProfile) {
+            mCollapsibleHeightForTabs = measureHeightWithVerticalMargins(mViewHolder.mHeaderTitle)
+                    + measureHeightWithVerticalMargins(mViewHolder.mRecommendedWidgetsTable);
             // In a work profile setup, the full widget sheet contains the following views:
-            //           -------               -|
-            //           Widgets               -|---> LinearLayout for search & recommendations
-            //          Search bar             -|
-            //      Personal | Work
+            //           ------- (pinned)           -|
+            //          Widgets (collapsible)       -|---> LinearLayout for search & recommendations
+            //          Search bar (pinned)         -|
+            //  Widgets recommendation (collapsible)-|
+            //      Personal | Work (pinned)
             //           View Pager
             //
             // Views after the search & recommendations are not bound by RelativelyLayout param.
             // To position them on the expected location, padding & margin are added to these views
 
             // Tabs should have a padding of the height of the search & recommendations container.
-            mPrimaryWorkTabsView.setPadding(
-                    mPrimaryWorkTabsView.getPaddingLeft(),
-                    topContainerHeight,
-                    mPrimaryWorkTabsView.getPaddingRight(),
-                    mPrimaryWorkTabsView.getPaddingBottom());
+            RelativeLayout.LayoutParams tabsLayoutParams =
+                    (RelativeLayout.LayoutParams) mPrimaryWorkTabsView.getLayoutParams();
+            tabsLayoutParams.topMargin = topContainerHeight;
+            mPrimaryWorkTabsView.setLayoutParams(tabsLayoutParams);
 
             // Instead of setting the top offset directly, we split the top offset into two values:
             // 1. topOffsetAfterAllViewsCollapsed: this is the top offset after all collapsible
@@ -124,39 +160,52 @@
             //
             // When the views are first inflated, the sum of topOffsetAfterAllViewsCollapsed and
             // mMaxCollapsibleDistance should equal to the top container height.
-            int tabsViewActualHeight = measureHeightWithVerticalMargins(mPrimaryWorkTabsView)
-                    - mPrimaryWorkTabsView.getPaddingTop();
             int topOffsetAfterAllViewsCollapsed =
-                    topContainerHeight + tabsViewActualHeight - mMaxCollapsibleHeight;
+                    topContainerHeight + mTabsHeight - mCollapsibleHeightForTabs;
 
-            RelativeLayout.LayoutParams layoutParams =
+            RelativeLayout.LayoutParams viewPagerLayoutParams =
                     (RelativeLayout.LayoutParams) mPrimaryWorkViewPager.getLayoutParams();
-            layoutParams.setMargins(0, topOffsetAfterAllViewsCollapsed, 0, 0);
-            mPrimaryWorkViewPager.setLayoutParams(layoutParams);
-            mPrimaryWorkViewPager.requestLayout();
+            if (viewPagerLayoutParams.topMargin != topOffsetAfterAllViewsCollapsed) {
+                viewPagerLayoutParams.topMargin = topOffsetAfterAllViewsCollapsed;
+                mPrimaryWorkViewPager.setLayoutParams(viewPagerLayoutParams);
+                hasMarginOrPaddingUpdated = true;
+            }
 
-            mPrimaryRecyclerView.setPadding(
-                    mPrimaryRecyclerView.getPaddingLeft(),
-                    mMaxCollapsibleHeight,
-                    mPrimaryRecyclerView.getPaddingRight(),
-                    mPrimaryRecyclerView.getPaddingBottom());
-            mWorkRecyclerView.setPadding(
-                    mWorkRecyclerView.getPaddingLeft(),
-                    mMaxCollapsibleHeight,
-                    mWorkRecyclerView.getPaddingRight(),
-                    mWorkRecyclerView.getPaddingBottom());
+            if (mPrimaryRecyclerView.getPaddingTop() != mCollapsibleHeightForTabs) {
+                mPrimaryRecyclerView.setPadding(
+                        mPrimaryRecyclerView.getPaddingLeft(),
+                        mCollapsibleHeightForTabs,
+                        mPrimaryRecyclerView.getPaddingRight(),
+                        mPrimaryRecyclerView.getPaddingBottom());
+                hasMarginOrPaddingUpdated = true;
+            }
+            if (mWorkRecyclerView.getPaddingTop() != mCollapsibleHeightForTabs) {
+                mWorkRecyclerView.setPadding(
+                        mWorkRecyclerView.getPaddingLeft(),
+                        mCollapsibleHeightForTabs,
+                        mWorkRecyclerView.getPaddingRight(),
+                        mWorkRecyclerView.getPaddingBottom());
+                hasMarginOrPaddingUpdated = true;
+            }
         } else {
-            mPrimaryRecyclerView.setPadding(
-                    mPrimaryRecyclerView.getPaddingLeft(),
-                    topContainerHeight,
-                    mPrimaryRecyclerView.getPaddingRight(),
-                    mPrimaryRecyclerView.getPaddingBottom());
+            if (mPrimaryRecyclerView.getPaddingTop() != topContainerHeight) {
+                mPrimaryRecyclerView.setPadding(
+                        mPrimaryRecyclerView.getPaddingLeft(),
+                        topContainerHeight,
+                        mPrimaryRecyclerView.getPaddingRight(),
+                        mPrimaryRecyclerView.getPaddingBottom());
+                hasMarginOrPaddingUpdated = true;
+            }
         }
-        mSearchRecyclerView.setPadding(
-                mSearchRecyclerView.getPaddingLeft(),
-                topContainerHeight,
-                mSearchRecyclerView.getPaddingRight(),
-                mSearchRecyclerView.getPaddingBottom());
+        if (mSearchRecyclerView.getPaddingTop() != topContainerHeight) {
+            mSearchRecyclerView.setPadding(
+                    mSearchRecyclerView.getPaddingLeft(),
+                    topContainerHeight,
+                    mSearchRecyclerView.getPaddingRight(),
+                    mSearchRecyclerView.getPaddingBottom());
+            hasMarginOrPaddingUpdated = true;
+        }
+        return hasMarginOrPaddingUpdated;
     }
 
     /**
@@ -168,13 +217,22 @@
         // Always use the recycler view offset because fast scroller offset has a different scale.
         int recyclerViewYOffset = mCurrentRecyclerView.getCurrentScrollY();
         if (recyclerViewYOffset < 0) return;
-        if (mMaxCollapsibleHeight > 0) {
-            int yDisplacement = Math.max(-recyclerViewYOffset, -mMaxCollapsibleHeight);
+
+        if (mCollapsibleHeightForRecommendation > 0) {
+            int yDisplacement = Math.max(-recyclerViewYOffset,
+                    -mCollapsibleHeightForRecommendation);
             mViewHolder.mHeaderTitle.setTranslationY(yDisplacement);
-            mViewHolder.mSearchBar.setTranslationY(yDisplacement);
-            if (mHasWorkProfile) {
-                mPrimaryWorkTabsView.setTranslationY(yDisplacement);
-            }
+            mViewHolder.mRecommendedWidgetsTable.setTranslationY(yDisplacement);
+        }
+
+        if (mCollapsibleHeightForSearch > 0) {
+            int searchYDisplacement = Math.max(-recyclerViewYOffset, -mCollapsibleHeightForSearch);
+            mViewHolder.mSearchBar.setTranslationY(searchYDisplacement);
+        }
+
+        if (mHasWorkProfile && mCollapsibleHeightForTabs > 0) {
+            int yDisplacementForTabs = Math.max(-recyclerViewYOffset, -mCollapsibleHeightForTabs);
+            mPrimaryWorkTabsView.setTranslationY(yDisplacementForTabs);
         }
     }
 
@@ -189,6 +247,9 @@
 
     /** private the height, in pixel, + the vertical margins of a given view. */
     private static int measureHeightWithVerticalMargins(View view) {
+        if (view.getVisibility() != View.VISIBLE) {
+            return 0;
+        }
         MarginLayoutParams marginLayoutParams = (MarginLayoutParams) view.getLayoutParams();
         return view.getMeasuredHeight() + marginLayoutParams.bottomMargin
                 + marginLayoutParams.topMargin;
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index 5747690..1c34ece 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -23,6 +23,7 @@
 import android.animation.PropertyValuesHolder;
 import android.content.Context;
 import android.content.pm.LauncherApps;
+import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Process;
 import android.os.UserHandle;
@@ -32,6 +33,7 @@
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewGroup;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 import android.widget.TextView;
@@ -47,6 +49,7 @@
 import com.android.launcher3.R;
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.compat.AccessibilityManagerCompat;
+import com.android.launcher3.model.WidgetItem;
 import com.android.launcher3.views.RecyclerViewFastScroller;
 import com.android.launcher3.views.TopRoundedCornerView;
 import com.android.launcher3.widget.BaseWidgetSheet;
@@ -54,6 +57,7 @@
 import com.android.launcher3.widget.model.WidgetsListBaseEntry;
 import com.android.launcher3.widget.picker.search.SearchModeListener;
 import com.android.launcher3.widget.picker.search.WidgetsSearchBar;
+import com.android.launcher3.widget.util.WidgetsTableUtils;
 import com.android.launcher3.workprofile.PersonalWorkPagedView;
 import com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip.OnActivePageChangedListener;
 
@@ -67,10 +71,15 @@
 public class WidgetsFullSheet extends BaseWidgetSheet
         implements Insettable, ProviderChangedListener, OnActivePageChangedListener,
         WidgetsRecyclerView.HeaderViewDimensionsProvider, SearchModeListener {
+    private static final String TAG = WidgetsFullSheet.class.getSimpleName();
 
     private static final long DEFAULT_OPEN_DURATION = 267;
     private static final long FADE_IN_DURATION = 150;
     private static final float VERTICAL_START_POSITION = 0.3f;
+    // 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.
+    private static final float RECOMMENDATION_TABLE_HEIGHT_RATIO = 0.75f;
 
     private final Rect mInsets = new Rect();
     private final boolean mHasWorkProfile;
@@ -80,10 +89,12 @@
             mCurrentUser.equals(entry.mPkgItem.user);
     private final Predicate<WidgetsListBaseEntry> mWorkWidgetsFilter =
             mPrimaryWidgetsFilter.negate();
+    private final int mTabsHeight;
+    private final int mWidgetCellHorizontalPadding;
 
     @Nullable private PersonalWorkPagedView mViewPager;
-    private int mInitialTabsHeight = 0;
     private boolean mIsInSearchMode;
+    private int mMaxSpansPerRow = 4;
     private View mTabsView;
     private TextView mNoWidgetsView;
     private SearchAndRecommendationViewHolder mSearchAndRecommendationViewHolder;
@@ -95,6 +106,12 @@
         mAdapters.put(AdapterHolder.PRIMARY, new AdapterHolder(AdapterHolder.PRIMARY));
         mAdapters.put(AdapterHolder.WORK, new AdapterHolder(AdapterHolder.WORK));
         mAdapters.put(AdapterHolder.SEARCH, new AdapterHolder(AdapterHolder.SEARCH));
+        mTabsHeight = mHasWorkProfile
+                ? getContext().getResources()
+                        .getDimensionPixelSize(R.dimen.all_apps_header_tab_height)
+                : 0;
+        mWidgetCellHorizontalPadding = 2 * getResources().getDimensionPixelOffset(
+                R.dimen.widget_cell_horizontal_padding);
     }
 
     public WidgetsFullSheet(Context context, AttributeSet attrs) {
@@ -139,6 +156,7 @@
                 findViewById(R.id.search_and_recommendations_container));
         mSearchAndRecommendationsScrollController = new SearchAndRecommendationsScrollController(
                 mHasWorkProfile,
+                mTabsHeight,
                 mSearchAndRecommendationViewHolder,
                 findViewById(R.id.primary_widgets_list_view),
                 mHasWorkProfile ? findViewById(R.id.work_widgets_list_view) : null,
@@ -149,6 +167,7 @@
 
         mNoWidgetsView = findViewById(R.id.no_widgets_text);
 
+        onRecommendedWidgetsBound();
         onWidgetsBound();
 
         mSearchAndRecommendationViewHolder.mSearchBar.initialize(
@@ -232,6 +251,7 @@
         mInsets.set(insets);
 
         setBottomPadding(mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView, insets.bottom);
+        setBottomPadding(mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView, insets.bottom);
         if (mHasWorkProfile) {
             setBottomPadding(mAdapters.get(AdapterHolder.WORK).mWidgetsRecyclerView, insets.bottom);
         }
@@ -255,6 +275,22 @@
 
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        doMeasure(widthMeasureSpec, heightMeasureSpec);
+
+        if (mSearchAndRecommendationsScrollController.updateMarginAndPadding()) {
+            doMeasure(widthMeasureSpec, heightMeasureSpec);
+        }
+
+        if (updateMaxSpansPerRow()) {
+            doMeasure(widthMeasureSpec, heightMeasureSpec);
+
+            if (mSearchAndRecommendationsScrollController.updateMarginAndPadding()) {
+                doMeasure(widthMeasureSpec, heightMeasureSpec);
+            }
+        }
+    }
+
+    private void doMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         DeviceProfile deviceProfile = mLauncher.getDeviceProfile();
         int widthUsed;
         if (mInsets.bottom > 0) {
@@ -270,22 +306,29 @@
                 widthUsed, heightMeasureSpec, heightUsed);
         setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),
                 MeasureSpec.getSize(heightMeasureSpec));
+    }
 
-        int paddingPx = 2 * getResources().getDimensionPixelOffset(
-                R.dimen.widget_cell_horizontal_padding);
-        int maxSpansPerRow = getMeasuredWidth() / (deviceProfile.cellWidthPx + paddingPx);
-        mAdapters.get(AdapterHolder.PRIMARY).mWidgetsListAdapter.setMaxHorizontalSpansPerRow(
-                maxSpansPerRow);
-        if (mHasWorkProfile) {
-            mAdapters.get(AdapterHolder.WORK).mWidgetsListAdapter.setMaxHorizontalSpansPerRow(
-                    maxSpansPerRow);
+    /** Returns {@code true} if the max spans have been updated. */
+    private boolean updateMaxSpansPerRow() {
+        if (getMeasuredWidth() == 0) return false;
+
+        int previousMaxSpansPerRow = mMaxSpansPerRow;
+        mMaxSpansPerRow = getMeasuredWidth()
+                / (mLauncher.getDeviceProfile().cellWidthPx + mWidgetCellHorizontalPadding);
+
+        if (previousMaxSpansPerRow != mMaxSpansPerRow) {
+            mAdapters.get(AdapterHolder.PRIMARY).mWidgetsListAdapter.setMaxHorizontalSpansPerRow(
+                    mMaxSpansPerRow);
+            mAdapters.get(AdapterHolder.SEARCH).mWidgetsListAdapter.setMaxHorizontalSpansPerRow(
+                    mMaxSpansPerRow);
+            if (mHasWorkProfile) {
+                mAdapters.get(AdapterHolder.WORK).mWidgetsListAdapter.setMaxHorizontalSpansPerRow(
+                        mMaxSpansPerRow);
+            }
+            onRecommendedWidgetsBound();
+            return true;
         }
-
-        if (mInitialTabsHeight == 0 && mTabsView != null) {
-            mInitialTabsHeight = measureHeightWithVerticalMargins(mTabsView);
-        }
-
-        mSearchAndRecommendationsScrollController.updateMarginAndPadding();
+        return false;
     }
 
     @Override
@@ -342,6 +385,8 @@
             mViewPager.snapToPage(AdapterHolder.PRIMARY);
         }
         attachScrollbarToRecyclerView(mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView);
+
+        mSearchAndRecommendationsScrollController.updateMarginAndPadding();
     }
 
     @Override
@@ -353,6 +398,8 @@
 
     private void setViewVisibilityBasedOnSearch(boolean isInSearchMode) {
         mIsInSearchMode = isInSearchMode;
+        mSearchAndRecommendationViewHolder.mRecommendedWidgetsTable
+                .setVisibility(isInSearchMode ? GONE : VISIBLE);
         if (mHasWorkProfile) {
             mViewPager.setVisibility(isInSearchMode ? GONE : VISIBLE);
             mTabsView.setVisibility(isInSearchMode ? GONE : VISIBLE);
@@ -370,6 +417,25 @@
         mAdapters.get(AdapterHolder.WORK).mWidgetsListAdapter.resetExpandedHeader();
     }
 
+    @Override
+    public void onRecommendedWidgetsBound() {
+        List<WidgetItem> recommendedWidgets =
+                mLauncher.getPopupDataProvider().getRecommendedWidgets();
+        WidgetsRecommendationTableLayout table =
+                mSearchAndRecommendationViewHolder.mRecommendedWidgetsTable;
+        if (recommendedWidgets.size() > 0) {
+            float maxTableHeight =
+                    (mLauncher.getDeviceProfile().heightPx - mTabsHeight - getHeaderViewHeight())
+                            * RECOMMENDATION_TABLE_HEIGHT_RATIO;
+            List<ArrayList<WidgetItem>> recommendedWidgetsInTable =
+                    WidgetsTableUtils.groupWidgetItemsIntoTable(recommendedWidgets,
+                            mMaxSpansPerRow);
+            table.setRecommendedWidgets(recommendedWidgetsInTable, maxTableHeight);
+        } else {
+            table.setVisibility(GONE);
+        }
+    }
+
     private void open(boolean animate) {
         if (animate) {
             if (getPopupContainer().getInsets().bottom > 0) {
@@ -471,6 +537,23 @@
                 + marginLayoutParams.topMargin;
     }
 
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        if (mIsInSearchMode) {
+            mSearchAndRecommendationViewHolder.mSearchBar.reset();
+        }
+    }
+
+    @Override
+    public boolean onBackPressed() {
+        if (mIsInSearchMode) {
+            mSearchAndRecommendationViewHolder.mSearchBar.reset();
+            return true;
+        }
+        return super.onBackPressed();
+    }
+
     /** A holder class for holding adapters & their corresponding recycler view. */
     private final class AdapterHolder {
         static final int PRIMARY = 0;
@@ -513,20 +596,29 @@
             mWidgetsRecyclerView.setEdgeEffectFactory(
                     ((TopRoundedCornerView) mContent).createEdgeEffectFactory());
             mWidgetsListAdapter.setApplyBitmapDeferred(false, mWidgetsRecyclerView);
+            mWidgetsListAdapter.setMaxHorizontalSpansPerRow(mMaxSpansPerRow);
         }
     }
 
     final class SearchAndRecommendationViewHolder {
-        final View mContainer;
+        final ViewGroup mContainer;
         final View mCollapseHandle;
         final WidgetsSearchBar mSearchBar;
         final TextView mHeaderTitle;
+        final WidgetsRecommendationTableLayout mRecommendedWidgetsTable;
 
-        SearchAndRecommendationViewHolder(View searchAndRecommendationContainer) {
+        SearchAndRecommendationViewHolder(ViewGroup searchAndRecommendationContainer) {
             mContainer = searchAndRecommendationContainer;
             mCollapseHandle = mContainer.findViewById(R.id.collapse_handle);
             mSearchBar = mContainer.findViewById(R.id.widgets_search_bar);
             mHeaderTitle = mContainer.findViewById(R.id.title);
+            mRecommendedWidgetsTable = mContainer.findViewById(R.id.recommended_widget_table);
+            mRecommendedWidgetsTable.setWidgetCellOnTouchListener((view, event) -> {
+                getRecyclerView().onTouchEvent(event);
+                return false;
+            });
+            mRecommendedWidgetsTable.setWidgetCellLongClickListener(WidgetsFullSheet.this);
+            mRecommendedWidgetsTable.setWidgetCellOnClickListener(WidgetsFullSheet.this);
         }
     }
 }
diff --git a/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java b/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java
new file mode 100644
index 0000000..6569fb0
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java
@@ -0,0 +1,175 @@
+/*
+ * 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.content.Context;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+import android.widget.TableLayout;
+import android.widget.TableRow;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.R;
+import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.widget.WidgetCell;
+import com.android.launcher3.widget.WidgetImageView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** A {@link TableLayout} for showing recommended widgets. */
+public final class WidgetsRecommendationTableLayout extends TableLayout {
+    private static final float SCALE_DOWN_RATIO = 0.9f;
+    private final DeviceProfile mDeviceProfile;
+    private final float mWidgetCellTextViewsHeight;
+
+    private float mRecommendationTableMaxHeight = Float.MAX_VALUE;
+    @Nullable private OnLongClickListener mWidgetCellOnLongClickListener;
+    @Nullable private OnClickListener mWidgetCellOnClickListener;
+    @Nullable private OnTouchListener mWidgetCellOnTouchListener;
+
+    public WidgetsRecommendationTableLayout(Context context) {
+        this(context, /* attrs= */ null);
+    }
+
+    public WidgetsRecommendationTableLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mDeviceProfile = Launcher.getLauncher(context).getDeviceProfile();
+        // There are 1 row for title, 1 row for dimension and 2 rows for description.
+        mWidgetCellTextViewsHeight = 4 * getResources().getDimension(R.dimen.widget_cell_font_size);
+    }
+
+    /** Sets a {@link android.view.View.OnLongClickListener} for all widget cells in this table. */
+    public void setWidgetCellLongClickListener(OnLongClickListener onLongClickListener) {
+        mWidgetCellOnLongClickListener = onLongClickListener;
+    }
+
+    /** Sets a {@link android.view.View.OnClickListener} for all widget cells in this table. */
+    public void setWidgetCellOnClickListener(OnClickListener widgetCellOnClickListener) {
+        mWidgetCellOnClickListener = widgetCellOnClickListener;
+    }
+
+    /** Sets a {@link android.view.View.OnTouchListener} for all widget cells in this table. */
+    public void setWidgetCellOnTouchListener(OnTouchListener widgetCellOnTouchListener) {
+        mWidgetCellOnTouchListener = widgetCellOnTouchListener;
+    }
+
+    /**
+     * Sets a list of recommended widgets that would like to be displayed in this table within the
+     * desired {@code recommendationTableMaxHeight}.
+     *
+     * <p>If the content can't fit {@code recommendationTableMaxHeight}, this view will remove a
+     * last row from the {@code recommendedWidgets} until it fits or only one row left. If the only
+     * row still doesn't fit, we scale down the preview image.
+     */
+    public void setRecommendedWidgets(List<ArrayList<WidgetItem>> recommendedWidgets,
+            float recommendationTableMaxHeight) {
+        mRecommendationTableMaxHeight = recommendationTableMaxHeight;
+        RecommendationTableData data = fitRecommendedWidgetsToTableSpace(/* previewScale= */ 1f,
+                recommendedWidgets);
+        bindData(data);
+    }
+
+    private void bindData(RecommendationTableData data) {
+        if (data.mRecommendationTable.size() == 0) {
+            setVisibility(GONE);
+            return;
+        }
+
+        removeAllViews();
+
+        for (int i = 0; i < data.mRecommendationTable.size(); i++) {
+            List<WidgetItem> widgetItems = data.mRecommendationTable.get(i);
+            TableRow tableRow = new TableRow(getContext());
+            tableRow.setGravity(Gravity.TOP);
+
+            for (WidgetItem widgetItem : widgetItems) {
+                WidgetCell widgetCell = addItemCell(tableRow);
+                widgetCell.setPreviewSize(widgetItem.spanX, widgetItem.spanY, data.mPreviewScale);
+                widgetCell.applyFromCellItem(widgetItem,
+                        LauncherAppState.getInstance(getContext()).getWidgetCache());
+                widgetCell.ensurePreview();
+            }
+            addView(tableRow);
+        }
+        setVisibility(VISIBLE);
+    }
+
+    private WidgetCell addItemCell(ViewGroup parent) {
+        WidgetCell widget = (WidgetCell) LayoutInflater.from(
+                getContext()).inflate(R.layout.widget_cell, parent, false);
+
+        widget.setOnTouchListener(mWidgetCellOnTouchListener);
+        WidgetImageView preview = widget.findViewById(R.id.widget_preview);
+        preview.setOnClickListener(mWidgetCellOnClickListener);
+        preview.setOnLongClickListener(mWidgetCellOnLongClickListener);
+        widget.setAnimatePreview(false);
+
+        parent.addView(widget);
+        return widget;
+    }
+
+    private RecommendationTableData fitRecommendedWidgetsToTableSpace(
+            float previewScale,
+            List<ArrayList<WidgetItem>> recommendedWidgetsInTable) {
+        // A naive estimation of the widgets recommendation table height without inflation.
+        float totalHeight = 0;
+        for (int i = 0; i < recommendedWidgetsInTable.size(); i++) {
+            List<WidgetItem> widgetItems = recommendedWidgetsInTable.get(i);
+            float rowHeight = 0;
+            for (int j = 0; j < widgetItems.size(); j++) {
+                float previewHeight = widgetItems.get(j).spanY * mDeviceProfile.allAppsCellHeightPx
+                        * previewScale;
+                rowHeight = Math.max(rowHeight, previewHeight + mWidgetCellTextViewsHeight);
+            }
+            totalHeight += rowHeight;
+        }
+
+        if (totalHeight < mRecommendationTableMaxHeight) {
+            return new RecommendationTableData(recommendedWidgetsInTable, previewScale);
+        }
+
+        if (recommendedWidgetsInTable.size() > 1) {
+            // We don't want to scale down widgets preview unless we really need to. Reduce the
+            // num of row by 1 to see if it fits.
+            return fitRecommendedWidgetsToTableSpace(
+                    previewScale,
+                    recommendedWidgetsInTable.subList(/* fromIndex= */0,
+                            /* toIndex= */recommendedWidgetsInTable.size() - 1));
+        }
+
+        float nextPreviewScale = previewScale * SCALE_DOWN_RATIO;
+        return fitRecommendedWidgetsToTableSpace(nextPreviewScale, recommendedWidgetsInTable);
+    }
+
+    /** Data class for the widgets recommendation table and widgets preview scaling. */
+    private class RecommendationTableData {
+        private final List<ArrayList<WidgetItem>> mRecommendationTable;
+        private final float mPreviewScale;
+
+        RecommendationTableData(List<ArrayList<WidgetItem>> recommendationTable,
+                float previewScale) {
+            mRecommendationTable = recommendationTable;
+            mPreviewScale = previewScale;
+        }
+    }
+}
diff --git a/src/com/android/launcher3/widget/picker/search/LauncherWidgetsSearchBar.java b/src/com/android/launcher3/widget/picker/search/LauncherWidgetsSearchBar.java
index 5520826..cc33619 100644
--- a/src/com/android/launcher3/widget/picker/search/LauncherWidgetsSearchBar.java
+++ b/src/com/android/launcher3/widget/picker/search/LauncherWidgetsSearchBar.java
@@ -63,6 +63,11 @@
     }
 
     @Override
+    public void reset() {
+        mController.clearSearchResult();
+    }
+
+    @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
         mEditText = findViewById(R.id.widgets_search_bar_edit_text);
diff --git a/src/com/android/launcher3/widget/picker/search/WidgetsSearchBar.java b/src/com/android/launcher3/widget/picker/search/WidgetsSearchBar.java
index af6dc48..ef7bf23 100644
--- a/src/com/android/launcher3/widget/picker/search/WidgetsSearchBar.java
+++ b/src/com/android/launcher3/widget/picker/search/WidgetsSearchBar.java
@@ -30,6 +30,11 @@
     void initialize(List<WidgetsListBaseEntry> allWidgets, SearchModeListener searchModeListener);
 
     /**
+     * Clears search bar.
+     */
+    void reset();
+
+    /**
      * Sets the vertical location, in pixels, of this search bar relative to its top position.
      */
     void setTranslationY(float translationY);
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index a7b92b7..7bfe33c 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -1214,7 +1214,7 @@
 
         final MotionEvent event = getMotionEvent(downTime, currentTime, action, point.x, point.y);
         assertTrue("injectInputEvent failed",
-                mInstrumentation.getUiAutomation().injectInputEvent(event, true));
+                mInstrumentation.getUiAutomation().injectInputEvent(event, true, false));
         event.recycle();
     }
 
diff --git a/tests/tapl/com/android/launcher3/tapl/Widgets.java b/tests/tapl/com/android/launcher3/tapl/Widgets.java
index 22f4d31..fe4c712 100644
--- a/tests/tapl/com/android/launcher3/tapl/Widgets.java
+++ b/tests/tapl/com/android/launcher3/tapl/Widgets.java
@@ -30,7 +30,6 @@
 import com.android.launcher3.testing.TestProtocol;
 
 import java.util.Collection;
-import java.util.List;
 
 /**
  * All widgets container.
@@ -106,7 +105,8 @@
                     fullWidgetsPicker.wait(Until.scrollable(true), WAIT_TIME_MS));
             final Point displaySize = mLauncher.getRealDisplaySize();
 
-            final UiObject2 widgetsContainer = findTestAppWidgetsTableContainer();
+            Rect headerRect = new Rect();
+            final UiObject2 widgetsContainer = findTestAppWidgetsTableContainer(headerRect);
             mLauncher.assertTrue("Can't locate widgets list for the test app: "
                             + mLauncher.getLauncherPackageName(),
                     widgetsContainer != null);
@@ -132,7 +132,12 @@
 
                 mLauncher.assertTrue("Too many attempts", ++i <= 40);
                 final int scroll = getWidgetsScroll();
-                mLauncher.scrollToLastVisibleRow(fullWidgetsPicker, tableRows, 0);
+                mLauncher.scroll(
+                        fullWidgetsPicker,
+                        Direction.DOWN,
+                        headerRect,
+                        10,
+                        true);
                 final int newScroll = getWidgetsScroll();
                 mLauncher.assertTrue(
                         "Scrolled in a wrong direction in Widgets: from " + scroll + " to "
@@ -143,7 +148,7 @@
     }
 
     /** Finds the widgets list of this test app from the collapsed full widgets picker. */
-    private UiObject2 findTestAppWidgetsTableContainer() {
+    private UiObject2 findTestAppWidgetsTableContainer(Rect outHeaderRect) {
         final BySelector headerSelector = By.res(mLauncher.getLauncherPackageName(),
                 "widgets_list_header");
         final BySelector targetAppSelector = By.clazz("android.widget.TextView").text(
@@ -156,6 +161,7 @@
             UiObject2 fullWidgetsPicker = verifyActiveContainer();
 
             UiObject2 header = fullWidgetsPicker.findObject(headerSelector);
+            outHeaderRect.set(0, 0, 0, header.getVisibleBounds().height());
             mLauncher.assertTrue("Can't find a widget header", header != null);
 
             // Look for a header that has the test app name.
@@ -179,11 +185,14 @@
                 if (widgetsContainer != null) {
                     return widgetsContainer;
                 }
-                mLauncher.scrollToLastVisibleRow(fullWidgetsPicker, List.of(headerTitle), 0);
-            } else {
-                mLauncher.scrollToLastVisibleRow(fullWidgetsPicker, fullWidgetsPicker.getChildren(),
-                        0);
+
             }
+            mLauncher.scroll(
+                    fullWidgetsPicker,
+                    Direction.DOWN,
+                    outHeaderRect,
+                    /* steps= */ 10,
+                    /* slowDown= */ true);
         }
 
         return null;