Merge "Add logs to help debug TwoButtonNavBarTouchController" into sc-dev
diff --git a/Android.bp b/Android.bp
index e30d22f..7e6a565 100644
--- a/Android.bp
+++ b/Android.bp
@@ -115,6 +115,9 @@
     manifest: "AndroidManifest-common.xml",
     sdk_version: "current",
     min_sdk_version: "26",
+    lint: {
+        baseline_filename: "lint-baseline-res-lib.xml",
+    },
 }
 
 //
@@ -127,6 +130,9 @@
     sdk_version: "current",
     min_sdk_version: "26",
     manifest: "AndroidManifest-common.xml",
+    lint: {
+        baseline_filename: "lint-baseline-common-deps-lib.xml",
+    },
 }
 
 //
@@ -171,6 +177,9 @@
     additional_manifests: [
         "AndroidManifest-common.xml",
     ],
+    lint: {
+        baseline_filename: "lint-baseline-launcher3.xml",
+    },
 }
 
 // Library with all the dependencies for building quickstep
@@ -211,3 +220,33 @@
     name: "launcher-proguard-rules",
     srcs: ["proguard.flags"],
 }
+
+
+// Library with all the dependencies for building Launcher Go
+android_library {
+    name: "LauncherGoResLib",
+    srcs: [
+        "src/**/*.java",
+        "quickstep/src/**/*.java",
+        "go/src/**/*.java",
+        "go/quickstep/src/**/*.java",
+    ],
+    resource_dirs: [
+        "go/res",
+        "go/quickstep/res",
+    ],
+    static_libs: [
+        "Launcher3CommonDepsLib",
+        "QuickstepResLib",
+    ],
+    manifest: "quickstep/AndroidManifest-launcher.xml",
+    additional_manifests: [
+        "go/AndroidManifest.xml",
+        "AndroidManifest-common.xml",
+    ],
+    min_sdk_version: "29",
+    lint: {
+        baseline_filename: "lint-baseline-go-res-lib.xml",
+    },
+}
+
diff --git a/lint-baseline-common-deps-lib.xml b/lint-baseline-common-deps-lib.xml
new file mode 100644
index 0000000..e52f8fb
--- /dev/null
+++ b/lint-baseline-common-deps-lib.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0">
+
+    <issue
+        id="NewApi"
+        message="`?android:attr/dialogCornerRadius` requires API level 28 (current min is 26)"
+        errorLine1="        android:topLeftRadius=&quot;?android:attr/dialogCornerRadius&quot;"
+        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/res/drawable/add_item_dialog_background.xml"
+            line="6"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="`?android:attr/dialogCornerRadius` requires API level 28 (current min is 26)"
+        errorLine1="        android:topRightRadius=&quot;?android:attr/dialogCornerRadius&quot; />"
+        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/res/drawable/add_item_dialog_background.xml"
+            line="7"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="`@android:style/Widget.DeviceDefault.Button.Colored` requires API level 28 (current min is 26)"
+        errorLine1="    &lt;style name=&quot;Widget.DeviceDefault.Button.Rounded.Colored&quot; parent=&quot;@android:style/Widget.DeviceDefault.Button.Colored&quot;>"
+        errorLine2="                                                              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/res/values/styles.xml"
+            line="287"
+            column="63"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="`@android:dimen/system_app_widget_background_radius` requires API level 31 (current min is 26)"
+        errorLine1="    &lt;corners android:radius=&quot;@android:dimen/system_app_widget_background_radius&quot; />"
+        errorLine2="             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/res/drawable/widget_resize_frame.xml"
+            line="20"
+            column="14"/>
+    </issue>
+
+</issues>
diff --git a/lint-baseline-go-res-lib.xml b/lint-baseline-go-res-lib.xml
new file mode 100644
index 0000000..c5669f2
--- /dev/null
+++ b/lint-baseline-go-res-lib.xml
@@ -0,0 +1,576 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0">
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `android.view.View#getWindowInsetsController`"
+        errorLine1="        getWindowInsetsController().hide(WindowInsets.Type.ime());"
+        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/allapps/AllAppsContainerView.java"
+            line="203"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `android.view.WindowInsets.Type#ime`"
+        errorLine1="        getWindowInsetsController().hide(WindowInsets.Type.ime());"
+        errorLine2="                                                           ~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/allapps/AllAppsContainerView.java"
+            line="203"
+            column="60"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `android.view.WindowInsetsController#hide`"
+        errorLine1="        getWindowInsetsController().hide(WindowInsets.Type.ime());"
+        errorLine2="                                    ~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/allapps/AllAppsContainerView.java"
+            line="203"
+            column="37"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `android.view.View#getWindowInsetsController`"
+        errorLine1="                getWindowInsetsController().hide(WindowInsets.Type.ime());"
+        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/allapps/AllAppsRecyclerView.java"
+            line="193"
+            column="17"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `android.view.WindowInsets.Type#ime`"
+        errorLine1="                getWindowInsetsController().hide(WindowInsets.Type.ime());"
+        errorLine2="                                                                   ~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/allapps/AllAppsRecyclerView.java"
+            line="193"
+            column="68"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `android.view.WindowInsetsController#hide`"
+        errorLine1="                getWindowInsetsController().hide(WindowInsets.Type.ime());"
+        errorLine2="                                            ~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/allapps/AllAppsRecyclerView.java"
+            line="193"
+            column="45"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 29): `android.appwidget.AppWidgetHostView#updateAppWidgetSize`"
+        errorLine1="            widgetView.updateAppWidgetSize(new Bundle(), sizes);"
+        errorLine2="                       ~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/AppWidgetResizeFrame.java"
+            line="399"
+            column="24"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 29): `android.graphics.Rect#inset`"
+        errorLine1="        potentialTaskRect.inset(insets.left, insets.top, insets.right, insets.bottom);"
+        errorLine2="                          ~~~~~">
+        <location
+            file="packages/apps/Launcher3/quickstep/src/com/android/quickstep/BaseActivityInterface.java"
+            line="248"
+            column="27"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 29): `android.graphics.Rect#inset`"
+        errorLine1="        potentialTaskRect.inset("
+        errorLine2="                          ~~~~~">
+        <location
+            file="packages/apps/Launcher3/quickstep/src/com/android/quickstep/BaseActivityInterface.java"
+            line="249"
+            column="27"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 29): `android.graphics.Rect#inset`"
+        errorLine1="        outRect.inset(Math.max(insets.left, sideMargin), Math.max(insets.top, topMargin),"
+        errorLine2="                ~~~~~">
+        <location
+            file="packages/apps/Launcher3/quickstep/src/com/android/quickstep/BaseActivityInterface.java"
+            line="291"
+            column="17"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 29): `android.graphics.Rect#inset`"
+        errorLine1="        gridRect.inset(0, dp.overviewTaskThumbnailTopMarginPx, 0, 0);"
+        errorLine2="                 ~~~~~">
+        <location
+            file="packages/apps/Launcher3/quickstep/src/com/android/quickstep/BaseActivityInterface.java"
+            line="315"
+            column="18"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `android.view.WindowManager#getCurrentWindowMetrics`"
+        errorLine1="                    .getCurrentWindowMetrics().getWindowInsets();"
+        errorLine2="                     ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/DeviceProfile.java"
+            line="236"
+            column="22"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `android.view.WindowMetrics#getWindowInsets`"
+        errorLine1="                    .getCurrentWindowMetrics().getWindowInsets();"
+        errorLine2="                                               ~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/DeviceProfile.java"
+            line="236"
+            column="48"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `android.content.Context#getDisplay`"
+        errorLine1="            if (mContext.getDisplay() != null) {"
+        errorLine2="                         ~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/quickstep/src/com/android/quickstep/interaction/EdgeBackGestureHandler.java"
+            line="115"
+            column="26"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `android.content.Context#getDisplay`"
+        errorLine1="                mContext.getDisplay().getRealSize(mDisplaySize);"
+        errorLine2="                         ~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/quickstep/src/com/android/quickstep/interaction/EdgeBackGestureHandler.java"
+            line="116"
+            column="26"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `android.content.ContextWrapper#getDisplay`"
+        errorLine1="        Display display = getDisplay();"
+        errorLine2="                          ~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java"
+            line="153"
+            column="27"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `java.util.List#of`"
+        errorLine1="                    List.of(new Rect(0, 0, metrics.widthPixels, metrics.heightPixels)));"
+        errorLine2="                         ~~">
+        <location
+            file="packages/apps/Launcher3/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java"
+            line="158"
+            column="26"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 29): `android.appwidget.AppWidgetHostView#resetColorResources`"
+        errorLine1="            resetColorResources();"
+        errorLine2="            ~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java"
+            line="137"
+            column="13"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `java.util.List#of`"
+        errorLine1="            mColorExtractor.addLocation(List.of(mLastLocationRegistered));"
+        errorLine2="                                             ~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java"
+            line="367"
+            column="46"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `java.util.List#of`"
+        errorLine1="                mColorExtractor.addLocation(List.of(mLastLocationRegistered));"
+        errorLine2="                                                 ~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java"
+            line="390"
+            column="50"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 31 (current min is 29): `android.appwidget.AppWidgetProviderInfo#maxResizeWidth`"
+        errorLine1="                (ATLEAST_S &amp;&amp; maxResizeWidth > 0)"
+        errorLine2="                              ~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java"
+            line="91"
+            column="31"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 31 (current min is 29): `android.appwidget.AppWidgetProviderInfo#maxResizeWidth`"
+        errorLine1="                        ? getSpanX(widgetPadding, maxResizeWidth, smallestCellWidth)"
+        errorLine2="                                                  ~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java"
+            line="92"
+            column="51"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 31 (current min is 29): `android.appwidget.AppWidgetProviderInfo#maxResizeHeight`"
+        errorLine1="                (ATLEAST_S &amp;&amp; maxResizeHeight > 0)"
+        errorLine2="                              ~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java"
+            line="95"
+            column="31"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 31 (current min is 29): `android.appwidget.AppWidgetProviderInfo#maxResizeHeight`"
+        errorLine1="                        ? getSpanY(widgetPadding, maxResizeHeight, smallestCellHeight)"
+        errorLine2="                                                  ~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java"
+            line="96"
+            column="51"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 31 (current min is 29): `android.appwidget.AppWidgetProviderInfo#targetCellWidth`"
+        errorLine1="        if (ATLEAST_S &amp;&amp; targetCellWidth >= minSpanX &amp;&amp; targetCellWidth &lt;= maxSpanX"
+        errorLine2="                         ~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java"
+            line="101"
+            column="26"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 31 (current min is 29): `android.appwidget.AppWidgetProviderInfo#targetCellWidth`"
+        errorLine1="        if (ATLEAST_S &amp;&amp; targetCellWidth >= minSpanX &amp;&amp; targetCellWidth &lt;= maxSpanX"
+        errorLine2="                                                        ~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java"
+            line="101"
+            column="57"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 31 (current min is 29): `android.appwidget.AppWidgetProviderInfo#targetCellHeight`"
+        errorLine1="                &amp;&amp; targetCellHeight >= minSpanY &amp;&amp; targetCellHeight &lt;= maxSpanY) {"
+        errorLine2="                   ~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java"
+            line="102"
+            column="20"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 31 (current min is 29): `android.appwidget.AppWidgetProviderInfo#targetCellHeight`"
+        errorLine1="                &amp;&amp; targetCellHeight >= minSpanY &amp;&amp; targetCellHeight &lt;= maxSpanY) {"
+        errorLine2="                                                   ~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java"
+            line="102"
+            column="52"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 31 (current min is 29): `android.appwidget.AppWidgetProviderInfo#targetCellWidth`"
+        errorLine1="            spanX = targetCellWidth;"
+        errorLine2="                    ~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java"
+            line="103"
+            column="21"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 31 (current min is 29): `android.appwidget.AppWidgetProviderInfo#targetCellHeight`"
+        errorLine1="            spanY = targetCellHeight;"
+        errorLine2="                    ~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java"
+            line="104"
+            column="21"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `android.content.Context#getDisplay`"
+        errorLine1="        final Display display = mContext.getDisplay();"
+        errorLine2="                                         ~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java"
+            line="94"
+            column="42"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 29): `android.content.pm.LauncherActivityInfo#getLoadingProgress`"
+        errorLine1="            return (int) (100 * info.getLoadingProgress());"
+        errorLine2="                                     ~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/util/PackageManagerHelper.java"
+            line="338"
+            column="38"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `java.util.List#of`"
+        errorLine1="    private List&lt;WidgetsListBaseEntry> mAllWidgets = List.of();"
+        errorLine2="                                                          ~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/popup/PopupDataProvider.java"
+            line="64"
+            column="59"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `java.util.List#of`"
+        errorLine1="    private List&lt;ItemInfo> mRecommendedWidgets = List.of();"
+        errorLine2="                                                      ~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/popup/PopupDataProvider.java"
+            line="66"
+            column="55"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `new android.view.SurfaceControlViewHost`"
+        errorLine1="                    .submit(() -> new SurfaceControlViewHost(mContext, mDisplay, mHostToken))"
+        errorLine2="                                  ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java"
+            line="91"
+            column="35"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `android.view.SurfaceControlViewHost#getSurfacePackage`"
+        errorLine1="            surfacePackage = mSurfaceControlViewHost.getSurfacePackage();"
+        errorLine2="                                                     ~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java"
+            line="93"
+            column="54"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `android.view.SurfaceControlViewHost#setView`"
+        errorLine1="                host.setView(view, view.getMeasuredWidth(), view.getMeasuredHeight());"
+        errorLine2="                     ~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java"
+            line="127"
+            column="22"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Cast from `SurfacePackage` to `Parcelable` requires API level 30 (current min is 29)"
+        errorLine1="        result.putParcelable(KEY_SURFACE_PACKAGE, surfacePackage);"
+        errorLine2="                                                  ~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java"
+            line="132"
+            column="51"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `android.view.SurfaceControlViewHost#release`"
+        errorLine1="                mSurfaceControlViewHost.release();"
+        errorLine2="                                        ~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java"
+            line="149"
+            column="41"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `android.graphics.Outline#setPath`"
+        errorLine1="        outline.setPath(mPath);"
+        errorLine2="                ~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/popup/RoundedArrowDrawable.java"
+            line="88"
+            column="17"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 29): `new android.widget.EdgeEffect`"
+        errorLine1="                ? new EdgeEffect(context, attrs) : new EdgeEffect(context);"
+        errorLine2="                  ~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/views/SpringRelativeLayout.java"
+            line="49"
+            column="19"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 29): `new android.widget.EdgeEffect`"
+        errorLine1="                ? new EdgeEffect(context, attrs) : new EdgeEffect(context);"
+        errorLine2="                  ~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/views/SpringRelativeLayout.java"
+            line="51"
+            column="19"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `android.view.WindowManager.LayoutParams#setFitInsetsTypes`"
+        errorLine1="        mWindowLayoutParams.setFitInsetsTypes(0);"
+        errorLine2="                            ~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java"
+            line="316"
+            column="29"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `android.view.WindowInsets#getInsets`"
+        errorLine1="            Insets systemInsets = insets.getInsets(WindowInsets.Type.systemBars());"
+        errorLine2="                                         ~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java"
+            line="118"
+            column="42"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `android.view.WindowInsets.Type#systemBars`"
+        errorLine1="            Insets systemInsets = insets.getInsets(WindowInsets.Type.systemBars());"
+        errorLine2="                                                                     ~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java"
+            line="118"
+            column="70"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 29): `android.appwidget.AppWidgetProviderInfo#loadDescription`"
+        errorLine1="            CharSequence description = mItem.widgetInfo.loadDescription(getContext());"
+        errorLine2="                                                        ~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/WidgetCell.java"
+            line="193"
+            column="57"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 31 (current min is 29): `android.appwidget.AppWidgetProviderInfo#previewLayout`"
+        errorLine1="                &amp;&amp; item.widgetInfo.previewLayout != Resources.ID_NULL) {"
+        errorLine2="                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/WidgetCell.java"
+            line="214"
+            column="20"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 31 (current min is 29): `android.appwidget.AppWidgetProviderInfo#previewLayout`"
+        errorLine1="            launcherAppWidgetProviderInfo.initialLayout = item.widgetInfo.previewLayout;"
+        errorLine2="                                                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/WidgetCell.java"
+            line="222"
+            column="59"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `android.view.View#getWindowInsetsController`"
+        errorLine1="        getWindowInsetsController().hide(WindowInsets.Type.ime());"
+        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java"
+            line="558"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `android.view.WindowInsets.Type#ime`"
+        errorLine1="        getWindowInsetsController().hide(WindowInsets.Type.ime());"
+        errorLine2="                                                           ~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java"
+            line="558"
+            column="60"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `android.view.WindowInsetsController#hide`"
+        errorLine1="        getWindowInsetsController().hide(WindowInsets.Type.ime());"
+        errorLine2="                                    ~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java"
+            line="558"
+            column="37"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `java.util.List#of`"
+        errorLine1="            return new RecommendationTableData(List.of(), previewScale);"
+        errorLine2="                                                    ~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java"
+            line="139"
+            column="53"/>
+    </issue>
+
+</issues>
diff --git a/lint-baseline-launcher3.xml b/lint-baseline-launcher3.xml
new file mode 100644
index 0000000..469ad94
--- /dev/null
+++ b/lint-baseline-launcher3.xml
@@ -0,0 +1,576 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0">
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 26): `android.view.View#getWindowInsetsController`"
+        errorLine1="        getWindowInsetsController().hide(WindowInsets.Type.ime());"
+        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/allapps/AllAppsContainerView.java"
+            line="203"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 26): `android.view.WindowInsets.Type#ime`"
+        errorLine1="        getWindowInsetsController().hide(WindowInsets.Type.ime());"
+        errorLine2="                                                           ~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/allapps/AllAppsContainerView.java"
+            line="203"
+            column="60"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 26): `android.view.WindowInsetsController#hide`"
+        errorLine1="        getWindowInsetsController().hide(WindowInsets.Type.ime());"
+        errorLine2="                                    ~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/allapps/AllAppsContainerView.java"
+            line="203"
+            column="37"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 26): `android.view.View#getWindowInsetsController`"
+        errorLine1="                getWindowInsetsController().hide(WindowInsets.Type.ime());"
+        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/allapps/AllAppsRecyclerView.java"
+            line="193"
+            column="17"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 26): `android.view.WindowInsets.Type#ime`"
+        errorLine1="                getWindowInsetsController().hide(WindowInsets.Type.ime());"
+        errorLine2="                                                                   ~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/allapps/AllAppsRecyclerView.java"
+            line="193"
+            column="68"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 26): `android.view.WindowInsetsController#hide`"
+        errorLine1="                getWindowInsetsController().hide(WindowInsets.Type.ime());"
+        errorLine2="                                            ~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/allapps/AllAppsRecyclerView.java"
+            line="193"
+            column="45"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 26): `android.appwidget.AppWidgetHostView#updateAppWidgetSize`"
+        errorLine1="            widgetView.updateAppWidgetSize(new Bundle(), sizes);"
+        errorLine2="                       ~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/AppWidgetResizeFrame.java"
+            line="399"
+            column="24"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 26): `android.view.WindowManager#getCurrentWindowMetrics`"
+        errorLine1="                    .getCurrentWindowMetrics().getWindowInsets();"
+        errorLine2="                     ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/DeviceProfile.java"
+            line="236"
+            column="22"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 26): `android.view.WindowMetrics#getWindowInsets`"
+        errorLine1="                    .getCurrentWindowMetrics().getWindowInsets();"
+        errorLine2="                                               ~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/DeviceProfile.java"
+            line="236"
+            column="48"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 29 (current min is 26): `android.content.res.Resources#getFloat`"
+        errorLine1="        folderLabelTextScale = res.getFloat(R.dimen.folder_label_text_scale);"
+        errorLine2="                                   ~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/DeviceProfile.java"
+            line="256"
+            column="36"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 29 (current min is 26): `android.content.res.Resources#getFloat`"
+        errorLine1="        return mContext.getResources().getFloat(resId);"
+        errorLine2="                                       ~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/util/DynamicResource.java"
+            line="73"
+            column="40"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 26): `android.appwidget.AppWidgetHostView#resetColorResources`"
+        errorLine1="            resetColorResources();"
+        errorLine2="            ~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java"
+            line="137"
+            column="13"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 26): `java.util.List#of`"
+        errorLine1="            mColorExtractor.addLocation(List.of(mLastLocationRegistered));"
+        errorLine2="                                             ~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java"
+            line="367"
+            column="46"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 26): `java.util.List#of`"
+        errorLine1="                mColorExtractor.addLocation(List.of(mLastLocationRegistered));"
+        errorLine2="                                                 ~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java"
+            line="390"
+            column="50"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 31 (current min is 26): `android.appwidget.AppWidgetProviderInfo#maxResizeWidth`"
+        errorLine1="                (ATLEAST_S &amp;&amp; maxResizeWidth > 0)"
+        errorLine2="                              ~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java"
+            line="91"
+            column="31"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 31 (current min is 26): `android.appwidget.AppWidgetProviderInfo#maxResizeWidth`"
+        errorLine1="                        ? getSpanX(widgetPadding, maxResizeWidth, smallestCellWidth)"
+        errorLine2="                                                  ~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java"
+            line="92"
+            column="51"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 31 (current min is 26): `android.appwidget.AppWidgetProviderInfo#maxResizeHeight`"
+        errorLine1="                (ATLEAST_S &amp;&amp; maxResizeHeight > 0)"
+        errorLine2="                              ~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java"
+            line="95"
+            column="31"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 31 (current min is 26): `android.appwidget.AppWidgetProviderInfo#maxResizeHeight`"
+        errorLine1="                        ? getSpanY(widgetPadding, maxResizeHeight, smallestCellHeight)"
+        errorLine2="                                                  ~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java"
+            line="96"
+            column="51"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 31 (current min is 26): `android.appwidget.AppWidgetProviderInfo#targetCellWidth`"
+        errorLine1="        if (ATLEAST_S &amp;&amp; targetCellWidth >= minSpanX &amp;&amp; targetCellWidth &lt;= maxSpanX"
+        errorLine2="                         ~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java"
+            line="101"
+            column="26"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 31 (current min is 26): `android.appwidget.AppWidgetProviderInfo#targetCellWidth`"
+        errorLine1="        if (ATLEAST_S &amp;&amp; targetCellWidth >= minSpanX &amp;&amp; targetCellWidth &lt;= maxSpanX"
+        errorLine2="                                                        ~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java"
+            line="101"
+            column="57"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 31 (current min is 26): `android.appwidget.AppWidgetProviderInfo#targetCellHeight`"
+        errorLine1="                &amp;&amp; targetCellHeight >= minSpanY &amp;&amp; targetCellHeight &lt;= maxSpanY) {"
+        errorLine2="                   ~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java"
+            line="102"
+            column="20"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 31 (current min is 26): `android.appwidget.AppWidgetProviderInfo#targetCellHeight`"
+        errorLine1="                &amp;&amp; targetCellHeight >= minSpanY &amp;&amp; targetCellHeight &lt;= maxSpanY) {"
+        errorLine2="                                                   ~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java"
+            line="102"
+            column="52"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 31 (current min is 26): `android.appwidget.AppWidgetProviderInfo#targetCellWidth`"
+        errorLine1="            spanX = targetCellWidth;"
+        errorLine2="                    ~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java"
+            line="103"
+            column="21"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 31 (current min is 26): `android.appwidget.AppWidgetProviderInfo#targetCellHeight`"
+        errorLine1="            spanY = targetCellHeight;"
+        errorLine2="                    ~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java"
+            line="104"
+            column="21"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 28 (current min is 26): `android.app.Person#getKey`"
+        errorLine1="        return people.stream().filter(person -> person.getKey() != null)"
+        errorLine2="                                                       ~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/notification/NotificationKeyData.java"
+            line="72"
+            column="56"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Method reference requires API level 28 (current min is 26): `Person::getKey`"
+        errorLine1="                .map(Person::getKey).sorted().toArray(String[]::new);"
+        errorLine2="                     ~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/notification/NotificationKeyData.java"
+            line="73"
+            column="22"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 26): `android.content.pm.LauncherActivityInfo#getLoadingProgress`"
+        errorLine1="            return (int) (100 * info.getLoadingProgress());"
+        errorLine2="                                     ~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/util/PackageManagerHelper.java"
+            line="338"
+            column="38"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 29 (current min is 26): `android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_PAGE_LEFT`"
+        errorLine1="                AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_LEFT"
+        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/PagedView.java"
+            line="1752"
+            column="17"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 29 (current min is 26): `android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_PAGE_RIGHT`"
+        errorLine1="                : AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_RIGHT);"
+        errorLine2="                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/PagedView.java"
+            line="1753"
+            column="19"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 29 (current min is 26): `android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_PAGE_RIGHT`"
+        errorLine1="                AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_RIGHT"
+        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/PagedView.java"
+            line="1760"
+            column="17"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 29 (current min is 26): `android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_PAGE_LEFT`"
+        errorLine1="                : AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_LEFT);"
+        errorLine2="                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/PagedView.java"
+            line="1761"
+            column="19"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 26): `java.util.List#of`"
+        errorLine1="    private List&lt;WidgetsListBaseEntry> mAllWidgets = List.of();"
+        errorLine2="                                                          ~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/popup/PopupDataProvider.java"
+            line="64"
+            column="59"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 26): `java.util.List#of`"
+        errorLine1="    private List&lt;ItemInfo> mRecommendedWidgets = List.of();"
+        errorLine2="                                                      ~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/popup/PopupDataProvider.java"
+            line="66"
+            column="55"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 26): `new android.view.SurfaceControlViewHost`"
+        errorLine1="                    .submit(() -> new SurfaceControlViewHost(mContext, mDisplay, mHostToken))"
+        errorLine2="                                  ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java"
+            line="91"
+            column="35"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 26): `android.view.SurfaceControlViewHost#getSurfacePackage`"
+        errorLine1="            surfacePackage = mSurfaceControlViewHost.getSurfacePackage();"
+        errorLine2="                                                     ~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java"
+            line="93"
+            column="54"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 26): `android.view.SurfaceControlViewHost#setView`"
+        errorLine1="                host.setView(view, view.getMeasuredWidth(), view.getMeasuredHeight());"
+        errorLine2="                     ~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java"
+            line="127"
+            column="22"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Cast from `SurfacePackage` to `Parcelable` requires API level 30 (current min is 26)"
+        errorLine1="        result.putParcelable(KEY_SURFACE_PACKAGE, surfacePackage);"
+        errorLine2="                                                  ~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java"
+            line="132"
+            column="51"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 26): `android.view.SurfaceControlViewHost#release`"
+        errorLine1="                mSurfaceControlViewHost.release();"
+        errorLine2="                                        ~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java"
+            line="149"
+            column="41"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 26): `android.graphics.Outline#setPath`"
+        errorLine1="        outline.setPath(mPath);"
+        errorLine2="                ~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/popup/RoundedArrowDrawable.java"
+            line="88"
+            column="17"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 26): `new android.widget.EdgeEffect`"
+        errorLine1="                ? new EdgeEffect(context, attrs) : new EdgeEffect(context);"
+        errorLine2="                  ~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/views/SpringRelativeLayout.java"
+            line="49"
+            column="19"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 26): `new android.widget.EdgeEffect`"
+        errorLine1="                ? new EdgeEffect(context, attrs) : new EdgeEffect(context);"
+        errorLine2="                  ~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/views/SpringRelativeLayout.java"
+            line="51"
+            column="19"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 29 (current min is 26): `android.view.WindowInsets#getTappableElementInsets`"
+        errorLine1="            return windowInsets.getTappableElementInsets().bottom > 0;"
+        errorLine2="                                ~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/graphics/SysUiScrim.java"
+            line="190"
+            column="33"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 29 (current min is 26): `android.graphics.Insets#bottom`"
+        errorLine1="            return windowInsets.getTappableElementInsets().bottom > 0;"
+        errorLine2="                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/graphics/SysUiScrim.java"
+            line="190"
+            column="20"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 28 (current min is 26): `android.appwidget.AppWidgetProviderInfo#widgetFeatures`"
+        errorLine1="        int featureFlags = mProviderInfo.widgetFeatures;"
+        errorLine2="                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/WidgetAddFlowHandler.java"
+            line="93"
+            column="28"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 26): `android.appwidget.AppWidgetProviderInfo#loadDescription`"
+        errorLine1="            CharSequence description = mItem.widgetInfo.loadDescription(getContext());"
+        errorLine2="                                                        ~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/WidgetCell.java"
+            line="193"
+            column="57"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 31 (current min is 26): `android.appwidget.AppWidgetProviderInfo#previewLayout`"
+        errorLine1="                &amp;&amp; item.widgetInfo.previewLayout != Resources.ID_NULL) {"
+        errorLine2="                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/WidgetCell.java"
+            line="214"
+            column="20"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 31 (current min is 26): `android.appwidget.AppWidgetProviderInfo#previewLayout`"
+        errorLine1="            launcherAppWidgetProviderInfo.initialLayout = item.widgetInfo.previewLayout;"
+        errorLine2="                                                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/WidgetCell.java"
+            line="222"
+            column="59"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 26): `android.view.View#getWindowInsetsController`"
+        errorLine1="        getWindowInsetsController().hide(WindowInsets.Type.ime());"
+        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java"
+            line="558"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 26): `android.view.WindowInsets.Type#ime`"
+        errorLine1="        getWindowInsetsController().hide(WindowInsets.Type.ime());"
+        errorLine2="                                                           ~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java"
+            line="558"
+            column="60"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 26): `android.view.WindowInsetsController#hide`"
+        errorLine1="        getWindowInsetsController().hide(WindowInsets.Type.ime());"
+        errorLine2="                                    ~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java"
+            line="558"
+            column="37"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 26): `java.util.List#of`"
+        errorLine1="            return new RecommendationTableData(List.of(), previewScale);"
+        errorLine2="                                                    ~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java"
+            line="139"
+            column="53"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Method reference requires API level 28 (current min is 26): `Person::getKey`"
+        errorLine1="            : Arrays.stream(persons).map(Person::getKey).sorted().toArray(String[]::new);"
+        errorLine2="                                         ~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/model/data/WorkspaceItemInfo.java"
+            line="178"
+            column="42"/>
+    </issue>
+
+</issues>
diff --git a/lint-baseline-res-lib.xml b/lint-baseline-res-lib.xml
new file mode 100644
index 0000000..e52f8fb
--- /dev/null
+++ b/lint-baseline-res-lib.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0">
+
+    <issue
+        id="NewApi"
+        message="`?android:attr/dialogCornerRadius` requires API level 28 (current min is 26)"
+        errorLine1="        android:topLeftRadius=&quot;?android:attr/dialogCornerRadius&quot;"
+        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/res/drawable/add_item_dialog_background.xml"
+            line="6"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="`?android:attr/dialogCornerRadius` requires API level 28 (current min is 26)"
+        errorLine1="        android:topRightRadius=&quot;?android:attr/dialogCornerRadius&quot; />"
+        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/res/drawable/add_item_dialog_background.xml"
+            line="7"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="`@android:style/Widget.DeviceDefault.Button.Colored` requires API level 28 (current min is 26)"
+        errorLine1="    &lt;style name=&quot;Widget.DeviceDefault.Button.Rounded.Colored&quot; parent=&quot;@android:style/Widget.DeviceDefault.Button.Colored&quot;>"
+        errorLine2="                                                              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/res/values/styles.xml"
+            line="287"
+            column="63"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="`@android:dimen/system_app_widget_background_radius` requires API level 31 (current min is 26)"
+        errorLine1="    &lt;corners android:radius=&quot;@android:dimen/system_app_widget_background_radius&quot; />"
+        errorLine2="             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/res/drawable/widget_resize_frame.xml"
+            line="20"
+            column="14"/>
+    </issue>
+
+</issues>
diff --git a/quickstep/res/layout/taskbar.xml b/quickstep/res/layout/taskbar.xml
index 84e2304..732222a 100644
--- a/quickstep/res/layout/taskbar.xml
+++ b/quickstep/res/layout/taskbar.xml
@@ -24,7 +24,6 @@
         android:id="@+id/taskbar_view"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:background="@color/taskbar_background"
         android:gravity="center"/>
 
 </com.android.launcher3.taskbar.TaskbarContainerView>
\ No newline at end of file
diff --git a/quickstep/res/layout/taskbar_divider.xml b/quickstep/res/layout/taskbar_divider.xml
deleted file mode 100644
index 87649f7..0000000
--- a/quickstep/res/layout/taskbar_divider.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?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.
--->
-
-<View
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="@dimen/taskbar_divider_thickness"
-    android:layout_height="@dimen/taskbar_divider_height"
-    android:background="@color/taskbar_divider" />
\ No newline at end of file
diff --git a/quickstep/res/values/colors.xml b/quickstep/res/values/colors.xml
index 54730f1..3bc8ddc 100644
--- a/quickstep/res/values/colors.xml
+++ b/quickstep/res/values/colors.xml
@@ -27,5 +27,4 @@
 
     <!-- Taskbar -->
     <color name="taskbar_background">#101010</color>
-    <color name="taskbar_divider">#C0C0C0</color>
 </resources>
\ No newline at end of file
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 3e6c78f..99be502 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -134,7 +134,5 @@
     <dimen name="taskbar_icon_drag_icon_size">54dp</dimen>
     <!-- Note that this applies to both sides of all icons, so visible space is double this. -->
     <dimen name="taskbar_icon_spacing">8dp</dimen>
-    <dimen name="taskbar_divider_thickness">1dp</dimen>
-    <dimen name="taskbar_divider_height">32dp</dimen>
     <dimen name="taskbar_folder_margin">16dp</dimen>
 </resources>
diff --git a/quickstep/robolectric_tests/src/com/android/quickstep/OrientationTouchTransformerTest.java b/quickstep/robolectric_tests/src/com/android/quickstep/OrientationTouchTransformerTest.java
index eca27b5..e771962 100644
--- a/quickstep/robolectric_tests/src/com/android/quickstep/OrientationTouchTransformerTest.java
+++ b/quickstep/robolectric_tests/src/com/android/quickstep/OrientationTouchTransformerTest.java
@@ -247,10 +247,7 @@
     }
 
     @Test
-    @Ignore("There's too much that goes into needing to mock a real motion event so the "
-            + "transforms in native code get applied correctly. Once that happens then maybe we can"
-            + " write slightly more complex unit tests")
-    public void applyTransform_taskNotFrozen_90Rotate_inTwoRegions() {
+    public void applyTransform_taskNotFrozen_90Rotate_withTwoRegions() {
         mTouchTransformer.createOrAddTouchRegion(mInfo);
         mTouchTransformer.enableMultipleRegions(true, mInfo);
         mTouchTransformer
@@ -262,6 +259,7 @@
         // Portrait point in landscape orientation axis
         MotionEvent inRegion2 = generateMotionEvent(MotionEvent.ACTION_DOWN, 10, 10);
         mTouchTransformer.transform(inRegion1_down);
+        // no-op
         mTouchTransformer.transform(inRegion2);
         assertTrue(mTouchTransformer.touchInValidSwipeRegions(
                 inRegion1_down.getX(), inRegion1_down.getY()));
@@ -269,9 +267,19 @@
         assertFalse(mTouchTransformer.touchInValidSwipeRegions(inRegion2.getX(), inRegion2.getY()));
 
         mTouchTransformer.transform(inRegion1_up);
+    }
 
-        // Set the new region with this MotionEvent.ACTION_DOWN
-        inRegion2 = generateAndTransformMotionEvent(MotionEvent.ACTION_DOWN, 10, 370);
+    @Test
+    public void applyTransform_90Rotate_inRotatedRegion() {
+        // Create regions for both 0 Rotation and 90 Rotation
+        mTouchTransformer.createOrAddTouchRegion(mInfo);
+        mTouchTransformer.enableMultipleRegions(true, mInfo);
+        mTouchTransformer
+                .createOrAddTouchRegion(createDisplayInfo(NORMAL_SCREEN_SIZE, Surface.ROTATION_90));
+        // Portrait point in landscape orientation axis
+        float x1 = generateTouchRegionHeight(NORMAL_SCREEN_SIZE, Surface.ROTATION_0);
+        // bottom of screen, from landscape perspective right side of screen
+        MotionEvent inRegion2 = generateAndTransformMotionEvent(MotionEvent.ACTION_DOWN, x1, 370);
         assertTrue(mTouchTransformer.touchInValidSwipeRegions(inRegion2.getX(), inRegion2.getY()));
     }
 
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index e9ded8a..0524b21 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -108,6 +108,7 @@
         SysUINavigationMode.INSTANCE.get(this).removeModeChangeListener(this);
         if (mTaskbarController != null) {
             mTaskbarController.cleanup();
+            mTaskbarController = null;
         }
 
         super.onDestroy();
@@ -219,7 +220,7 @@
         mSplitPlaceholderView = findViewById(R.id.split_placeholder);
         RecentsView overviewPanel = (RecentsView) getOverviewPanel();
         mSplitPlaceholderView.init(
-                new SplitSelectStateController(SystemUiProxy.INSTANCE.get(this))
+                new SplitSelectStateController(mHandler, SystemUiProxy.INSTANCE.get(this))
         );
         overviewPanel.init(mActionsView, mSplitPlaceholderView);
         mActionsView.updateVerticalMargin(SysUINavigationMode.getMode(this));
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 82a83fc..009ca27 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -61,6 +61,8 @@
 import android.os.SystemProperties;
 import android.util.Pair;
 import android.view.View;
+import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -137,6 +139,15 @@
     private static final long APP_LAUNCH_ALPHA_DOWN_DURATION =
             (long) (APP_LAUNCH_ALPHA_DURATION * APP_LAUNCH_DOWN_DUR_SCALE_FACTOR);
 
+    public static final int ANIMATION_NAV_FADE_IN_DURATION = 266;
+    public static final int ANIMATION_NAV_FADE_OUT_DURATION = 133;
+    public static final long ANIMATION_DELAY_NAV_FADE_IN =
+            APP_LAUNCH_DURATION - ANIMATION_NAV_FADE_IN_DURATION;
+    public static final Interpolator NAV_FADE_IN_INTERPOLATOR =
+            new PathInterpolator(0f, 0f, 0f, 1f);
+    public static final Interpolator NAV_FADE_OUT_INTERPOLATOR =
+            new PathInterpolator(0.2f, 0f, 1f, 1f);
+
     private static final long CROP_DURATION = 375;
     private static final long RADIUS_DURATION = 375;
 
@@ -276,10 +287,11 @@
      */
     protected void composeRecentsLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
             @NonNull RemoteAnimationTargetCompat[] appTargets,
-            @NonNull RemoteAnimationTargetCompat[] wallpaperTargets, boolean launcherClosing) {
+            @NonNull RemoteAnimationTargetCompat[] wallpaperTargets,
+            @NonNull RemoteAnimationTargetCompat[] nonAppTargets, boolean launcherClosing) {
         TaskViewUtils.composeRecentsLaunchAnimator(anim, v, appTargets, wallpaperTargets,
-                launcherClosing, mLauncher.getStateManager(), mLauncher.getOverviewPanel(),
-                mLauncher.getDepthController());
+                nonAppTargets, launcherClosing, mLauncher.getStateManager(),
+                mLauncher.getOverviewPanel(), mLauncher.getDepthController());
     }
 
     private boolean areAllTargetsTranslucent(@NonNull RemoteAnimationTargetCompat[] targets) {
@@ -305,6 +317,7 @@
     private void composeIconLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
             @NonNull RemoteAnimationTargetCompat[] appTargets,
             @NonNull RemoteAnimationTargetCompat[] wallpaperTargets,
+            @NonNull RemoteAnimationTargetCompat[] nonAppTargets,
             boolean launcherClosing) {
         // Set the state animation first so that any state listeners are called
         // before our internal listeners.
@@ -313,8 +326,8 @@
         final int rotationChange = getRotationChange(appTargets);
         // Note: the targetBounds are relative to the launcher
         Rect windowTargetBounds = getWindowTargetBounds(appTargets, rotationChange);
-        anim.play(getOpeningWindowAnimators(v, appTargets, wallpaperTargets, windowTargetBounds,
-                areAllTargetsTranslucent(appTargets), rotationChange));
+        anim.play(getOpeningWindowAnimators(v, appTargets, wallpaperTargets, nonAppTargets,
+                windowTargetBounds, areAllTargetsTranslucent(appTargets), rotationChange));
         if (launcherClosing) {
             Pair<AnimatorSet, Runnable> launcherContentAnimator =
                     getLauncherContentAnimator(true /* isAppOpening */,
@@ -515,6 +528,7 @@
     private Animator getOpeningWindowAnimators(View v,
             RemoteAnimationTargetCompat[] appTargets,
             RemoteAnimationTargetCompat[] wallpaperTargets,
+            RemoteAnimationTargetCompat[] nonAppTargets,
             Rect windowTargetBounds, boolean appTargetsAreTranslucent, int rotationChange) {
         RectF launcherIconBounds = new RectF();
         FloatingIconView floatingView = FloatingIconView.getFloatingIconView(mLauncher, v,
@@ -523,10 +537,11 @@
         Matrix matrix = new Matrix();
 
         RemoteAnimationTargets openingTargets = new RemoteAnimationTargets(appTargets,
-                wallpaperTargets, MODE_OPENING);
+                wallpaperTargets, nonAppTargets, MODE_OPENING);
         SurfaceTransactionApplier surfaceApplier =
                 new SurfaceTransactionApplier(floatingView);
         openingTargets.addReleaseCheck(surfaceApplier);
+        RemoteAnimationTargetCompat navBarTarget = openingTargets.getNavBarRemoteAnimationTarget();
 
         int[] dragLayerBounds = new int[2];
         mDragLayer.getLocationOnScreen(dragLayerBounds);
@@ -601,6 +616,11 @@
             FloatProp mCropRectHeight = new FloatProp(prop.cropHeightStart, prop.cropHeightEnd, 0,
                     CROP_DURATION, EXAGGERATED_EASE);
 
+            FloatProp mNavFadeOut = new FloatProp(1f, 0f, 0, ANIMATION_NAV_FADE_OUT_DURATION,
+                    NAV_FADE_OUT_INTERPOLATOR);
+            FloatProp mNavFadeIn = new FloatProp(0f, 1f, ANIMATION_DELAY_NAV_FADE_IN,
+                    ANIMATION_NAV_FADE_IN_DURATION, NAV_FADE_IN_INTERPOLATOR);
+
             @Override
             public void onUpdate(float percent) {
                 // Calculate the size of the scaled icon.
@@ -706,6 +726,21 @@
                     params[i] = builder.build();
                 }
                 surfaceApplier.scheduleApply(params);
+
+                if (navBarTarget != null) {
+                    final SurfaceParams.Builder navBuilder =
+                            new SurfaceParams.Builder(navBarTarget.leash);
+                    if (mNavFadeIn.value > mNavFadeIn.getStartValue()) {
+                        matrix.setScale(scale, scale);
+                        matrix.postTranslate(windowTransX0, windowTransY0);
+                        navBuilder.withMatrix(matrix)
+                                .withWindowCrop(crop)
+                                .withAlpha(mNavFadeIn.value);
+                    } else {
+                        navBuilder.withAlpha(mNavFadeOut.value);
+                    }
+                    surfaceApplier.scheduleApply(navBuilder.build());
+                }
             }
         });
 
@@ -1088,19 +1123,18 @@
                 RemoteAnimationTargetCompat[] nonAppTargets,
                 LauncherAnimationRunner.AnimationResult result) {
             AnimatorSet anim = new AnimatorSet();
-
             boolean launcherClosing =
                     launcherIsATargetWithMode(appTargets, MODE_CLOSING);
 
             final boolean launchingFromRecents = isLaunchingFromRecents(mV, appTargets);
             final boolean launchingFromTaskbar = mLauncher.isViewInTaskbar(mV);
             if (launchingFromRecents) {
-                composeRecentsLaunchAnimator(anim, mV, appTargets, wallpaperTargets,
+                composeRecentsLaunchAnimator(anim, mV, appTargets, wallpaperTargets, nonAppTargets,
                         launcherClosing);
             } else if (launchingFromTaskbar) {
                 // TODO
             } else {
-                composeIconLaunchAnimator(anim, mV, appTargets, wallpaperTargets,
+                composeIconLaunchAnimator(anim, mV, appTargets, wallpaperTargets, nonAppTargets,
                         launcherClosing);
             }
 
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
index 4451e7a..a6844e4 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
@@ -89,7 +89,7 @@
         ArrayList<WorkspaceItemInfo> putIntoFolder = new ArrayList<>();
 
         //separate folders and items that can get in folders
-        for (int i = 0; i < mLauncher.getDeviceProfile().inv.numHotseatIcons; i++) {
+        for (int i = 0; i < mLauncher.getDeviceProfile().numShownHotseatIcons; i++) {
             View view = mHotseat.getChildAt(i, 0);
             if (view == null) continue;
             ItemInfo info = (ItemInfo) view.getTag();
@@ -188,7 +188,7 @@
                     .getInt(LauncherSettings.Settings.EXTRA_VALUE);
             mNewScreens = IntArray.wrap(pageId);
         }
-        for (int i = 0; i < mLauncher.getDeviceProfile().inv.numHotseatIcons; i++) {
+        for (int i = 0; i < mLauncher.getDeviceProfile().numShownHotseatIcons; i++) {
             View child = mHotseat.getChildAt(i, 0);
             if (child == null || child.getTag() == null) continue;
             ItemInfo tag = (ItemInfo) child.getTag();
@@ -224,7 +224,7 @@
 
     void showDimissTip() {
         if (mHotseat.getShortcutsAndWidgets().getChildCount()
-                < mLauncher.getDeviceProfile().inv.numHotseatIcons) {
+                < mLauncher.getDeviceProfile().numShownHotseatIcons) {
             Snackbar.show(mLauncher, R.string.hotseat_tip_gaps_filled,
                     R.string.hotseat_prediction_settings, null,
                     () -> mLauncher.startActivity(getSettingsIntent()));
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
index c3677ea..a2ed211 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
@@ -88,7 +88,7 @@
         Rect padding = grid.getHotseatLayoutPadding();
 
         mSampleHotseat.getLayoutParams().height = grid.cellHeightPx;
-        mSampleHotseat.setGridSize(grid.inv.numHotseatIcons, 1);
+        mSampleHotseat.setGridSize(grid.numShownHotseatIcons, 1);
         mSampleHotseat.setPadding(padding.left, 0, padding.right, 0);
 
         Button turnOnBtn = findViewById(R.id.turn_predictions_on);
@@ -178,7 +178,7 @@
     }
 
     private void populatePreview(List<WorkspaceItemInfo> predictions) {
-        for (int i = 0; i < mLauncher.getDeviceProfile().inv.numHotseatIcons; i++) {
+        for (int i = 0; i < mLauncher.getDeviceProfile().numShownHotseatIcons; i++) {
             WorkspaceItemInfo info = predictions.get(i);
             PredictedAppIcon icon = PredictedAppIcon.createIcon(mSampleHotseat, info);
             icon.setEnabled(false);
@@ -194,7 +194,7 @@
      */
     public void show(List<WorkspaceItemInfo> predictions) {
         if (getParent() != null
-                || predictions.size() < mLauncher.getDeviceProfile().inv.numHotseatIcons
+                || predictions.size() < mLauncher.getDeviceProfile().numShownHotseatIcons
                 || mHotseatEduController == null) {
             return;
         }
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
index f297343..67ed5fb 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
@@ -33,10 +33,10 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.DragSource;
 import com.android.launcher3.DropTarget;
 import com.android.launcher3.Hotseat;
-import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.R;
 import com.android.launcher3.anim.AnimationSuccessListener;
@@ -68,7 +68,7 @@
  * pinning of predicted apps and manages replacement of predicted apps with user drag.
  */
 public class HotseatPredictionController implements DragController.DragListener,
-        SystemShortcut.Factory<QuickstepLauncher>, InvariantDeviceProfile.OnIDPChangeListener,
+        SystemShortcut.Factory<QuickstepLauncher>, DeviceProfile.OnDeviceProfileChangeListener,
         DragSource, ViewGroup.OnHierarchyChangeListener {
 
     private static final int FLAG_UPDATE_PAUSED = 1 << 0;
@@ -115,10 +115,10 @@
     public HotseatPredictionController(QuickstepLauncher launcher) {
         mLauncher = launcher;
         mHotseat = launcher.getHotseat();
-        mHotSeatItemsCount = mLauncher.getDeviceProfile().inv.numHotseatIcons;
+        mHotSeatItemsCount = mLauncher.getDeviceProfile().numShownHotseatIcons;
         mLauncher.getDragController().addDragListener(this);
 
-        launcher.getDeviceProfile().inv.addOnChangeListener(this);
+        launcher.addOnDeviceProfileChangeListener(this);
         mHotseat.getShortcutsAndWidgets().setOnHierarchyChangeListener(this);
     }
 
@@ -281,7 +281,7 @@
      * Unregisters callbacks and frees resources
      */
     public void destroy() {
-        mLauncher.getDeviceProfile().inv.removeOnChangeListener(this);
+        mLauncher.removeOnDeviceProfileChangeListener(this);
     }
 
     /**
@@ -446,8 +446,8 @@
     }
 
     @Override
-    public void onIdpChanged(int changeFlags, InvariantDeviceProfile profile) {
-        this.mHotSeatItemsCount = profile.numHotseatIcons;
+    public void onDeviceProfileChanged(DeviceProfile profile) {
+        this.mHotSeatItemsCount = profile.numShownHotseatIcons;
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java
index 90f762e..4956fa1 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java
@@ -44,7 +44,7 @@
                             .getBinder(LauncherSettings.Settings.EXTRA_VALUE)) {
                 InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
                 GridBackupTable backupTable = new GridBackupTable(context,
-                        transaction.getDb(), idp.numHotseatIcons, idp.numColumns,
+                        transaction.getDb(), idp.numDatabaseHotseatIcons, idp.numColumns,
                         idp.numRows);
                 backupTable.createCustomBackupTable(HYBRID_HOTSEAT_BACKUP_TABLE);
                 transaction.commit();
@@ -69,7 +69,7 @@
                 }
                 InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
                 GridBackupTable backupTable = new GridBackupTable(context,
-                        transaction.getDb(), idp.numHotseatIcons, idp.numColumns,
+                        transaction.getDb(), idp.numDatabaseHotseatIcons, idp.numColumns,
                         idp.numRows);
                 backupTable.restoreFromCustomBackupTable(HYBRID_HOTSEAT_BACKUP_TABLE, true);
                 transaction.commit();
diff --git a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
index df3657d..8c68872 100644
--- a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
+++ b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
@@ -111,7 +111,7 @@
         mDataModel.extraItems.put(CONTAINER_PREDICTION, mAllAppsState.items);
 
         WorkspaceItemFactory hotseatFactory =
-                new WorkspaceItemFactory(mApp, ums, pinnedShortcuts, mIDP.numHotseatIcons);
+                new WorkspaceItemFactory(mApp, ums, pinnedShortcuts, mIDP.numDatabaseHotseatIcons);
         mHotseatState.items.setItems(
                 mHotseatState.storage.read(mApp.getContext(), hotseatFactory, ums.allUsers::get));
         mDataModel.extraItems.put(CONTAINER_HOTSEAT_PREDICTION, mHotseatState.items);
@@ -211,7 +211,7 @@
         registerPredictor(mHotseatState, apm.createAppPredictionSession(
                 new AppPredictionContext.Builder(context)
                         .setUiSurface("hotseat")
-                        .setPredictedTargetCount(mIDP.numHotseatIcons)
+                        .setPredictedTargetCount(mIDP.numDatabaseHotseatIcons)
                         .setExtras(convertDataModelToAppTargetBundle(context, mDataModel))
                         .build()));
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarAnimationController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarAnimationController.java
index 7c54e2d..46e4506 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarAnimationController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarAnimationController.java
@@ -50,6 +50,10 @@
     private final AnimatedFloat mTaskbarScaleForLauncherState = new AnimatedFloat(
             this::updateScale);
 
+    // TranslationY.
+    private final AnimatedFloat mTaskbarTranslationYForLauncherState = new AnimatedFloat(
+            this::updateTranslationY);
+
     public TaskbarAnimationController(BaseQuickstepLauncher launcher,
             TaskbarController.TaskbarAnimationControllerCallbacks taskbarCallbacks) {
         mLauncher = launcher;
@@ -81,6 +85,10 @@
         return mTaskbarScaleForLauncherState;
     }
 
+    protected AnimatedFloat getTaskbarTranslationYForLauncherState() {
+        return mTaskbarTranslationYForLauncherState;
+    }
+
     protected Animator createAnimToBackgroundAlpha(float toAlpha, long duration) {
         return mTaskbarBackgroundAlpha.animateToValue(mTaskbarBackgroundAlpha.value, toAlpha)
                 .setDuration(duration);
@@ -95,6 +103,7 @@
         mTaskbarCallbacks.updateTaskbarBackgroundAlpha(mTaskbarBackgroundAlpha.value);
         updateVisibilityAlpha();
         updateScale();
+        updateTranslationY();
     }
 
     private void updateVisibilityAlpha() {
@@ -120,6 +129,15 @@
         mTaskbarCallbacks.updateTaskbarScale(scale);
     }
 
+    private void updateTranslationY() {
+        // We use mTaskbarBackgroundAlpha as a proxy for whether Launcher is resumed/paused, the
+        // assumption being that Taskbar should always be at translationY 0f regardless of the
+        // current LauncherState if Launcher is paused.
+        float translationY = mTaskbarTranslationYForLauncherState.value;
+        translationY = Utilities.mapRange(mTaskbarBackgroundAlpha.value, translationY, 0f);
+        mTaskbarCallbacks.updateTaskbarTranslationY(translationY);
+    }
+
     private void setNavBarButtonAlpha(float navBarAlpha) {
         SystemUiProxy.INSTANCE.get(mLauncher).setNavBarButtonAlpha(navBarAlpha, false);
     }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarContainerView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarContainerView.java
index ccf6b41..621bba7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarContainerView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarContainerView.java
@@ -19,6 +19,8 @@
 import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo.TOUCHABLE_INSETS_REGION;
 
 import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
 import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.view.View;
@@ -38,6 +40,7 @@
 
     private final int[] mTempLoc = new int[2];
     private final int mFolderMargin;
+    private final Paint mTaskbarBackgroundPaint;
 
     // Initialized in TaskbarController constructor.
     private TaskbarController.TaskbarContainerViewCallbacks mControllerCallbacks;
@@ -63,6 +66,8 @@
             int defStyleAttr, int defStyleRes) {
         super(context, attrs, 1 /* alphaChannelCount */);
         mFolderMargin = getResources().getDimensionPixelSize(R.dimen.taskbar_folder_margin);
+        mTaskbarBackgroundPaint = new Paint();
+        mTaskbarBackgroundPaint.setColor(getResources().getColor(R.color.taskbar_background));
     }
 
     protected void construct(TaskbarController.TaskbarContainerViewCallbacks callbacks) {
@@ -97,11 +102,14 @@
             // mTaskbarView is, since its position never changes and insets rather than overlays.
             int[] loc = mTempLoc;
             float scale = mTaskbarView.getScaleX();
+            float translationY = mTaskbarView.getTranslationY();
             mTaskbarView.setScaleX(1);
             mTaskbarView.setScaleY(1);
+            mTaskbarView.setTranslationY(0);
             mTaskbarView.getLocationInWindow(loc);
             mTaskbarView.setScaleX(scale);
             mTaskbarView.setScaleY(scale);
+            mTaskbarView.setTranslationY(translationY);
             insetsInfo.contentInsets.left = loc[0];
             insetsInfo.contentInsets.top = loc[1];
             insetsInfo.contentInsets.right = getWidth() - (loc[0] + mTaskbarView.getWidth());
@@ -153,4 +161,20 @@
     protected TaskbarActivityContext getTaskbarActivityContext() {
         return mActivity;
     }
+
+    @Override
+    protected void dispatchDraw(Canvas canvas) {
+        canvas.drawRect(0, canvas.getHeight() - mTaskbarView.getHeight(), canvas.getWidth(),
+                canvas.getHeight(), mTaskbarBackgroundPaint);
+        super.dispatchDraw(canvas);
+    }
+
+    /**
+     * Sets the alpha of the background color behind all the Taskbar contents.
+     * @param alpha 0 is fully transparent, 1 is fully opaque.
+     */
+    protected void setTaskbarBackgroundAlpha(float alpha) {
+        mTaskbarBackgroundPaint.setAlpha((int) (alpha * 255));
+        invalidate();
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java
index 559ede1..c93de00 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java
@@ -25,7 +25,6 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.app.ActivityOptions;
-import android.content.ComponentName;
 import android.graphics.PixelFormat;
 import android.graphics.Point;
 import android.graphics.Rect;
@@ -56,9 +55,6 @@
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.WindowManagerWrapper;
 
-import java.util.ArrayList;
-import java.util.List;
-
 /**
  * Interfaces with Launcher/WindowManager/SystemUI to determine what to show in TaskbarView.
  */
@@ -76,17 +72,11 @@
     private final TaskbarStateHandler mTaskbarStateHandler;
     private final TaskbarAnimationController mTaskbarAnimationController;
     private final TaskbarHotseatController mHotseatController;
-    private final TaskbarRecentsController mRecentsController;
     private final TaskbarDragController mDragController;
 
     // Initialized in init().
     private WindowManager.LayoutParams mWindowLayoutParams;
 
-    // Contains all loaded Tasks, not yet deduped from Hotseat items.
-    private List<Task> mLatestLoadedRecentTasks;
-    // Contains all loaded Hotseat items.
-    private ItemInfo[] mLatestLoadedHotseatItems;
-
     private @Nullable Animator mAnimator;
     private boolean mIsAnimatingToLauncher;
 
@@ -106,8 +96,6 @@
                 createTaskbarAnimationControllerCallbacks());
         mHotseatController = new TaskbarHotseatController(mLauncher,
                 createTaskbarHotseatControllerCallbacks());
-        mRecentsController = new TaskbarRecentsController(mLauncher,
-                createTaskbarRecentsControllerCallbacks());
         mDragController = new TaskbarDragController(mLauncher);
     }
 
@@ -115,7 +103,7 @@
         return new TaskbarAnimationControllerCallbacks() {
             @Override
             public void updateTaskbarBackgroundAlpha(float alpha) {
-                mTaskbarViewInApp.setBackgroundAlpha(alpha);
+                mTaskbarContainerView.setTaskbarBackgroundAlpha(alpha);
             }
 
             @Override
@@ -129,6 +117,18 @@
                 mTaskbarViewInApp.setScaleX(scale);
                 mTaskbarViewInApp.setScaleY(scale);
             }
+
+            @Override
+            public void updateTaskbarTranslationY(float translationY) {
+                if (translationY < 0) {
+                    // Resize to accommodate the max translation we'll reach.
+                    setTaskbarWindowHeight(mTaskbarSize.y
+                            + mLauncher.getHotseat().getTaskbarOffsetY());
+                } else {
+                    setTaskbarWindowHeight(mTaskbarSize.y);
+                }
+                mTaskbarViewInApp.setTranslationY(translationY);
+            }
         };
     }
 
@@ -220,24 +220,6 @@
             @Override
             public void updateHotseatItems(ItemInfo[] hotseatItemInfos) {
                 mTaskbarViewInApp.updateHotseatItems(hotseatItemInfos);
-                mLatestLoadedHotseatItems = hotseatItemInfos;
-                dedupeAndUpdateRecentItems();
-            }
-        };
-    }
-
-    private TaskbarRecentsControllerCallbacks createTaskbarRecentsControllerCallbacks() {
-        return new TaskbarRecentsControllerCallbacks() {
-            @Override
-            public void updateRecentItems(ArrayList<Task> recentTasks) {
-                mLatestLoadedRecentTasks = recentTasks;
-                dedupeAndUpdateRecentItems();
-            }
-
-            @Override
-            public void updateRecentTaskAtIndex(int taskIndex, Task task) {
-                mTaskbarViewInApp.updateRecentTaskAtIndex(taskIndex, task);
-                mTaskbarViewOnHome.updateRecentTaskAtIndex(taskIndex, task);
             }
         };
     }
@@ -246,16 +228,13 @@
      * Initializes the Taskbar, including adding it to the screen.
      */
     public void init() {
-        mTaskbarViewInApp.init(mHotseatController.getNumHotseatIcons(),
-                mRecentsController.getNumRecentIcons());
-        mTaskbarViewOnHome.init(mHotseatController.getNumHotseatIcons(),
-                mRecentsController.getNumRecentIcons());
+        mTaskbarViewInApp.init(mHotseatController.getNumHotseatIcons());
+        mTaskbarViewOnHome.init(mHotseatController.getNumHotseatIcons());
         mTaskbarContainerView.init(mTaskbarViewInApp);
         addToWindowManager();
         mTaskbarStateHandler.setTaskbarCallbacks(createTaskbarStateHandlerCallbacks());
         mTaskbarAnimationController.init();
         mHotseatController.init();
-        mRecentsController.init();
 
         setWhichTaskbarViewIsVisible(mLauncher.hasBeenResumed()
                 ? mTaskbarViewOnHome
@@ -273,6 +252,11 @@
             public AnimatedFloat getScaleTarget() {
                 return mTaskbarAnimationController.getTaskbarScaleForLauncherState();
             }
+
+            @Override
+            public AnimatedFloat getTranslationYTarget() {
+                return mTaskbarAnimationController.getTaskbarTranslationYForLauncherState();
+            }
         };
     }
 
@@ -292,7 +276,6 @@
         mTaskbarStateHandler.setTaskbarCallbacks(null);
         mTaskbarAnimationController.cleanup();
         mHotseatController.cleanup();
-        mRecentsController.cleanup();
 
         setWhichTaskbarViewIsVisible(null);
     }
@@ -425,53 +408,6 @@
         return mTaskbarViewInApp.isDraggingItem() || mTaskbarViewOnHome.isDraggingItem();
     }
 
-    private void dedupeAndUpdateRecentItems() {
-        if (mLatestLoadedRecentTasks == null || mLatestLoadedHotseatItems == null) {
-            return;
-        }
-
-        final int numRecentIcons = mRecentsController.getNumRecentIcons();
-
-        // From most recent to least recently opened.
-        List<Task> dedupedTasksInDescendingOrder = new ArrayList<>();
-        for (int i = mLatestLoadedRecentTasks.size() - 1; i >= 0; i--) {
-            Task task = mLatestLoadedRecentTasks.get(i);
-            boolean isTaskInHotseat = false;
-            for (ItemInfo hotseatItem : mLatestLoadedHotseatItems) {
-                if (hotseatItem == null) {
-                    continue;
-                }
-                ComponentName hotseatActivity = hotseatItem.getTargetComponent();
-                if (hotseatActivity != null && task.key.sourceComponent.getPackageName()
-                        .equals(hotseatActivity.getPackageName())) {
-                    isTaskInHotseat = true;
-                    break;
-                }
-            }
-            if (!isTaskInHotseat) {
-                dedupedTasksInDescendingOrder.add(task);
-                if (dedupedTasksInDescendingOrder.size() == numRecentIcons) {
-                    break;
-                }
-            }
-        }
-
-        // TaskbarView expects an array of all the recent tasks to show, in the order to show them.
-        // So we create an array of the proper size, then fill it in such that the most recent items
-        // are at the end. If there aren't enough elements to fill the array, leave them null.
-        Task[] tasksArray = new Task[numRecentIcons];
-        for (int i = 0; i < tasksArray.length; i++) {
-            Task task = i >= dedupedTasksInDescendingOrder.size()
-                    ? null
-                    : dedupedTasksInDescendingOrder.get(i);
-            tasksArray[tasksArray.length - 1 - i] = task;
-        }
-
-        mTaskbarViewInApp.updateRecentTasks(tasksArray);
-        mTaskbarViewOnHome.updateRecentTasks(tasksArray);
-        mRecentsController.loadIconsForTasks(tasksArray);
-    }
-
     /**
      * @return Whether the given View is in the same window as Taskbar.
      */
@@ -487,13 +423,15 @@
         Rect hotseatBounds = new Rect();
         DeviceProfile grid = mLauncher.getDeviceProfile();
         int hotseatHeight = grid.workspacePadding.bottom + grid.taskbarSize;
-        int hotseatTopDiff = hotseatHeight - grid.taskbarSize;
+        int taskbarOffset = mLauncher.getHotseat().getTaskbarOffsetY();
+        int hotseatTopDiff = hotseatHeight - grid.taskbarSize - taskbarOffset;
+        int hotseatBottomDiff = taskbarOffset;
 
         mTaskbarViewOnHome.getHotseatBounds().roundOut(hotseatBounds);
         mLauncher.getHotseat().setPadding(hotseatBounds.left,
                 hotseatBounds.top + hotseatTopDiff,
                 mTaskbarViewOnHome.getWidth() - hotseatBounds.right,
-                mTaskbarViewOnHome.getHeight() - hotseatBounds.bottom);
+                mTaskbarViewOnHome.getHeight() - hotseatBounds.bottom + hotseatBottomDiff);
     }
 
     private void setWhichTaskbarViewIsVisible(@Nullable TaskbarView visibleTaskbar) {
@@ -519,13 +457,15 @@
      * Updates the TaskbarContainer to MATCH_PARENT vs original Taskbar size.
      */
     private void setTaskbarWindowFullscreen(boolean fullscreen) {
-        if (fullscreen) {
-            mWindowLayoutParams.width = MATCH_PARENT;
-            mWindowLayoutParams.height = MATCH_PARENT;
-        } else {
-            mWindowLayoutParams.width = mTaskbarSize.x;
-            mWindowLayoutParams.height = mTaskbarSize.y;
-        }
+        setTaskbarWindowHeight(fullscreen ? MATCH_PARENT : mTaskbarSize.y);
+    }
+
+    /**
+     * Updates the TaskbarContainer height (pass mTaskbarSize.y to reset).
+     */
+    private void setTaskbarWindowHeight(int height) {
+        mWindowLayoutParams.width = mTaskbarSize.x;
+        mWindowLayoutParams.height = height;
         mWindowManager.updateViewLayout(mTaskbarContainerView, mWindowLayoutParams);
     }
 
@@ -535,6 +475,7 @@
     protected interface TaskbarStateHandlerCallbacks {
         AnimatedFloat getAlphaTarget();
         AnimatedFloat getScaleTarget();
+        AnimatedFloat getTranslationYTarget();
     }
 
     /**
@@ -545,6 +486,7 @@
         void updateTaskbarBackgroundAlpha(float alpha);
         void updateTaskbarVisibilityAlpha(float alpha);
         void updateTaskbarScale(float scale);
+        void updateTaskbarTranslationY(float translationY);
     }
 
     /**
@@ -573,12 +515,4 @@
     protected interface TaskbarHotseatControllerCallbacks {
         void updateHotseatItems(ItemInfo[] hotseatItemInfos);
     }
-
-    /**
-     * Contains methods that TaskbarRecentsController can call to interface with TaskbarController.
-     */
-    protected interface TaskbarRecentsControllerCallbacks {
-        void updateRecentItems(ArrayList<Task> recentTasks);
-        void updateRecentTaskAtIndex(int taskIndex, Task task);
-    }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarHotseatController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarHotseatController.java
index b1bafdb..68829cd 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarHotseatController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarHotseatController.java
@@ -52,7 +52,7 @@
         mLauncher = launcher;
         mHotseat = mLauncher.getHotseat();
         mTaskbarCallbacks = taskbarCallbacks;
-        mNumHotseatIcons = mLauncher.getDeviceProfile().inv.numHotseatIcons;
+        mNumHotseatIcons = mLauncher.getDeviceProfile().numShownHotseatIcons;
     }
 
     protected void init() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentsController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentsController.java
deleted file mode 100644
index 4256d2b..0000000
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentsController.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.taskbar;
-
-import com.android.launcher3.BaseQuickstepLauncher;
-import com.android.quickstep.RecentsModel;
-import com.android.quickstep.util.CancellableTask;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.system.TaskStackChangeListener;
-import com.android.systemui.shared.system.TaskStackChangeListeners;
-
-import java.util.ArrayList;
-
-/**
- * Works with TaskbarController to update the TaskbarView's Recent items.
- */
-public class TaskbarRecentsController {
-
-    private final int mNumRecentIcons = 2;
-    private final BaseQuickstepLauncher mLauncher;
-    private final TaskbarController.TaskbarRecentsControllerCallbacks mTaskbarCallbacks;
-    private final RecentsModel mRecentsModel;
-
-    private final TaskStackChangeListener mTaskStackChangeListener = new TaskStackChangeListener() {
-        @Override
-        public void onTaskStackChanged() {
-            reloadRecentTasksIfNeeded();
-        }
-    };
-
-    // TODO: add TaskbarVisualsChangedListener as well (for calendar/clock?)
-
-    // Used to keep track of the last requested task list id, so that we do not request to load the
-    // tasks again if we have already requested it and the task list has not changed
-    private int mTaskListChangeId = -1;
-
-    // The current background requests to load the task icons
-    private CancellableTask[] mIconLoadRequests = new CancellableTask[mNumRecentIcons];
-
-    private boolean mIsAlive;
-
-    public TaskbarRecentsController(BaseQuickstepLauncher launcher,
-            TaskbarController.TaskbarRecentsControllerCallbacks taskbarCallbacks) {
-        mLauncher = launcher;
-        mTaskbarCallbacks = taskbarCallbacks;
-        mRecentsModel = RecentsModel.INSTANCE.get(mLauncher);
-    }
-
-    protected void init() {
-        mIsAlive = true;
-        TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackChangeListener);
-        reloadRecentTasksIfNeeded();
-    }
-
-    protected void cleanup() {
-        mIsAlive = false;
-        TaskStackChangeListeners.getInstance().unregisterTaskStackListener(
-                mTaskStackChangeListener);
-        cancelAllPendingIconLoadTasks();
-    }
-
-    private void reloadRecentTasksIfNeeded() {
-        if (!mRecentsModel.isTaskListValid(mTaskListChangeId)) {
-            mTaskListChangeId = mRecentsModel.getTasks(this::onRecentTasksChanged);
-        }
-    }
-
-    private void cancelAllPendingIconLoadTasks() {
-        for (int i = 0; i < mIconLoadRequests.length; i++) {
-            if (mIconLoadRequests[i] != null) {
-                mIconLoadRequests[i].cancel();
-            }
-            mIconLoadRequests[i] = null;
-        }
-    }
-
-    private void onRecentTasksChanged(ArrayList<Task> tasks) {
-        if (mIsAlive) {
-            mTaskbarCallbacks.updateRecentItems(tasks);
-        }
-    }
-
-    /**
-     * For each Task, loads its icon from the cache in the background, then calls
-     * {@link TaskbarController.TaskbarRecentsControllerCallbacks#updateRecentTaskAtIndex}.
-     */
-    protected void loadIconsForTasks(Task[] tasks) {
-        cancelAllPendingIconLoadTasks();
-        for (int i = 0; i < tasks.length; i++) {
-            Task task = tasks[i];
-            if (task == null) {
-                continue;
-            }
-            final int taskIndex = i;
-            mIconLoadRequests[i] = mRecentsModel.getIconCache().updateIconInBackground(
-                    task, updatedTask -> onTaskIconLoaded(task, taskIndex));
-        }
-    }
-
-    private void onTaskIconLoaded(Task task, int taskIndex) {
-        mTaskbarCallbacks.updateRecentTaskAtIndex(taskIndex, task);
-    }
-
-    protected int getNumRecentIcons() {
-        return mNumRecentIcons;
-    }
-}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStateHandler.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStateHandler.java
index 9fc7d99..6ea51fa 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStateHandler.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStateHandler.java
@@ -16,6 +16,7 @@
 package com.android.launcher3.taskbar;
 
 import static com.android.launcher3.LauncherState.TASKBAR;
+import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 
 import androidx.annotation.Nullable;
@@ -54,9 +55,11 @@
 
         AnimatedFloat alphaTarget = mTaskbarCallbacks.getAlphaTarget();
         AnimatedFloat scaleTarget = mTaskbarCallbacks.getScaleTarget();
+        AnimatedFloat translationYTarget = mTaskbarCallbacks.getTranslationYTarget();
         boolean isTaskbarVisible = (state.getVisibleElements(mLauncher) & TASKBAR) != 0;
         alphaTarget.updateValue(isTaskbarVisible ? 1f : 0f);
         scaleTarget.updateValue(state.getTaskbarScale(mLauncher));
+        translationYTarget.updateValue(state.getTaskbarTranslationY(mLauncher));
     }
 
     @Override
@@ -68,9 +71,12 @@
 
         AnimatedFloat alphaTarget = mTaskbarCallbacks.getAlphaTarget();
         AnimatedFloat scaleTarget = mTaskbarCallbacks.getScaleTarget();
+        AnimatedFloat translationYTarget = mTaskbarCallbacks.getTranslationYTarget();
         boolean isTaskbarVisible = (toState.getVisibleElements(mLauncher) & TASKBAR) != 0;
         animation.setFloat(alphaTarget, AnimatedFloat.VALUE, isTaskbarVisible ? 1f : 0f, LINEAR);
         animation.setFloat(scaleTarget, AnimatedFloat.VALUE, toState.getTaskbarScale(mLauncher),
                 LINEAR);
+        animation.setFloat(translationYTarget, AnimatedFloat.VALUE,
+                toState.getTaskbarTranslationY(mLauncher), ACCEL_DEACCEL);
     }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index 21a2d51..60a7add 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -24,8 +24,6 @@
 import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.graphics.RectF;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.view.DragEvent;
 import android.view.MotionEvent;
@@ -47,16 +45,12 @@
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.views.ActivityContext;
-import com.android.systemui.shared.recents.model.Task;
 
 /**
  * Hosts the Taskbar content such as Hotseat and Recent Apps. Drawn on top of other apps.
  */
 public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconParent, Insettable {
 
-    private final ColorDrawable mBackgroundDrawable;
-    private final int mDividerWidth;
-    private final int mDividerHeight;
     private final int mIconTouchSize;
     private final boolean mIsRtl;
     private final int mTouchSlop;
@@ -74,9 +68,6 @@
     private LayoutTransition mLayoutTransition;
     private int mHotseatStartIndex;
     private int mHotseatEndIndex;
-    private View mHotseatRecentsDivider;
-    private int mRecentsStartIndex;
-    private int mRecentsEndIndex;
 
     // Delegate touches to the closest view if within mIconTouchSize.
     private boolean mDelegateTargeted;
@@ -104,9 +95,6 @@
         super(context, attrs, defStyleAttr, defStyleRes);
 
         Resources resources = getResources();
-        mBackgroundDrawable = (ColorDrawable) getBackground();
-        mDividerWidth = resources.getDimensionPixelSize(R.dimen.taskbar_divider_thickness);
-        mDividerHeight = resources.getDimensionPixelSize(R.dimen.taskbar_divider_height);
         mIconTouchSize = resources.getDimensionPixelSize(R.dimen.taskbar_icon_touch_size);
         mIsRtl = Utilities.isRtl(resources);
         mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
@@ -119,18 +107,11 @@
         mItemMarginLeftRight = Math.round(mItemMarginLeftRight * mNonIconScale);
     }
 
-    protected void init(int numHotseatIcons, int numRecentIcons) {
+    protected void init(int numHotseatIcons) {
         mHotseatStartIndex = 0;
         mHotseatEndIndex = mHotseatStartIndex + numHotseatIcons - 1;
         updateHotseatItems(new ItemInfo[numHotseatIcons]);
 
-        int dividerIndex = mHotseatEndIndex + 1;
-        mHotseatRecentsDivider = addDivider(dividerIndex);
-
-        mRecentsStartIndex = dividerIndex + 1;
-        mRecentsEndIndex = mRecentsStartIndex + numRecentIcons - 1;
-        updateRecentTasks(new Task[numRecentIcons]);
-
         mLayoutTransition = new LayoutTransition();
         addUpdateListenerForAllLayoutTransitions(() -> {
             if (getLayoutTransition() == mLayoutTransition) {
@@ -165,7 +146,6 @@
         endAllLayoutTransitionAnimators();
         setLayoutTransition(null);
         removeAllViews();
-        mHotseatRecentsDivider = null;
     }
 
     private void endAllLayoutTransitionAnimators() {
@@ -177,14 +157,6 @@
     }
 
     /**
-     * Sets the alpha of the background color behind all the Taskbar contents.
-     * @param alpha 0 is fully transparent, 1 is fully opaque.
-     */
-    public void setBackgroundAlpha(float alpha) {
-        mBackgroundDrawable.setAlpha((int) (alpha * 255));
-    }
-
-    /**
      * Inflates/binds the Hotseat views to show in the Taskbar given their ItemInfos.
      */
     protected void updateHotseatItems(ItemInfo[] hotseatItemInfos) {
@@ -248,8 +220,6 @@
             }
             updateHotseatItemVisibility(hotseatView);
         }
-
-        updateHotseatRecentsDividerVisibility();
     }
 
     protected void updateHotseatItemsVisibility() {
@@ -273,95 +243,6 @@
         }
     }
 
-    private View addDivider(int dividerIndex) {
-        View divider = inflate(R.layout.taskbar_divider);
-        LayoutParams lp = new LayoutParams(mDividerWidth, mDividerHeight);
-        lp.setMargins(mItemMarginLeftRight, 0, mItemMarginLeftRight, 0);
-        divider.setScaleX(mNonIconScale);
-        divider.setScaleY(mNonIconScale);
-        addView(divider, dividerIndex, lp);
-        return divider;
-    }
-
-    /**
-     * Inflates/binds the Recents items to show in the Taskbar given their Tasks.
-     */
-    protected void updateRecentTasks(Task[] tasks) {
-        for (int i = 0; i < tasks.length; i++) {
-            Task task = tasks[i];
-            int recentsIndex = mRecentsStartIndex + i;
-            View recentsView = getChildAt(recentsIndex);
-
-            // Inflate empty icon Views.
-            if (recentsView == null) {
-                BubbleTextView btv = (BubbleTextView) inflate(R.layout.taskbar_app_icon);
-                LayoutParams lp = new LayoutParams(btv.getIconSize(), btv.getIconSize());
-                lp.setMargins(mItemMarginLeftRight, 0, mItemMarginLeftRight, 0);
-                recentsView = btv;
-                addView(recentsView, recentsIndex, lp);
-            }
-
-            // Apply the Task, or hide the view if there is none for a given index.
-            if (recentsView instanceof BubbleTextView && task != null) {
-                applyTaskToBubbleTextView((BubbleTextView) recentsView, task);
-                recentsView.setVisibility(VISIBLE);
-                recentsView.setOnClickListener(mControllerCallbacks.getItemOnClickListener());
-                recentsView.setOnLongClickListener(
-                        mControllerCallbacks.getItemOnLongClickListener());
-            } else {
-                recentsView.setVisibility(GONE);
-                recentsView.setOnClickListener(null);
-                recentsView.setOnLongClickListener(null);
-            }
-        }
-
-        updateHotseatRecentsDividerVisibility();
-    }
-
-    private void applyTaskToBubbleTextView(BubbleTextView btv, Task task) {
-        if (task.icon != null) {
-            Drawable icon = task.icon.getConstantState().newDrawable().mutate();
-            btv.applyIconAndLabel(icon, task.titleDescription);
-        }
-        btv.setTag(task);
-    }
-
-    protected void updateRecentTaskAtIndex(int taskIndex, Task task) {
-        View taskView = getChildAt(mRecentsStartIndex + taskIndex);
-        if (taskView instanceof BubbleTextView) {
-            applyTaskToBubbleTextView((BubbleTextView) taskView, task);
-        }
-    }
-
-    /**
-     * Make the divider VISIBLE between the Hotseat and Recents if there is at least one icon in
-     * each, otherwise make it GONE.
-     */
-    private void updateHotseatRecentsDividerVisibility() {
-        if (mHotseatRecentsDivider == null) {
-            return;
-        }
-
-        boolean hasAtLeastOneHotseatItem = false;
-        for (int i = mHotseatStartIndex; i <= mHotseatEndIndex; i++) {
-            if (getChildAt(i).getVisibility() != GONE) {
-                hasAtLeastOneHotseatItem = true;
-                break;
-            }
-        }
-
-        boolean hasAtLeastOneRecentItem = false;
-        for (int i = mRecentsStartIndex; i <= mRecentsEndIndex; i++) {
-            if (getChildAt(i).getVisibility() != GONE) {
-                hasAtLeastOneRecentItem = true;
-                break;
-            }
-        }
-
-        mHotseatRecentsDivider.setVisibility(hasAtLeastOneHotseatItem && hasAtLeastOneRecentItem
-                ? VISIBLE : GONE);
-    }
-
     @Override
     public boolean onTouchEvent(MotionEvent event) {
         boolean handled = delegateTouchIfNecessary(event);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
index 6cdeb0f..30c07b0 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -88,6 +88,11 @@
     }
 
     @Override
+    public float getTaskbarTranslationY(Launcher launcher) {
+        return 0f;
+    }
+
+    @Override
     public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) {
         return new PageAlphaProvider(DEACCEL_2) {
             @Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
index 965f474..53afd21 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
@@ -35,7 +35,13 @@
         float shiftRange = launcher.getAllAppsController().getShiftRange();
         float shiftProgress = getVerticalProgress(launcher) - NORMAL.getVerticalProgress(launcher);
         float translationY = shiftProgress * shiftRange;
-        return new ScaleAndTranslation(1, 0, translationY);
+        return new ScaleAndTranslation(0.9f, 0, translationY);
+    }
+
+    @Override
+    public float getVerticalProgress(Launcher launcher) {
+        // Don't move all apps shelf while quick-switching (just let it fade).
+        return 1f;
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
index 697516d..77b0804 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
@@ -23,7 +23,7 @@
 import static com.android.launcher3.LauncherState.QUICK_SWITCH;
 import static com.android.launcher3.anim.AlphaUpdateListener.ALPHA_CUTOFF_THRESHOLD;
 import static com.android.launcher3.anim.Interpolators.ACCEL_0_75;
-import static com.android.launcher3.anim.Interpolators.DEACCEL_5;
+import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
@@ -31,9 +31,10 @@
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_UNKNOWN_SWIPEUP;
 import static com.android.launcher3.logging.StatsLogManager.getLauncherAtomEvent;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_DEPTH;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_FADE;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_TRANSLATE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_SCALE;
 import static com.android.launcher3.states.StateAnimationConfig.SKIP_ALL_ANIMATIONS;
 import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
 import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_RIGHT;
@@ -80,9 +81,8 @@
 public class NoButtonQuickSwitchTouchController implements TouchController,
         BothAxesSwipeDetector.Listener {
 
-    /** The minimum progress of the scale/translationY animation until drag end. */
     private static final float Y_ANIM_MIN_PROGRESS = 0.25f;
-    private static final Interpolator FADE_OUT_INTERPOLATOR = DEACCEL_5;
+    private static final Interpolator FADE_OUT_INTERPOLATOR = DEACCEL_3;
     private static final Interpolator TRANSLATE_OUT_INTERPOLATOR = ACCEL_0_75;
     private static final Interpolator SCALE_DOWN_INTERPOLATOR = LINEAR;
     private static final long ATOMIC_DURATION_FROM_PAUSED_TO_OVERVIEW = 300;
@@ -187,7 +187,8 @@
         StateAnimationConfig nonOverviewBuilder = new StateAnimationConfig();
         nonOverviewBuilder.setInterpolator(ANIM_WORKSPACE_FADE, FADE_OUT_INTERPOLATOR);
         nonOverviewBuilder.setInterpolator(ANIM_ALL_APPS_FADE, FADE_OUT_INTERPOLATOR);
-        nonOverviewBuilder.setInterpolator(ANIM_WORKSPACE_TRANSLATE, TRANSLATE_OUT_INTERPOLATOR);
+        nonOverviewBuilder.setInterpolator(ANIM_WORKSPACE_SCALE, FADE_OUT_INTERPOLATOR);
+        nonOverviewBuilder.setInterpolator(ANIM_DEPTH, FADE_OUT_INTERPOLATOR);
         nonOverviewBuilder.setInterpolator(ANIM_VERTICAL_PROGRESS, TRANSLATE_OUT_INTERPOLATOR);
         updateNonOverviewAnim(QUICK_SWITCH, nonOverviewBuilder);
         mNonOverviewAnim.dispatchOnStart();
@@ -207,7 +208,7 @@
     /** Create state animation to control non-overview components. */
     private void updateNonOverviewAnim(LauncherState toState, StateAnimationConfig config) {
         config.duration = (long) (Math.max(mXRange, mYRange) * 2);
-        config.animFlags = config.animFlags | SKIP_OVERVIEW;
+        config.animFlags |= SKIP_OVERVIEW;
         mNonOverviewAnim = mLauncher.getStateManager()
                 .createAnimationToNewWorkspace(toState, config);
         mNonOverviewAnim.getTarget().addListener(mClearStateOnCancelListener);
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 2a903eb..c547d00 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -801,11 +801,9 @@
             }
         }
         if (mParallelRunningAnim != null) {
-            if (cancel) {
-                mParallelRunningAnim.cancel();
-            } else {
-                mParallelRunningAnim.end();
-            }
+            // Unlike the above animation, the parallel animation won't have anything to take up
+            // the work if it's canceled, so just end it instead.
+            mParallelRunningAnim.end();
         }
     }
 
diff --git a/quickstep/src/com/android/quickstep/FallbackActivityInterface.java b/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
index e13d1a4..8168e88 100644
--- a/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
@@ -27,6 +27,7 @@
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.statemanager.StateManager;
 import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.quickstep.fallback.RecentsState;
 import com.android.quickstep.util.ActivityInitListener;
@@ -139,7 +140,25 @@
 
     @Override
     public void onExitOverview(RotationTouchHelper deviceState, Runnable exitRunnable) {
-        // no-op, fake landscape not supported for 3P
+        final StateManager<RecentsState> stateManager = getCreatedActivity().getStateManager();
+        if (stateManager.getState() == HOME) {
+            exitRunnable.run();
+            notifyRecentsOfOrientation(deviceState);
+            return;
+        }
+
+        stateManager.addStateListener(
+                new StateManager.StateListener<RecentsState>() {
+                    @Override
+                    public void onStateTransitionComplete(RecentsState toState) {
+                        // Are we going from Recents to Workspace?
+                        if (toState == HOME) {
+                            exitRunnable.run();
+                            notifyRecentsOfOrientation(deviceState);
+                            stateManager.removeStateListener(this);
+                        }
+                    }
+                });
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
index 7e4a352..ec1cc4a 100644
--- a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -247,7 +247,8 @@
             if (appearedTaskTarget.activityType == ACTIVITY_TYPE_HOME) {
                 RemoteAnimationTargets targets = new RemoteAnimationTargets(
                         new RemoteAnimationTargetCompat[] {appearedTaskTarget},
-                        new RemoteAnimationTargetCompat[0], appearedTaskTarget.mode);
+                        new RemoteAnimationTargetCompat[0], new RemoteAnimationTargetCompat[0],
+                        appearedTaskTarget.mode);
                 mHomeAlphaParams.setTargetSet(targets);
                 updateHomeAlpha();
                 return true;
diff --git a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
index a749f9a..2a6e478 100644
--- a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
+++ b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
@@ -53,7 +53,7 @@
  */
 class OrientationTouchTransformer {
 
-    class CurrentDisplay {
+    private static class CurrentDisplay {
         public Point size;
         public int rotation;
 
@@ -68,6 +68,13 @@
         }
 
         @Override
+        public String toString() {
+            return "CurrentDisplay:"
+                    + " rotation: " + rotation
+                    + " size: " + size;
+        }
+
+        @Override
         public boolean equals(Object o) {
             if (this == o) return true;
             if (o == null || getClass() != o.getClass()) return false;
@@ -86,21 +93,20 @@
 
     private static final String TAG = "OrientationTouchTransformer";
     private static final boolean DEBUG = false;
-    private static final int MAX_ORIENTATIONS = 4;
 
     private static final int QUICKSTEP_ROTATION_UNINITIALIZED = -1;
 
     private final Matrix mTmpMatrix = new Matrix();
     private final float[] mTmpPoint = new float[2];
 
-    private Map<CurrentDisplay, OrientationRectF> mSwipeTouchRegions =
+    private final Map<CurrentDisplay, OrientationRectF> mSwipeTouchRegions =
             new HashMap<CurrentDisplay, OrientationRectF>();
     private final RectF mAssistantLeftRegion = new RectF();
     private final RectF mAssistantRightRegion = new RectF();
     private final RectF mOneHandedModeRegion = new RectF();
     private CurrentDisplay mCurrentDisplay = new CurrentDisplay();
     private int mNavBarGesturalHeight;
-    private int mNavBarLargerGesturalHeight;
+    private final int mNavBarLargerGesturalHeight;
     private boolean mEnableMultipleRegions;
     private Resources mResources;
     private OrientationRectF mLastRectTouched;
@@ -374,10 +380,7 @@
                     return;
                 }
 
-                for (int i = 0; i < MAX_ORIENTATIONS; i++) {
-                    CurrentDisplay display = new CurrentDisplay(mCurrentDisplay.size, i);
-                    OrientationRectF rect = mSwipeTouchRegions.get(display);
-
+                for (OrientationRectF rect : mSwipeTouchRegions.values()) {
                     if (TestProtocol.sDebugTracing) {
                         Log.d(TestProtocol.NO_SWIPE_TO_HOME, "transform:DOWN, rect=" + rect);
                     }
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 1340abb..7c453e7 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -108,8 +108,7 @@
 
         SplitPlaceholderView splitPlaceholderView = findViewById(R.id.split_placeholder);
         splitPlaceholderView.init(
-                new SplitSelectStateController(
-                        SystemUiProxy.INSTANCE.get(this))
+                new SplitSelectStateController(mUiHandler, SystemUiProxy.INSTANCE.get(this))
         );
 
         mDragLayer.recreateControllers();
@@ -192,7 +191,7 @@
                     RemoteAnimationTargetCompat[] nonAppTargets,
                     AnimationResult result) -> {
             AnimatorSet anim = composeRecentsLaunchAnimator(taskView, appTargets,
-                    wallpaperTargets);
+                    wallpaperTargets, nonAppTargets);
             anim.addListener(resetStateListener());
             result.setAnimation(anim, RecentsActivity.this, onEndCallback::executeAllAndDestroy);
         };
@@ -213,12 +212,13 @@
      */
     private AnimatorSet  composeRecentsLaunchAnimator(TaskView taskView,
             RemoteAnimationTargetCompat[] appTargets,
-            RemoteAnimationTargetCompat[] wallpaperTargets) {
+            RemoteAnimationTargetCompat[] wallpaperTargets,
+            RemoteAnimationTargetCompat[] nonAppTargets) {
         AnimatorSet target = new AnimatorSet();
         boolean activityClosing = taskIsATargetWithMode(appTargets, getTaskId(), MODE_CLOSING);
         PendingAnimation pa = new PendingAnimation(RECENTS_LAUNCH_DURATION);
         createRecentsWindowAnimator(taskView, !activityClosing, appTargets,
-                wallpaperTargets, null /* depthController */, pa);
+                wallpaperTargets, nonAppTargets, null /* depthController */, pa);
         target.play(pa.buildAnim());
 
         // Found a visible recents task that matches the opening app, lets launch the app from there
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index 23e35f6..4a191e1 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -138,7 +138,7 @@
         mDisplayId = mDisplayHolder.getInfo().id;
         mIsOneHandedModeSupported = SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false);
         runOnDestroy(() -> mDisplayHolder.removeChangeListener(this));
-        mRotationTouchHelper = new RotationTouchHelper(context, mDisplayHolder);
+        mRotationTouchHelper = RotationTouchHelper.INSTANCE.get(context);
         runOnDestroy(mRotationTouchHelper::destroy);
 
         // Register for user unlocked if necessary
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
index 718c5ba..3861bab 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
@@ -33,7 +33,7 @@
     public RecentsAnimationTargets(RemoteAnimationTargetCompat[] apps,
             RemoteAnimationTargetCompat[] wallpapers, Rect homeContentInsets,
             Rect minimizedHomeBounds) {
-        super(apps, wallpapers, MODE_CLOSING);
+        super(apps, wallpapers, new RemoteAnimationTargetCompat[0], MODE_CLOSING);
         this.homeContentInsets = homeContentInsets;
         this.minimizedHomeBounds = minimizedHomeBounds;
     }
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index ba24e6a..c786167 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -112,20 +112,19 @@
     }
 
     /**
-     * Finds and returns the task key associated with the given task id.
+     * Checks if a task has been removed or not.
      *
-     * @param callback The callback to receive the task key if it is found or null. This is always
-     *                 called on the UI thread.
+     * @param callback Receives true if task is removed, false otherwise
      */
-    public void findTaskWithId(int taskId, Consumer<Task.TaskKey> callback) {
+    public void isTaskRemoved(int taskId, Consumer<Boolean> callback) {
         mTaskList.getTasks(true /* loadKeysOnly */, (tasks) -> {
             for (Task task : tasks) {
                 if (task.key.id == taskId) {
-                    callback.accept(task.key);
+                    callback.accept(false);
                     return;
                 }
             }
-            callback.accept(null);
+            callback.accept(true);
         });
     }
 
diff --git a/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java b/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java
index ab5e3ba..a1af77d 100644
--- a/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java
+++ b/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java
@@ -15,6 +15,8 @@
  */
 package com.android.quickstep;
 
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
+
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
 import java.util.ArrayList;
@@ -30,13 +32,15 @@
     public final RemoteAnimationTargetCompat[] unfilteredApps;
     public final RemoteAnimationTargetCompat[] apps;
     public final RemoteAnimationTargetCompat[] wallpapers;
+    public final RemoteAnimationTargetCompat[] nonApps;
     public final int targetMode;
     public final boolean hasRecents;
 
     private boolean mReleased = false;
 
     public RemoteAnimationTargets(RemoteAnimationTargetCompat[] apps,
-            RemoteAnimationTargetCompat[] wallpapers, int targetMode) {
+            RemoteAnimationTargetCompat[] wallpapers, RemoteAnimationTargetCompat[] nonApps,
+            int targetMode) {
         ArrayList<RemoteAnimationTargetCompat> filteredApps = new ArrayList<>();
         boolean hasRecents = false;
         if (apps != null) {
@@ -55,6 +59,7 @@
         this.wallpapers = wallpapers;
         this.targetMode = targetMode;
         this.hasRecents = hasRecents;
+        this.nonApps = nonApps;
     }
 
     public RemoteAnimationTargetCompat findTask(int taskId) {
@@ -66,6 +71,18 @@
         return null;
     }
 
+    /**
+     * Gets the navigation bar remote animation target if exists.
+     */
+    public RemoteAnimationTargetCompat getNavBarRemoteAnimationTarget() {
+        for (RemoteAnimationTargetCompat target : nonApps) {
+            if (target.windowType == TYPE_NAVIGATION_BAR) {
+                return target;
+            }
+        }
+        return null;
+    }
+
     public boolean isAnimatingHome() {
         for (RemoteAnimationTargetCompat target : unfilteredApps) {
             if (target.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
@@ -98,6 +115,9 @@
         for (RemoteAnimationTargetCompat target : wallpapers) {
             target.release();
         }
+        for (RemoteAnimationTargetCompat target : nonApps) {
+            target.release();
+        }
     }
 
     /**
diff --git a/quickstep/src/com/android/quickstep/RotationTouchHelper.java b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
index 2cf3212..1155482 100644
--- a/quickstep/src/com/android/quickstep/RotationTouchHelper.java
+++ b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
@@ -28,9 +28,11 @@
 import android.view.OrientationEventListener;
 
 import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.DisplayController.DisplayHolder;
 import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener;
 import com.android.launcher3.util.DisplayController.Info;
+import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.quickstep.util.RecentsOrientedState;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.QuickStepContract;
@@ -43,6 +45,9 @@
         SysUINavigationMode.NavigationModeChangeListener,
         DisplayInfoChangeListener {
 
+    public static final MainThreadInitializedObject<RotationTouchHelper> INSTANCE =
+            new MainThreadInitializedObject<>(RotationTouchHelper::new);
+
     private final OrientationTouchTransformer mOrientationTouchTransformer;
     private final DisplayHolder mDisplayHolder;
     private final SysUINavigationMode mSysUiNavMode;
@@ -121,9 +126,9 @@
 
     private final Context mContext;
 
-    public RotationTouchHelper(Context context, DisplayHolder displayHolder) {
+    private RotationTouchHelper(Context context) {
         mContext = context;
-        mDisplayHolder = displayHolder;
+        mDisplayHolder = DisplayController.getDefaultDisplay(context);
         Resources resources = mContext.getResources();
         mSysUiNavMode = SysUINavigationMode.INSTANCE.get(context);
         mDisplayId = mDisplayHolder.getInfo().id;
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index 1f332c4..f6018d1 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -35,6 +35,7 @@
 import android.view.MotionEvent;
 
 import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.launcher3.util.SplitConfigurationOptions;
 import com.android.systemui.shared.recents.ISystemUiProxy;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.system.RemoteTransitionCompat;
@@ -255,18 +256,6 @@
     }
 
     @Override
-    public Bundle monitorGestureInput(String name, int displayId) {
-        if (mSystemUiProxy != null) {
-            try {
-                return mSystemUiProxy.monitorGestureInput(name, displayId);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call monitorGestureInput: " + name, e);
-            }
-        }
-        return null;
-    }
-
-    @Override
     public void notifyAccessibilityButtonClicked(int displayId) {
         if (mSystemUiProxy != null) {
             try {
@@ -491,6 +480,20 @@
         }
     }
 
+    /** Start multiple tasks in split-screen simultaneously. */
+    public void startTasks(int mainTaskId, Bundle mainOptions, int sideTaskId, Bundle sideOptions,
+            @SplitConfigurationOptions.StagePosition int sidePosition,
+            RemoteTransitionCompat remoteTransition) {
+        if (mSystemUiProxy != null) {
+            try {
+                mSplitScreen.startTasks(mainTaskId, mainOptions, sideTaskId, sideOptions,
+                        sidePosition, remoteTransition.getTransition());
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call startTask");
+            }
+        }
+    }
+
     public void startShortcut(String packageName, String shortcutId, int stage, int position,
             Bundle options, UserHandle user) {
         if (mSplitScreen != null) {
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index f14f60b..cbb2a66 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -15,9 +15,17 @@
  */
 package com.android.quickstep;
 
+import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.view.WindowManager.TRANSIT_TO_FRONT;
+
 import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
 import static com.android.launcher3.LauncherState.BACKGROUND_APP;
 import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.QuickstepTransitionManager.ANIMATION_DELAY_NAV_FADE_IN;
+import static com.android.launcher3.QuickstepTransitionManager.ANIMATION_NAV_FADE_IN_DURATION;
+import static com.android.launcher3.QuickstepTransitionManager.ANIMATION_NAV_FADE_OUT_DURATION;
+import static com.android.launcher3.QuickstepTransitionManager.NAV_FADE_IN_INTERPOLATOR;
+import static com.android.launcher3.QuickstepTransitionManager.NAV_FADE_OUT_INTERPOLATOR;
 import static com.android.launcher3.QuickstepTransitionManager.RECENTS_LAUNCH_DURATION;
 import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
@@ -42,7 +50,9 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.os.Build;
+import android.view.SurfaceControl;
 import android.view.View;
+import android.window.TransitionInfo;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -58,6 +68,7 @@
 import com.android.launcher3.statehandlers.DepthController;
 import com.android.launcher3.statemanager.StateManager;
 import com.android.launcher3.util.DisplayController;
+import com.android.quickstep.util.MultiValueUpdateListener;
 import com.android.quickstep.util.SurfaceTransactionApplier;
 import com.android.quickstep.util.TaskViewSimulator;
 import com.android.quickstep.util.TransformParams;
@@ -67,6 +78,7 @@
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
 
 /**
  * Utility class for helpful methods related to {@link TaskView} objects and their tasks.
@@ -137,7 +149,8 @@
 
     public static void createRecentsWindowAnimator(TaskView v, boolean skipViewChanges,
             RemoteAnimationTargetCompat[] appTargets,
-            RemoteAnimationTargetCompat[] wallpaperTargets, DepthController depthController,
+            RemoteAnimationTargetCompat[] wallpaperTargets,
+            RemoteAnimationTargetCompat[] nonAppTargets, DepthController depthController,
             PendingAnimation out) {
         boolean isRunningTask = v.isRunningTask();
         TransformParams params = null;
@@ -146,7 +159,7 @@
             params = v.getRecentsView().getLiveTileParams();
             tsv = v.getRecentsView().getLiveTileTaskViewSimulator();
         }
-        createRecentsWindowAnimator(v, skipViewChanges, appTargets, wallpaperTargets,
+        createRecentsWindowAnimator(v, skipViewChanges, appTargets, wallpaperTargets, nonAppTargets,
                 depthController, out, params, tsv);
     }
 
@@ -156,7 +169,8 @@
      */
     public static void createRecentsWindowAnimator(TaskView v, boolean skipViewChanges,
             RemoteAnimationTargetCompat[] appTargets,
-            RemoteAnimationTargetCompat[] wallpaperTargets, DepthController depthController,
+            RemoteAnimationTargetCompat[] wallpaperTargets,
+            RemoteAnimationTargetCompat[] nonAppTargets, DepthController depthController,
             PendingAnimation out, @Nullable TransformParams params,
             @Nullable TaskViewSimulator tsv) {
         boolean isQuickSwitch = v.isEndQuickswitchCuj();
@@ -164,8 +178,9 @@
 
         boolean inLiveTileMode = LIVE_TILE.get() && v.getRecentsView().getRunningTaskIndex() != -1;
         final RemoteAnimationTargets targets =
-                new RemoteAnimationTargets(appTargets, wallpaperTargets,
+                new RemoteAnimationTargets(appTargets, wallpaperTargets, nonAppTargets,
                         inLiveTileMode ? MODE_CLOSING : MODE_OPENING);
+        final RemoteAnimationTargetCompat navBarTarget = targets.getNavBarRemoteAnimationTarget();
 
         if (params == null) {
             SurfaceTransactionApplier applier = new SurfaceTransactionApplier(v);
@@ -225,6 +240,30 @@
             TaskViewSimulator finalTsv = tsv;
             TransformParams finalParams = params;
             out.addOnFrameCallback(() -> finalTsv.apply(finalParams));
+            if (navBarTarget != null) {
+                final Rect cropRect = new Rect();
+                out.addOnFrameListener(new MultiValueUpdateListener() {
+                    FloatProp mNavFadeOut = new FloatProp(1f, 0f, 0,
+                            ANIMATION_NAV_FADE_OUT_DURATION, NAV_FADE_OUT_INTERPOLATOR);
+                    FloatProp mNavFadeIn = new FloatProp(0f, 1f, ANIMATION_DELAY_NAV_FADE_IN,
+                            ANIMATION_NAV_FADE_IN_DURATION, NAV_FADE_IN_INTERPOLATOR);
+
+                    @Override
+                    public void onUpdate(float percent) {
+                        final SurfaceParams.Builder navBuilder =
+                                new SurfaceParams.Builder(navBarTarget.leash);
+                        if (mNavFadeIn.value > mNavFadeIn.getStartValue()) {
+                            finalTsv.getCurrentCropRect().round(cropRect);
+                            navBuilder.withMatrix(finalTsv.getCurrentMatrix())
+                                    .withWindowCrop(cropRect)
+                                    .withAlpha(mNavFadeIn.value);
+                        } else {
+                            navBuilder.withAlpha(mNavFadeOut.value);
+                        }
+                        finalParams.applySurfaceParams(navBuilder.build());
+                    }
+                });
+            }
             topMostSimulator = tsv;
         }
 
@@ -318,9 +357,50 @@
      * device is considered in multiWindowMode and things like insets and stuff change
      * and calculations have to be adjusted in the animations for that
      */
-    public static void composeRecentsSplitLaunchAnimator(@NonNull AnimatorSet anim,
+    public static void composeRecentsSplitLaunchAnimator(@NonNull TaskView initialView,
+            @NonNull TaskView v, @NonNull TransitionInfo transitionInfo,
+            SurfaceControl.Transaction t, @NonNull Runnable finishCallback) {
+
+        final TransitionInfo.Change[] splitRoots = new TransitionInfo.Change[2];
+        for (int i = 0; i < transitionInfo.getChanges().size(); ++i) {
+            final TransitionInfo.Change change = transitionInfo.getChanges().get(i);
+            final int taskId = change.getTaskInfo() != null ? change.getTaskInfo().taskId : -1;
+            final int mode = change.getMode();
+            // Find the target tasks' root tasks since those are the split stages that need to
+            // be animated (the tasks themselves are children and thus inherit animation).
+            if (taskId == initialView.getTask().key.id || taskId == v.getTask().key.id) {
+                if (!(mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT)) {
+                    throw new IllegalStateException(
+                            "Expected task to be showing, but it is " + mode);
+                }
+                if (change.getParent() == null) {
+                    throw new IllegalStateException("Initiating multi-split launch but the split"
+                            + "root of " + taskId + " is already visible or has broken hierarchy.");
+                }
+                splitRoots[taskId == initialView.getTask().key.id ? 0 : 1] =
+                        transitionInfo.getChange(change.getParent());
+            }
+        }
+
+        // This is where we should animate the split roots. For now, though, just make them visible.
+        for (int i = 0; i < 2; ++i) {
+            t.show(splitRoots[i].getLeash());
+            t.setAlpha(splitRoots[i].getLeash(), 1.f);
+        }
+
+        // This contains the initial state (before animation), so apply this at the beginning of
+        // the animation.
+        t.apply();
+
+        // Once there is an animation, this should be called AFTER the animation completes.
+        finishCallback.run();
+    }
+
+    /** Legacy version (until shell transitions are enabled) */
+    public static void composeRecentsSplitLaunchAnimatorLegacy(@NonNull AnimatorSet anim,
             @NonNull TaskView v, @NonNull RemoteAnimationTargetCompat[] appTargets,
-            @NonNull RemoteAnimationTargetCompat[] wallpaperTargets, boolean launcherClosing,
+            @NonNull RemoteAnimationTargetCompat[] wallpaperTargets,
+            @NonNull RemoteAnimationTargetCompat[] nonAppTargets, boolean launcherClosing,
             @NonNull StateManager stateManager, @NonNull DepthController depthController,
             int targetStage) {
         PendingAnimation out = new PendingAnimation(RECENTS_LAUNCH_DURATION);
@@ -336,7 +416,7 @@
         boolean inLiveTileMode =
                 ENABLE_QUICKSTEP_LIVE_TILE.get() && recentsView.getRunningTaskIndex() != -1;
         final RemoteAnimationTargets targets =
-                new RemoteAnimationTargets(appTargets, wallpaperTargets,
+                new RemoteAnimationTargets(appTargets, wallpaperTargets, nonAppTargets,
                         inLiveTileMode ? MODE_CLOSING : MODE_OPENING);
 
         if (params == null) {
@@ -392,7 +472,8 @@
 
     public static void composeRecentsLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
             @NonNull RemoteAnimationTargetCompat[] appTargets,
-            @NonNull RemoteAnimationTargetCompat[] wallpaperTargets, boolean launcherClosing,
+            @NonNull RemoteAnimationTargetCompat[] wallpaperTargets,
+            @NonNull RemoteAnimationTargetCompat[] nonAppTargets, boolean launcherClosing,
             @NonNull StateManager stateManager, @NonNull RecentsView recentsView,
             @NonNull DepthController depthController) {
         boolean skipLauncherChanges = !launcherClosing;
@@ -400,7 +481,7 @@
         TaskView taskView = findTaskViewToLaunch(recentsView, v, appTargets);
         PendingAnimation pa = new PendingAnimation(RECENTS_LAUNCH_DURATION);
         createRecentsWindowAnimator(taskView, skipLauncherChanges, appTargets, wallpaperTargets,
-                depthController, pa);
+                nonAppTargets, depthController, pa);
 
         Animator childStateAnimation = null;
         // Found a visible recents task that matches the opening app, lets launch the app from there
diff --git a/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java b/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
index b336d2f..4c10822 100644
--- a/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
+++ b/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
@@ -149,6 +149,7 @@
 
         SharedPreferences prefs = getPrefs(mContext);
         StatsLogManager.LauncherEvent gridSizeChangedEvent = null;
+        // TODO(b/184981523): This doesn't work for 2-panel grid, which has 6 hotseat icons
         switch (prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, -1)) {
             case 5:
                 gridSizeChangedEvent = LAUNCHER_GRID_SIZE_5;
diff --git a/quickstep/src/com/android/quickstep/util/MultiValueUpdateListener.java b/quickstep/src/com/android/quickstep/util/MultiValueUpdateListener.java
index e798d5c..b4ae1ca 100644
--- a/quickstep/src/com/android/quickstep/util/MultiValueUpdateListener.java
+++ b/quickstep/src/com/android/quickstep/util/MultiValueUpdateListener.java
@@ -64,5 +64,12 @@
 
             mAllProperties.add(this);
         }
+
+        /**
+         * Gets the start value.
+         */
+        public float getStartValue() {
+            return mStart;
+        }
     }
 }
diff --git a/quickstep/src/com/android/quickstep/util/RemoteFadeOutAnimationListener.java b/quickstep/src/com/android/quickstep/util/RemoteFadeOutAnimationListener.java
index 958ee24..81c124f 100644
--- a/quickstep/src/com/android/quickstep/util/RemoteFadeOutAnimationListener.java
+++ b/quickstep/src/com/android/quickstep/util/RemoteFadeOutAnimationListener.java
@@ -34,7 +34,8 @@
 
     public RemoteFadeOutAnimationListener(RemoteAnimationTargetCompat[] appTargets,
             RemoteAnimationTargetCompat[] wallpaperTargets) {
-        mTarget = new RemoteAnimationTargets(appTargets, wallpaperTargets, MODE_CLOSING);
+        mTarget = new RemoteAnimationTargets(appTargets, wallpaperTargets,
+                new RemoteAnimationTargetCompat[0], MODE_CLOSING);
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index d9154ed..9576eac 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -16,11 +16,17 @@
 
 package com.android.quickstep.util;
 
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
+
 import android.animation.AnimatorSet;
 import android.app.ActivityOptions;
 import android.os.Handler;
 import android.os.Looper;
 import android.util.Pair;
+import android.view.SurfaceControl;
+import android.window.TransitionInfo;
 
 import androidx.annotation.Nullable;
 
@@ -31,12 +37,15 @@
 import com.android.launcher3.WrappedLauncherAnimationRunner;
 import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
 import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.TaskAnimationManager;
 import com.android.quickstep.TaskViewUtils;
 import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.system.ActivityOptionsCompat;
 import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
 import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.RemoteTransitionCompat;
+import com.android.systemui.shared.system.RemoteTransitionRunner;
 
 /**
  * Represent data needed for the transient state when user has selected one app for split screen
@@ -47,9 +56,11 @@
     private final SystemUiProxy mSystemUiProxy;
     private TaskView mInitialTaskView;
     private SplitPositionOption mInitialPosition;
+    private final Handler mHandler;
 
-    public SplitSelectStateController(SystemUiProxy systemUiProxy) {
+    public SplitSelectStateController(Handler handler, SystemUiProxy systemUiProxy) {
         mSystemUiProxy = systemUiProxy;
+        mHandler = handler;
     }
 
     /**
@@ -64,6 +75,19 @@
      * To be called after second task selected
      */
     public void setSecondTaskId(TaskView taskView) {
+        if (TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) {
+            // Assume initial task is for top/left part of screen
+            final int[] taskIds = mInitialPosition.mStagePosition == STAGE_POSITION_TOP_OR_LEFT
+                    ? new int[]{mInitialTaskView.getTask().key.id, taskView.getTask().key.id}
+                    : new int[]{taskView.getTask().key.id, mInitialTaskView.getTask().key.id};
+
+            RemoteSplitLaunchAnimationRunner animationRunner =
+                    new RemoteSplitLaunchAnimationRunner(mInitialTaskView, taskView);
+            mSystemUiProxy.startTasks(taskIds[0], null /* mainOptions */, taskIds[1],
+                    null /* sideOptions */, STAGE_POSITION_BOTTOM_OR_RIGHT,
+                    new RemoteTransitionCompat(animationRunner, MAIN_EXECUTOR));
+            return;
+        }
         // Assume initial mInitialTaskId is for top/left part of screen
         WrappedAnimationRunnerImpl initialSplitRunnerWrapped =  new SplitLaunchAnimationRunner(
                 mInitialTaskView, 0);
@@ -96,6 +120,30 @@
     }
 
     /**
+     * Requires Shell Transitions
+     */
+    private class RemoteSplitLaunchAnimationRunner implements RemoteTransitionRunner {
+
+        private final TaskView mInitialTaskView;
+        private final TaskView mTaskView;
+
+        RemoteSplitLaunchAnimationRunner(TaskView initialTaskView, TaskView taskView) {
+            mInitialTaskView = initialTaskView;
+            mTaskView = taskView;
+        }
+
+        @Override
+        public void startAnimation(TransitionInfo info, SurfaceControl.Transaction t,
+                Runnable finishCallback) {
+            TaskViewUtils.composeRecentsSplitLaunchAnimator(mInitialTaskView, mTaskView,
+                    info, t, finishCallback);
+            // After successful launch, call resetState
+            resetState();
+        }
+    }
+
+    /**
+     * LEGACY
      * @return the opposite stage and position from the {@param position} provided as first and
      *         second object, respectively
      * Ex. If position is has stage = Main and position = Top/Left, this will return
@@ -109,6 +157,7 @@
     }
 
     /**
+     * LEGACY
      * Remote animation runner for animation to launch an app.
      */
     private class SplitLaunchAnimationRunner implements WrappedAnimationRunnerImpl {
@@ -129,8 +178,8 @@
                 LauncherAnimationRunner.AnimationResult result) {
             AnimatorSet anim = new AnimatorSet();
             BaseQuickstepLauncher activity = BaseActivity.fromContext(mV.getContext());
-            TaskViewUtils.composeRecentsSplitLaunchAnimator(anim, mV,
-                    appTargets, wallpaperTargets, true, activity.getStateManager(),
+            TaskViewUtils.composeRecentsSplitLaunchAnimatorLegacy(anim, mV,
+                    appTargets, wallpaperTargets, nonAppTargets, true, activity.getStateManager(),
                     activity.getDepthController(), mTargetState);
             result.setAnimation(anim, activity);
         }
diff --git a/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java b/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java
index 0436a1c..3b4fd31 100644
--- a/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java
+++ b/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java
@@ -15,8 +15,6 @@
  */
 package com.android.quickstep.util;
 
-import static com.android.systemui.shared.system.TransactionCompat.deferTransactionUntil;
-
 import android.annotation.TargetApi;
 import android.os.Build;
 import android.os.Handler;
@@ -90,11 +88,10 @@
             for (int i = params.length - 1; i >= 0; i--) {
                 SurfaceParams surfaceParams = params[i];
                 if (surfaceParams.surface.isValid()) {
-                    deferTransactionUntil(t, surfaceParams.surface, mBarrierSurfaceControl, frame);
-                    surfaceParams.applyTo(t);
+                      surfaceParams.applyTo(t);
                 }
             }
-            t.apply();
+            mTargetViewRootImpl.mergeWithNextTransaction(t, frame);
             Message.obtain(mApplyHandler, MSG_UPDATE_SEQUENCE_NUMBER, toApplySeqNo, 0)
                     .sendToTarget();
         });
diff --git a/quickstep/src/com/android/quickstep/util/TransformParams.java b/quickstep/src/com/android/quickstep/util/TransformParams.java
index 756331d..03d7a37 100644
--- a/quickstep/src/com/android/quickstep/util/TransformParams.java
+++ b/quickstep/src/com/android/quickstep/util/TransformParams.java
@@ -205,7 +205,7 @@
         return mTargetSet;
     }
 
-    public void applySurfaceParams(SurfaceParams[] params) {
+    public void applySurfaceParams(SurfaceParams... params) {
         if (mSyncTransactionApplier != null) {
             mSyncTransactionApplier.scheduleApply(params);
         } else {
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index c39e7df..87c46bf 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -72,7 +72,6 @@
 import android.graphics.Typeface;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
-import android.os.Handler;
 import android.os.UserHandle;
 import android.text.Layout;
 import android.text.StaticLayout;
@@ -96,6 +95,7 @@
 import android.widget.ListView;
 
 import androidx.annotation.Nullable;
+import androidx.annotation.UiThread;
 
 import com.android.launcher3.BaseActivity;
 import com.android.launcher3.BaseActivity.MultiWindowModeChangedListener;
@@ -111,6 +111,7 @@
 import com.android.launcher3.anim.SpringProperty;
 import com.android.launcher3.compat.AccessibilityManagerCompat;
 import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.icons.cache.HandlerRunnable;
 import com.android.launcher3.statehandlers.DepthController;
 import com.android.launcher3.statemanager.BaseState;
 import com.android.launcher3.statemanager.StatefulActivity;
@@ -153,6 +154,7 @@
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
 import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
 import com.android.wm.shell.pip.IPipAnimationListener;
 
 import java.util.ArrayList;
@@ -394,34 +396,27 @@
                 return;
             }
 
-            UI_HELPER_EXECUTOR.execute(() -> {
-                TaskView taskView = getTaskView(taskId);
-                if (taskView == null) {
-                    return;
-                }
-                Handler handler = taskView.getHandler();
-                if (handler == null) {
-                    return;
-                }
-
-                // TODO: Add callbacks from AM reflecting adding/removing from the recents list, and
-                //       remove all these checks
-                Task.TaskKey taskKey = taskView.getTask().key;
-                if (PackageManagerWrapper.getInstance().getActivityInfo(taskKey.getComponent(),
-                        taskKey.userId) == null) {
-                    // The package was uninstalled
-                    handler.post(() ->
-                            dismissTask(taskView, true /* animate */, false /* removeTask */));
-                } else {
-                    mModel.findTaskWithId(taskKey.id, (key) -> {
-                        if (key == null) {
-                            // The task was removed from the recents list
-                            handler.post(() -> dismissTask(taskView, true /* animate */,
-                                    false /* removeTask */));
+            TaskView taskView = getTaskView(taskId);
+            if (taskView == null) {
+                return;
+            }
+            Task.TaskKey taskKey = taskView.getTask().key;
+            UI_HELPER_EXECUTOR.execute(new HandlerRunnable<>(
+                    UI_HELPER_EXECUTOR.getHandler(),
+                    () -> PackageManagerWrapper.getInstance()
+                            .getActivityInfo(taskKey.getComponent(), taskKey.userId) == null,
+                    MAIN_EXECUTOR,
+                    apkRemoved -> {
+                        if (apkRemoved) {
+                            dismissTask(taskId);
+                        } else {
+                            mModel.isTaskRemoved(taskKey.id, taskRemoved -> {
+                                if (taskRemoved) {
+                                    dismissTask(taskId);
+                                }
+                            });
                         }
-                    });
-                }
-            });
+                    }));
         }
     };
 
@@ -668,7 +663,7 @@
         updateTaskStackListenerState();
         mModel.getThumbnailCache().getHighResLoadingState().addCallback(this);
         mActivity.addMultiWindowModeChangedListener(mMultiWindowModeChangedListener);
-        ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
+        TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
         mSyncTransactionApplier = new SurfaceTransactionApplier(this);
         mLiveTileParams.setSyncTransactionApplier(mSyncTransactionApplier);
         RecentsModel.INSTANCE.get(getContext()).addThumbnailChangeListener(this);
@@ -687,7 +682,7 @@
         updateTaskStackListenerState();
         mModel.getThumbnailCache().getHighResLoadingState().removeCallback(this);
         mActivity.removeMultiWindowModeChangedListener(mMultiWindowModeChangedListener);
-        ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mTaskStackListener);
+        TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
         mSyncTransactionApplier = null;
         mLiveTileParams.setSyncTransactionApplier(null);
         RecentsModel.INSTANCE.get(getContext()).removeThumbnailChangeListener(this);
@@ -766,7 +761,8 @@
         } else {
             TaskViewUtils.composeRecentsLaunchAnimator(
                     anim, taskView, apps,
-                    mLiveTileParams.getTargetSet().wallpapers, true /* launcherClosing */,
+                    mLiveTileParams.getTargetSet().wallpapers,
+                    mLiveTileParams.getTargetSet().nonApps, true /* launcherClosing */,
                     mActivity.getStateManager(), this,
                     getDepthController());
         }
@@ -2176,6 +2172,15 @@
         controller.start();
     }
 
+    @UiThread
+    private void dismissTask(int taskId) {
+        TaskView taskView = getTaskView(taskId);
+        if (taskView == null) {
+            return;
+        }
+        dismissTask(taskView, true /* animate */, false /* removeTask */);
+    }
+
     public void dismissTask(TaskView taskView, boolean animateTaskView, boolean removeTask) {
         runDismissAnimation(createTaskDismissAnimation(taskView, animateTaskView, removeTask,
                 DISMISS_TASK_DURATION));
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 3cd9175..8c71ab3 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -489,7 +489,7 @@
             AnimatorSet anim = new AnimatorSet();
             TaskViewUtils.composeRecentsLaunchAnimator(
                     anim, this, targets.apps,
-                    targets.wallpapers, true /* launcherClosing */,
+                    targets.wallpapers, targets.nonApps, true /* launcherClosing */,
                     mActivity.getStateManager(), recentsView,
                     recentsView.getDepthController());
             anim.addListener(new AnimatorListenerAdapter() {
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index 5ffe315..0fe5432 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -120,7 +120,6 @@
                 getCurrentOverviewPage(launcher) < currentTaskAfterFlingForward));
 
         // Test opening a task.
-        startTestActivity(2);
         OverviewTask task = mLauncher.pressHome().switchToOverview().getCurrentTask();
         assertNotNull("overview.getCurrentTask() returned null (1)", task);
         assertNotNull("OverviewTask.open returned null", task.open());
diff --git a/res/drawable/ic_allapps_search.xml b/res/drawable/ic_allapps_search.xml
index c0e20f1..dbed824 100644
--- a/res/drawable/ic_allapps_search.xml
+++ b/res/drawable/ic_allapps_search.xml
@@ -17,7 +17,8 @@
     android:width="24dp"
     android:height="24dp"
     android:viewportHeight="24.0"
-    android:viewportWidth="24.0">
+    android:viewportWidth="24.0"
+    android:autoMirrored="true">
     <path
         android:fillColor="?android:attr/textColorTertiary"
         android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z" />
diff --git a/res/layout/widgets_list_row_header.xml b/res/layout/widgets_list_row_header.xml
index 598041c..8259c16 100644
--- a/res/layout/widgets_list_row_header.xml
+++ b/res/layout/widgets_list_row_header.xml
@@ -15,6 +15,7 @@
 -->
 <com.android.launcher3.widget.picker.WidgetsListHeader xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
+    xmlns:launcher="http://schemas.android.com/apk/res-auto"
     android:id="@+id/widgets_list_header"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
@@ -22,7 +23,8 @@
     android:background="@drawable/widgets_list_middle_ripple"
     android:layout_marginBottom="@dimen/widget_list_entry_bottom_margin"
     android:paddingVertical="@dimen/widget_list_header_view_vertical_padding"
-    android:orientation="horizontal">
+    android:orientation="horizontal"
+    launcher:appIconSize="48dp">
 
     <ImageView
         android:id="@+id/app_icon"
diff --git a/res/layout/widgets_search_bar.xml b/res/layout/widgets_search_bar.xml
index e3836df..c3dd19e 100644
--- a/res/layout/widgets_search_bar.xml
+++ b/res/layout/widgets_search_bar.xml
@@ -13,7 +13,9 @@
         android:id="@+id/widgets_search_bar_edit_text"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:padding="12dp"
+        android:paddingVertical="12dp"
+        android:paddingStart="12dp"
+        android:paddingEnd="0dp"
         android:drawablePadding="8dp"
         android:drawableStart="@drawable/ic_allapps_search"
         android:background="@null"
@@ -21,6 +23,7 @@
         android:maxLines="1"
         android:layout_weight="1"
         android:inputType="text"
+        android:imeOptions="actionSearch"
         android:textColor="?android:attr/textColorSecondary"
         android:textColorHint="?android:attr/textColorTertiary"/>
 
@@ -28,7 +31,9 @@
         android:id="@+id/widgets_search_cancel_button"
         android:layout_height="wrap_content"
         android:layout_width="wrap_content"
-        android:padding="8dp"
+        android:paddingVertical="12dp"
+        android:paddingStart="8dp"
+        android:paddingEnd="12dp"
         android:src="@drawable/ic_gm_close_24"
         android:background="?android:selectableItemBackground"
         android:layout_gravity="center"
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 381b0fe..699c2e3 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -175,6 +175,9 @@
 
         <!-- numAllAppsColumns defaults to GridDisplayOption.numColumns, if not specified -->
         <attr name="numAllAppsColumns" format="integer" />
+
+        <!-- numShownHotseatIcons defaults to GridDisplayOption.numHotseatIcons, if not specified -->
+        <attr name="numShownHotseatIcons" format="integer" />
     </declare-styleable>
 
     <declare-styleable name="CellLayout">
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 51dddab..9e4a4a8 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -140,6 +140,9 @@
     <dimen name="widget_row_padding">8dp</dimen>
     <dimen name="widget_row_divider">2dp</dimen>
 
+    <dimen name="widget_picker_education_tip_width">120dp</dimen>
+    <dimen name="widget_picker_education_tip_min_margin">4dp</dimen>
+
     <!-- Padding applied to shortcut previews -->
     <dimen name="shortcut_preview_padding_left">0dp</dimen>
     <dimen name="shortcut_preview_padding_right">0dp</dimen>
@@ -277,16 +280,6 @@
     <!-- Onboarding bottomsheet related -->
     <dimen name="bottom_sheet_edu_padding">24dp</dimen>
 
-    <!-- Search related -->
-    <dimen name="search_hero_title_size">16sp</dimen>
-    <dimen name="search_hero_subtitle_size">14sp</dimen>
-    <dimen name="search_hero_inline_button_size">12sp</dimen>
-    <dimen name="search_settings_icon_size">36dp</dimen>
-    <dimen name="search_settings_icon_vertical_offset">16dp</dimen>
-    <dimen name="search_line_spacing">4dp</dimen>
-    <dimen name="search_decoration_corner_radius">28dp</dimen>
-    <dimen name="search_decoration_padding">1dp</dimen>
-
 <!-- Taskbar related (placeholders to compile in Launcher3 without Quickstep) -->
     <dimen name="taskbar_size">0dp</dimen>
 
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 986180c..0f937fa 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -95,6 +95,9 @@
     <!-- Tab label. A user can tap this tab to access their work widgets. [CHAR_LIMIT=25] -->
     <string name="widgets_full_sheet_work_tab">Work</string>
 
+    <!-- A widget category label for grouping widgets related to conversations. [CHAR_LIMIT=30] -->
+    <string name="widget_category_conversations">Conversations</string>
+
     <!-- All Apps -->
     <!-- Search bar text in the apps view. [CHAR_LIMIT=50] -->
     <string name="all_apps_search_bar_hint">Search apps</string>
@@ -107,12 +110,6 @@
     <!-- Label for an icon representing any generic app. [CHAR_LIMIT=50] -->
     <string name="label_application">App</string>
 
-    <!--All apps Search-->
-    <!-- Section title for apps [CHAR_LIMIT=50] -->
-    <string name="search_corpus_apps">Apps</string>
-    <!-- try instant app action for play search result [CHAR_LIMIT=50 -->
-    <string name="search_action_try_now">Try Now</string>
-
     <!-- Popup items -->
     <!-- Text to display as the header above notifications. [CHAR_LIMIT=30] -->
     <string name="notifications_header">Notifications</string>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index a27cdac..fd77b80 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -150,7 +150,7 @@
     </style>
 
     <style name="HomeSettingsTheme" parent="@android:style/Theme.DeviceDefault.Settings">
-        <item name="android:navigationBarColor">@android:color/transparent</item>
+        <item name="android:navigationBarColor">?android:colorPrimaryDark</item>
         <item name="preferenceTheme">@style/HomeSettingsPreferenceTheme</item>
     </style>
 
diff --git a/res/xml/launcher_preferences.xml b/res/xml/launcher_preferences.xml
index e4bea50..8195cc1 100644
--- a/res/xml/launcher_preferences.xml
+++ b/res/xml/launcher_preferences.xml
@@ -22,14 +22,7 @@
         android:key="pref_icon_badging"
         android:title="@string/notification_dots_title"
         android:persistent="false"
-        android:widgetLayout="@layout/notification_pref_warning" >
-        <intent android:action="android.settings.NOTIFICATION_SETTINGS">
-            <!-- This extra highlights the "Allow notification dots" field in Notification settings -->
-            <extra
-                android:name=":settings:fragment_args_key"
-                android:value="notification_badging" />
-        </intent>
-    </com.android.launcher3.settings.NotificationDotsPreference>
+        android:widgetLayout="@layout/notification_pref_warning" />
 
     <!--
       LAUNCHER_ADD_NEW_APPS_TO_HOME_SCREEN_ENABLED(613)
diff --git a/robolectric_tests/src/com/android/launcher3/model/BackupRestoreTest.java b/robolectric_tests/src/com/android/launcher3/model/BackupRestoreTest.java
index 8baf5a3..34a8025 100644
--- a/robolectric_tests/src/com/android/launcher3/model/BackupRestoreTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/BackupRestoreTest.java
@@ -35,6 +35,7 @@
 import android.content.pm.PackageInstaller;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
+import android.os.Process;
 import android.os.UserHandle;
 import android.os.UserManager;
 
@@ -64,17 +65,11 @@
     private static final long OLD_WORK_PROFILE_ID = 11;
     private static final int WORK_PROFILE_ID = 10;
 
-    private static final int SYSTEM_USER = 0;
-    private static final int FLAG_SYSTEM = 0x00000800;
-    private static final int FLAG_PROFILE = 0x00001000;
-
     private ShadowUserManager mUserManager;
     private BackupManager mBackupManager;
     private LauncherModelHelper mModelHelper;
     private SQLiteDatabase mDb;
     private InvariantDeviceProfile mIdp;
-    private UserHandle mMainProfileUser;
-    private UserHandle mWorkProfileUser;
 
     @Before
     public void setUp() {
@@ -90,17 +85,15 @@
         final UserManager userManager = RuntimeEnvironment.application.getSystemService(
                 UserManager.class);
         mUserManager = Shadow.extract(userManager);
-        // sign in to primary user
-        mMainProfileUser = mUserManager.addUser(SYSTEM_USER, "me", FLAG_SYSTEM);
         // sign in to work profile
-        mWorkProfileUser = mUserManager.addUser(WORK_PROFILE_ID, "work", FLAG_PROFILE);
+        mUserManager.addUser(WORK_PROFILE_ID, "work", ShadowUserManager.FLAG_MANAGED_PROFILE);
     }
 
     private void setupBackupManager() {
         mBackupManager = new BackupManager(RuntimeEnvironment.application);
         final LShadowBackupManager bm = Shadow.extract(mBackupManager);
-        bm.addProfile(MY_OLD_PROFILE_ID, mMainProfileUser);
-        bm.addProfile(OLD_WORK_PROFILE_ID, mWorkProfileUser);
+        bm.addProfile(MY_OLD_PROFILE_ID, Process.myUserHandle());
+        bm.addProfile(OLD_WORK_PROFILE_ID, UserHandle.of(WORK_PROFILE_ID));
     }
 
     @Test
@@ -134,7 +127,7 @@
                 { APP_ICON, SHORTCUT, SHORTCUT, NO__ICON},
             }}, 2, OLD_WORK_PROFILE_ID);
         // simulates the creation of backup upon restore
-        new GridBackupTable(RuntimeEnvironment.application, mDb, mIdp.numHotseatIcons,
+        new GridBackupTable(RuntimeEnvironment.application, mDb, mIdp.numDatabaseHotseatIcons,
                 mIdp.numColumns, mIdp.numRows).doBackup(
                         MY_OLD_PROFILE_ID, GridBackupTable.OPTION_REQUIRES_SANITIZATION);
         // reset favorites table
diff --git a/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java b/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
index 8e00dcb..d544a0b 100644
--- a/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
@@ -64,7 +64,7 @@
                 mModelHelper.addItem(APP_ICON, 4, HOTSEAT, 0, 0),
         };
 
-        mIdp.numHotseatIcons = 3;
+        mIdp.numDatabaseHotseatIcons = 3;
         new GridSizeMigrationTask(mContext, mDb, mValidPackages, false, 5, 3)
                 .migrateHotseat();
         // First item is dropped as it has the least weight.
@@ -81,7 +81,7 @@
                 mModelHelper.addItem(10, 4, HOTSEAT, 0, 0),
         };
 
-        mIdp.numHotseatIcons = 3;
+        mIdp.numDatabaseHotseatIcons = 3;
         new GridSizeMigrationTask(mContext, mDb, mValidPackages, false, 5, 3)
                 .migrateHotseat();
         // First item is dropped as it has the least weight.
diff --git a/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskV2Test.java b/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskV2Test.java
index cca333c..8e49fae 100644
--- a/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskV2Test.java
+++ b/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskV2Test.java
@@ -120,7 +120,7 @@
         };
         mModelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 2, testPackage7);
 
-        mIdp.numHotseatIcons = 4;
+        mIdp.numDatabaseHotseatIcons = 4;
         mIdp.numColumns = 4;
         mIdp.numRows = 4;
         GridSizeMigrationTaskV2.DbReader srcReader = new GridSizeMigrationTaskV2.DbReader(mDb,
@@ -128,16 +128,16 @@
                 srcHotseatItems.length);
         GridSizeMigrationTaskV2.DbReader destReader = new GridSizeMigrationTaskV2.DbReader(mDb,
                 LauncherSettings.Favorites.TABLE_NAME, mContext, mValidPackages,
-                mIdp.numHotseatIcons);
+                mIdp.numDatabaseHotseatIcons);
         GridSizeMigrationTaskV2 task = new GridSizeMigrationTaskV2(mContext, mDb, srcReader,
-                destReader, mIdp.numHotseatIcons, new Point(mIdp.numColumns, mIdp.numRows));
+                destReader, mIdp.numDatabaseHotseatIcons, new Point(mIdp.numColumns, mIdp.numRows));
         task.migrate();
 
         // Check hotseat items
         Cursor c = mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
                 new String[]{LauncherSettings.Favorites.SCREEN, LauncherSettings.Favorites.INTENT},
                 "container=" + CONTAINER_HOTSEAT, null, null, null);
-        assertEquals(c.getCount(), mIdp.numHotseatIcons);
+        assertEquals(c.getCount(), mIdp.numDatabaseHotseatIcons);
         int screenIndex = c.getColumnIndex(LauncherSettings.Favorites.SCREEN);
         int intentIndex = c.getColumnIndex(LauncherSettings.Favorites.INTENT);
         c.moveToNext();
@@ -186,5 +186,101 @@
         assertTrue(c.getString(intentIndex).contains(testPackage8));
         assertEquals(c.getInt(cellXIndex), 0);
         assertEquals(c.getInt(cellYIndex), 2);
+
+        c.close();
+    }
+
+    @Test
+    public void migrateToLargerHotseat() {
+        int[] srcHotseatItems = {
+                mModelHelper.addItem(APP_ICON, 0, HOTSEAT, 0, 0, testPackage1, 1, TMP_CONTENT_URI),
+                mModelHelper.addItem(SHORTCUT, 1, HOTSEAT, 0, 0, testPackage2, 2, TMP_CONTENT_URI),
+                mModelHelper.addItem(APP_ICON, 2, HOTSEAT, 0, 0, testPackage3, 3, TMP_CONTENT_URI),
+                mModelHelper.addItem(SHORTCUT, 3, HOTSEAT, 0, 0, testPackage4, 4, TMP_CONTENT_URI),
+        };
+
+        int numSrcDatabaseHotseatIcons = srcHotseatItems.length;
+        mIdp.numDatabaseHotseatIcons = 6;
+        mIdp.numColumns = 4;
+        mIdp.numRows = 4;
+        GridSizeMigrationTaskV2.DbReader srcReader = new GridSizeMigrationTaskV2.DbReader(mDb,
+                LauncherSettings.Favorites.TMP_TABLE, mContext, mValidPackages,
+                numSrcDatabaseHotseatIcons);
+        GridSizeMigrationTaskV2.DbReader destReader = new GridSizeMigrationTaskV2.DbReader(mDb,
+                LauncherSettings.Favorites.TABLE_NAME, mContext, mValidPackages,
+                mIdp.numDatabaseHotseatIcons);
+        GridSizeMigrationTaskV2 task = new GridSizeMigrationTaskV2(mContext, mDb, srcReader,
+                destReader, mIdp.numDatabaseHotseatIcons, new Point(mIdp.numColumns, mIdp.numRows));
+        task.migrate();
+
+        // Check hotseat items
+        Cursor c = mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
+                new String[]{LauncherSettings.Favorites.SCREEN, LauncherSettings.Favorites.INTENT},
+                "container=" + CONTAINER_HOTSEAT, null, null, null);
+        assertEquals(c.getCount(), numSrcDatabaseHotseatIcons);
+        int screenIndex = c.getColumnIndex(LauncherSettings.Favorites.SCREEN);
+        int intentIndex = c.getColumnIndex(LauncherSettings.Favorites.INTENT);
+        c.moveToNext();
+        assertEquals(c.getInt(screenIndex), 0);
+        assertTrue(c.getString(intentIndex).contains(testPackage1));
+        c.moveToNext();
+        assertEquals(c.getInt(screenIndex), 1);
+        assertTrue(c.getString(intentIndex).contains(testPackage2));
+        c.moveToNext();
+        assertEquals(c.getInt(screenIndex), 2);
+        assertTrue(c.getString(intentIndex).contains(testPackage3));
+        c.moveToNext();
+        assertEquals(c.getInt(screenIndex), 3);
+        assertTrue(c.getString(intentIndex).contains(testPackage4));
+
+        c.close();
+    }
+
+    @Test
+    public void migrateFromLargerHotseat() {
+        int[] srcHotseatItems = {
+                mModelHelper.addItem(APP_ICON, 0, HOTSEAT, 0, 0, testPackage1, 1, TMP_CONTENT_URI),
+                -1,
+                mModelHelper.addItem(SHORTCUT, 2, HOTSEAT, 0, 0, testPackage2, 2, TMP_CONTENT_URI),
+                mModelHelper.addItem(APP_ICON, 3, HOTSEAT, 0, 0, testPackage3, 3, TMP_CONTENT_URI),
+                mModelHelper.addItem(SHORTCUT, 4, HOTSEAT, 0, 0, testPackage4, 4, TMP_CONTENT_URI),
+                mModelHelper.addItem(APP_ICON, 5, HOTSEAT, 0, 0, testPackage5, 5, TMP_CONTENT_URI),
+        };
+
+        int numSrcDatabaseHotseatIcons = srcHotseatItems.length;
+        mIdp.numDatabaseHotseatIcons = 4;
+        mIdp.numColumns = 4;
+        mIdp.numRows = 4;
+        GridSizeMigrationTaskV2.DbReader srcReader = new GridSizeMigrationTaskV2.DbReader(mDb,
+                LauncherSettings.Favorites.TMP_TABLE, mContext, mValidPackages,
+                numSrcDatabaseHotseatIcons);
+        GridSizeMigrationTaskV2.DbReader destReader = new GridSizeMigrationTaskV2.DbReader(mDb,
+                LauncherSettings.Favorites.TABLE_NAME, mContext, mValidPackages,
+                mIdp.numDatabaseHotseatIcons);
+        GridSizeMigrationTaskV2 task = new GridSizeMigrationTaskV2(mContext, mDb, srcReader,
+                destReader, mIdp.numDatabaseHotseatIcons, new Point(mIdp.numColumns, mIdp.numRows));
+        task.migrate();
+
+        // Check hotseat items
+        Cursor c = mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
+                new String[]{LauncherSettings.Favorites.SCREEN, LauncherSettings.Favorites.INTENT},
+                "container=" + CONTAINER_HOTSEAT, null, null, null);
+        assertEquals(c.getCount(), mIdp.numDatabaseHotseatIcons);
+        int screenIndex = c.getColumnIndex(LauncherSettings.Favorites.SCREEN);
+        int intentIndex = c.getColumnIndex(LauncherSettings.Favorites.INTENT);
+        c.moveToNext();
+        assertEquals(c.getInt(screenIndex), 0);
+        assertTrue(c.getString(intentIndex).contains(testPackage1));
+        c.moveToNext();
+        assertEquals(c.getInt(screenIndex), 1);
+        assertTrue(c.getString(intentIndex).contains(testPackage2));
+        c.moveToNext();
+        assertEquals(c.getInt(screenIndex), 2);
+        assertTrue(c.getString(intentIndex).contains(testPackage3));
+        c.moveToNext();
+        assertEquals(c.getInt(screenIndex), 3);
+        assertTrue(c.getString(intentIndex).contains(testPackage4));
+
+        c.close();
     }
 }
diff --git a/robolectric_tests/src/com/android/launcher3/model/LoaderCursorTest.java b/robolectric_tests/src/com/android/launcher3/model/LoaderCursorTest.java
index fb08c56..800311a 100644
--- a/robolectric_tests/src/com/android/launcher3/model/LoaderCursorTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/LoaderCursorTest.java
@@ -160,7 +160,7 @@
     public void checkItemPlacement_outsideBounds() {
         mIDP.numRows = 4;
         mIDP.numColumns = 4;
-        mIDP.numHotseatIcons = 3;
+        mIDP.numDatabaseHotseatIcons = 3;
 
         // Item outside screen bounds are not placed
         assertFalse(mLoaderCursor.checkItemPlacement(
@@ -171,7 +171,7 @@
     public void checkItemPlacement_overlappingItems() {
         mIDP.numRows = 4;
         mIDP.numColumns = 4;
-        mIDP.numHotseatIcons = 3;
+        mIDP.numDatabaseHotseatIcons = 3;
 
         // Overlapping mItems are not placed
         assertTrue(mLoaderCursor.checkItemPlacement(
@@ -197,7 +197,7 @@
     public void checkItemPlacement_hotseat() {
         mIDP.numRows = 4;
         mIDP.numColumns = 4;
-        mIDP.numHotseatIcons = 3;
+        mIDP.numDatabaseHotseatIcons = 3;
 
         // Hotseat mItems are only placed based on screenId
         assertTrue(mLoaderCursor.checkItemPlacement(
diff --git a/robolectric_tests/src/com/android/launcher3/util/LauncherModelHelper.java b/robolectric_tests/src/com/android/launcher3/util/LauncherModelHelper.java
index 849f98b..846e201 100644
--- a/robolectric_tests/src/com/android/launcher3/util/LauncherModelHelper.java
+++ b/robolectric_tests/src/com/android/launcher3/util/LauncherModelHelper.java
@@ -355,7 +355,7 @@
             throws Exception {
         Context context = RuntimeEnvironment.application;
         InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(context);
-        idp.numRows = idp.numColumns = idp.numHotseatIcons = DEFAULT_GRID_SIZE;
+        idp.numRows = idp.numColumns = idp.numDatabaseHotseatIcons = DEFAULT_GRID_SIZE;
         idp.iconBitmapSize = DEFAULT_BITMAP_SIZE;
 
         Settings.Secure.putString(context.getContentResolver(),
diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java
index f61bc05..5b655a4 100644
--- a/src/com/android/launcher3/AutoInstallsLayout.java
+++ b/src/com/android/launcher3/AutoInstallsLayout.java
@@ -89,7 +89,7 @@
 
         // Try with grid size and hotseat count
         String layoutName = String.format(Locale.ENGLISH, FORMATTED_LAYOUT_RES_WITH_HOSTEAT,
-                grid.numColumns, grid.numRows, grid.numHotseatIcons);
+                grid.numColumns, grid.numRows, grid.numDatabaseHotseatIcons);
         int layoutId = targetRes.getIdentifier(layoutName, "xml", pkg);
 
         // Try with only grid size
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 35b0f27..985a445 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -137,6 +137,7 @@
     public int folderChildDrawablePaddingPx;
 
     // Hotseat
+    public final int numShownHotseatIcons;
     public int hotseatCellHeightPx;
     // In portrait: size = height, in landscape: size = width
     public int hotseatBarSizePx;
@@ -293,6 +294,7 @@
 
         workspaceCellPaddingXPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_padding_x);
 
+        numShownHotseatIcons = inv.numShownHotseatIcons;
         hotseatBarTopPaddingPx =
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_top_padding);
         hotseatBarBottomPaddingPx = (isTallDevice ? 0
@@ -735,7 +737,7 @@
             // for this, we pad the left and right of the hotseat with half of the difference of a
             // workspace cell vs a hotseat cell.
             float workspaceCellWidth = (float) widthPx / inv.numColumns;
-            float hotseatCellWidth = (float) widthPx / inv.numHotseatIcons;
+            float hotseatCellWidth = (float) widthPx / inv.numShownHotseatIcons;
             int hotseatAdjustment = Math.round((workspaceCellWidth - hotseatCellWidth) / 2);
             mHotseatPadding.set(
                     hotseatAdjustment + workspacePadding.left + cellLayoutPaddingLeftRightPx
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index 5429806..8496fd5 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -92,9 +92,9 @@
         mHasVerticalHotseat = hasVerticalHotseat;
         InvariantDeviceProfile idp = mActivity.getDeviceProfile().inv;
         if (hasVerticalHotseat) {
-            setGridSize(1, idp.numHotseatIcons);
+            setGridSize(1, idp.numShownHotseatIcons);
         } else {
-            setGridSize(idp.numHotseatIcons, 1);
+            setGridSize(idp.numShownHotseatIcons, 1);
         }
     }
 
@@ -199,25 +199,38 @@
         int left = (r - l - qsbWidth) / 2;
         int right = left + qsbWidth;
 
-        DeviceProfile dp = mActivity.getDeviceProfile();
-        int freeSpace = dp.isTaskbarPresent
-                ? dp.workspacePadding.bottom
-                : dp.hotseatBarSizePx - dp.hotseatCellHeightPx - mQsbHeight;
-        int bottom = b - t
-                - (int) (freeSpace * QSB_CENTER_FACTOR)
-                - (dp.isTaskbarPresent ? dp.taskbarSize : dp.getInsets().bottom);
+        int bottom = b - t - getQsbOffsetY();
         int top = bottom - mQsbHeight;
         mQsb.layout(left, top, right, bottom);
 
         int taskbarWidth = mTaskbarView.getMeasuredWidth();
         left = (r - l - taskbarWidth) / 2;
         right = left + taskbarWidth;
-        bottom = b - t;
+        bottom = b - t - getTaskbarOffsetY();
         top = bottom - mTaskbarViewHeight;
         mTaskbarView.layout(left, top, right, bottom);
     }
 
     /**
+     * Returns the number of pixels the QSB is translated from the bottom of the screen.
+     */
+    private int getQsbOffsetY() {
+        DeviceProfile dp = mActivity.getDeviceProfile();
+        int freeSpace = dp.isTaskbarPresent
+                ? dp.workspacePadding.bottom
+                : dp.hotseatBarSizePx - dp.hotseatCellHeightPx - mQsbHeight;
+        return (int) (freeSpace * QSB_CENTER_FACTOR)
+                + (dp.isTaskbarPresent ? dp.taskbarSize : dp.getInsets().bottom);
+    }
+
+    /**
+     * Returns the number of pixels the taskbar is translated from the bottom of the screen.
+     */
+    public int getTaskbarOffsetY() {
+        return (getQsbOffsetY() - mTaskbarViewHeight) / 2;
+    }
+
+    /**
      * Sets the alpha value of just our ShortcutAndWidgetContainer.
      */
     public void setIconsAlpha(float alpha) {
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 5612daf..f198777 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -122,7 +122,14 @@
     /**
      * Number of icons inside the hotseat area.
      */
-    public int numHotseatIcons;
+    protected int numShownHotseatIcons;
+
+    /**
+     * Number of icons inside the hotseat area that is stored in the database. This is greater than
+     * or equal to numnShownHotseatIcons, allowing for a seamless transition between two hotseat
+     * sizes that share the same DB.
+     */
+    public int numDatabaseHotseatIcons;
 
     /**
      * Number of columns in the all apps list.
@@ -165,7 +172,8 @@
         iconBitmapSize = p.iconBitmapSize;
         iconTextSize = p.iconTextSize;
         landscapeIconTextSize = p.landscapeIconTextSize;
-        numHotseatIcons = p.numHotseatIcons;
+        numShownHotseatIcons = p.numShownHotseatIcons;
+        numDatabaseHotseatIcons = p.numDatabaseHotseatIcons;
         numAllAppsColumns = p.numAllAppsColumns;
         isScalable = p.isScalable;
         devicePaddingId = p.devicePaddingId;
@@ -190,7 +198,7 @@
             Utilities.getPrefs(context).edit().putString(KEY_IDP_GRID_NAME, newGridName).apply();
         }
         Utilities.getPrefs(context).edit()
-                .putInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, numHotseatIcons)
+                .putInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, numDatabaseHotseatIcons)
                 .putString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, getPointString(numColumns, numRows))
                 .apply();
 
@@ -229,6 +237,7 @@
                 .add(myDisplayOption);
         result.iconSize = defaultDisplayOption.iconSize;
         result.landscapeIconSize = defaultDisplayOption.landscapeIconSize;
+        result.numShownHotseatIcons = myDisplayOption.numShownHotseatIcons;
         if (defaultDisplayOption.allAppsIconSize < myDisplayOption.allAppsIconSize) {
             result.allAppsIconSize = defaultDisplayOption.allAppsIconSize;
             result.numAllAppsColumns = defaultDisplayOption.numAllAppsColumns;
@@ -279,7 +288,7 @@
         GridOption closestProfile = displayOption.grid;
         numRows = closestProfile.numRows;
         numColumns = closestProfile.numColumns;
-        numHotseatIcons = closestProfile.numHotseatIcons;
+        numDatabaseHotseatIcons = closestProfile.numDatabaseHotseatIcons;
         dbFile = closestProfile.dbFile;
         defaultLayoutId = closestProfile.defaultLayoutId;
         demoModeLayoutId = closestProfile.demoModeLayoutId;
@@ -301,6 +310,7 @@
         minCellHeight = displayOption.minCellHeight;
         minCellWidth = displayOption.minCellWidth;
         borderSpacing = displayOption.borderSpacing;
+        numShownHotseatIcons = Math.round(displayOption.numShownHotseatIcons);
         numAllAppsColumns = Math.round(displayOption.numAllAppsColumns);
 
         if (Utilities.isGridOptionsEnabled(context)) {
@@ -391,7 +401,7 @@
                 numColumns != oldProfile.numColumns ||
                 numFolderColumns != oldProfile.numFolderColumns ||
                 numFolderRows != oldProfile.numFolderRows ||
-                numHotseatIcons != oldProfile.numHotseatIcons) {
+                numDatabaseHotseatIcons != oldProfile.numDatabaseHotseatIcons) {
             changeFlags |= CHANGE_FLAG_GRID;
         }
 
@@ -619,7 +629,7 @@
         private final int numFolderRows;
         private final int numFolderColumns;
 
-        private final int numHotseatIcons;
+        private final int numDatabaseHotseatIcons;
 
         private final String dbFile;
 
@@ -645,7 +655,7 @@
                     R.styleable.GridDisplayOption_defaultLayoutId, 0);
             demoModeLayoutId = a.getResourceId(
                     R.styleable.GridDisplayOption_demoModeLayoutId, defaultLayoutId);
-            numHotseatIcons = a.getInt(
+            numDatabaseHotseatIcons = a.getInt(
                     R.styleable.GridDisplayOption_numHotseatIcons, numColumns);
             numFolderRows = a.getInt(
                     R.styleable.GridDisplayOption_numFolderRows, numRows);
@@ -673,6 +683,7 @@
         private final float minHeightDps;
         private final boolean canBeDefault;
 
+        private float numShownHotseatIcons;
         private float numAllAppsColumns;
         private float minCellHeight;
         private float minCellWidth;
@@ -695,6 +706,8 @@
             minHeightDps = a.getFloat(R.styleable.ProfileDisplayOption_minHeightDps, 0);
             canBeDefault = a.getBoolean(
                     R.styleable.ProfileDisplayOption_canBeDefault, false);
+            numShownHotseatIcons = a.getInt(R.styleable.ProfileDisplayOption_numShownHotseatIcons,
+                    grid.numDatabaseHotseatIcons);
             numAllAppsColumns = a.getInt(R.styleable.ProfileDisplayOption_numAllAppsColumns,
                     grid.numColumns);
 
@@ -725,6 +738,7 @@
             minWidthDps = 0;
             minHeightDps = 0;
             canBeDefault = false;
+            numShownHotseatIcons = 0;
             numAllAppsColumns = 0;
             minCellHeight = 0;
             minCellWidth = 0;
@@ -732,6 +746,7 @@
         }
 
         private DisplayOption multiply(float w) {
+            numShownHotseatIcons *= w;
             numAllAppsColumns *= w;
             iconSize *= w;
             landscapeIconSize *= w;
@@ -746,6 +761,7 @@
         }
 
         private DisplayOption add(DisplayOption p) {
+            numShownHotseatIcons += p.numShownHotseatIcons;
             numAllAppsColumns += p.numAllAppsColumns;
             iconSize += p.iconSize;
             landscapeIconSize += p.landscapeIconSize;
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 699495c..eef3980 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -130,6 +130,10 @@
         mModelDelegate = ModelDelegate.newInstance(context, app, mBgAllAppsList, mBgDataModel);
     }
 
+    public ModelDelegate getModelDelegate() {
+        return mModelDelegate;
+    }
+
     /**
      * Adds the provided items to the workspace.
      */
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 2ec5e90..39b16fd 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -618,7 +618,7 @@
                 .appendQueryParameter("version", "1")
                 .appendQueryParameter("gridWidth", Integer.toString(grid.numColumns))
                 .appendQueryParameter("gridHeight", Integer.toString(grid.numRows))
-                .appendQueryParameter("hotseatSize", Integer.toString(grid.numHotseatIcons))
+                .appendQueryParameter("hotseatSize", Integer.toString(grid.numDatabaseHotseatIcons))
                 .build();
     }
 
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 0c509a1..58df9c8 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -184,6 +184,10 @@
         return launcher.getNormalTaskbarScale();
     }
 
+    public float getTaskbarTranslationY(Launcher launcher) {
+        return -launcher.getHotseat().getTaskbarOffsetY();
+    }
+
     public float getOverviewFullscreenProgress() {
         return 0;
     }
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 01f7c71..7496703 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -926,8 +926,7 @@
         if (disallowIntercept) {
             // We need to make sure to cancel our long press if
             // a scrollable widget takes over touch events
-            final View currentPage = getPageAt(mCurrentPage);
-            currentPage.cancelLongPress();
+            cancelCurrentPageLongPress();
         }
         super.requestDisallowInterceptTouchEvent(disallowIntercept);
     }
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 406e785..41865ce 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -75,9 +75,9 @@
 public class AllAppsContainerView extends SpringRelativeLayout implements DragSource,
         Insettable, OnDeviceProfileChangeListener, OnActivePageChangedListener {
 
-    private static final float FLING_VELOCITY_MULTIPLIER = 1000 * .8f;
+    private static final float FLING_VELOCITY_MULTIPLIER = 1000 * .2f;
     // Starts the springs after at least 55% of the animation has passed.
-    private static final float FLING_ANIMATION_THRESHOLD = 0.55f;
+    private static final float FLING_ANIMATION_THRESHOLD = 0.25f;
 
     protected final BaseDraggingActivity mLauncher;
     protected final AdapterHolder[] mAH;
diff --git a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
index a8185d6..2491217 100644
--- a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
+++ b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
@@ -106,7 +106,7 @@
                 - mAppsView.getActiveRecyclerView().getPaddingRight();
 
         int cellWidth = DeviceProfile.calculateCellWidth(rowWidth, dp.cellLayoutBorderSpacingPx,
-                dp.inv.numHotseatIcons);
+                dp.numShownHotseatIcons);
         int iconVisibleSize = Math.round(ICON_VISIBLE_AREA_FACTOR * dp.iconSizePx);
         int iconPadding = cellWidth - iconVisibleSize;
 
diff --git a/src/com/android/launcher3/anim/Interpolators.java b/src/com/android/launcher3/anim/Interpolators.java
index 7980138..7a38937 100644
--- a/src/com/android/launcher3/anim/Interpolators.java
+++ b/src/com/android/launcher3/anim/Interpolators.java
@@ -46,7 +46,6 @@
     public static final Interpolator DEACCEL_2 = new DecelerateInterpolator(2);
     public static final Interpolator DEACCEL_2_5 = new DecelerateInterpolator(2.5f);
     public static final Interpolator DEACCEL_3 = new DecelerateInterpolator(3f);
-    public static final Interpolator DEACCEL_5 = new DecelerateInterpolator(5f);
 
     public static final Interpolator ACCEL_DEACCEL = new AccelerateDecelerateInterpolator();
 
diff --git a/src/com/android/launcher3/anim/PendingAnimation.java b/src/com/android/launcher3/anim/PendingAnimation.java
index 4e90c9e..9068331 100644
--- a/src/com/android/launcher3/anim/PendingAnimation.java
+++ b/src/com/android/launcher3/anim/PendingAnimation.java
@@ -117,11 +117,18 @@
      * Adds a callback to be run on every frame of the animation
      */
     public void addOnFrameCallback(Runnable runnable) {
+        addOnFrameListener(anim -> runnable.run());
+    }
+
+    /**
+     * Adds a listener to be run on every frame of the animation
+     */
+    public void addOnFrameListener(ValueAnimator.AnimatorUpdateListener listener) {
         if (mProgressAnimator == null) {
             mProgressAnimator = ValueAnimator.ofFloat(0, 1);
         }
 
-        mProgressAnimator.addUpdateListener(anim -> runnable.run());
+        mProgressAnimator.addUpdateListener(listener);
     }
 
     /**
diff --git a/src/com/android/launcher3/dragndrop/AddItemActivity.java b/src/com/android/launcher3/dragndrop/AddItemActivity.java
index b7a7366..fc635a9 100644
--- a/src/com/android/launcher3/dragndrop/AddItemActivity.java
+++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java
@@ -62,6 +62,7 @@
 import com.android.launcher3.widget.PendingAddShortcutInfo;
 import com.android.launcher3.widget.PendingAddWidgetInfo;
 import com.android.launcher3.widget.WidgetCell;
+import com.android.launcher3.widget.WidgetCellPreview;
 import com.android.launcher3.widget.WidgetHostViewLoader;
 import com.android.launcher3.widget.WidgetImageView;
 import com.android.launcher3.widget.WidgetManagerHelper;
@@ -121,9 +122,10 @@
             }
         }
 
-        WidgetImageView preview = mWidgetCell.findViewById(R.id.widget_preview);
-        preview.setOnTouchListener(this);
-        preview.setOnLongClickListener(this);
+        WidgetCellPreview previewContainer = mWidgetCell.findViewById(
+                R.id.widget_preview_container);
+        previewContainer.setOnTouchListener(this);
+        previewContainer.setOnLongClickListener(this);
 
         // savedInstanceState is null when the activity is created the first time (i.e., avoids
         // duplicate logging during rotation)
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index a1b7997..cf1cb45 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -78,7 +78,6 @@
 import com.android.launcher3.R;
 import com.android.launcher3.ShortcutAndWidgetContainer;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.Workspace;
 import com.android.launcher3.Workspace.ItemOperator;
 import com.android.launcher3.accessibility.AccessibleDragListenerAdapter;
 import com.android.launcher3.accessibility.FolderAccessibilityHelper;
@@ -534,9 +533,25 @@
     }
 
     private void startAnimation(final AnimatorSet a) {
-        final Workspace workspace = mLauncher.getWorkspace();
-        final CellLayout currentCellLayout =
-                (CellLayout) workspace.getChildAt(workspace.getCurrentPage());
+        mLauncher.getWorkspace().getVisiblePages()
+                .forEach(visiblePage -> addAnimatorListenerForPage(a, (CellLayout) visiblePage));
+
+        a.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                mState = STATE_ANIMATING;
+                mCurrentAnimator = a;
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mCurrentAnimator = null;
+            }
+        });
+        a.start();
+    }
+
+    private void addAnimatorListenerForPage(AnimatorSet a, CellLayout currentCellLayout) {
         final boolean useHardware = shouldUseHardwareLayerForAnimation(currentCellLayout);
         final boolean wasHardwareAccelerated = currentCellLayout.isHardwareLayerEnabled();
 
@@ -546,8 +561,6 @@
                 if (useHardware) {
                     currentCellLayout.enableHardwareLayer(true);
                 }
-                mState = STATE_ANIMATING;
-                mCurrentAnimator = a;
             }
 
             @Override
@@ -555,10 +568,8 @@
                 if (useHardware) {
                     currentCellLayout.enableHardwareLayer(wasHardwareAccelerated);
                 }
-                mCurrentAnimator = null;
             }
         });
-        a.start();
     }
 
     private boolean shouldUseHardwareLayerForAnimation(CellLayout currentCellLayout) {
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index 5e9b179..31764c5 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -467,7 +467,7 @@
                 }
             }
             IntArray ranks = getMissingHotseatRanks(currentWorkspaceItems,
-                    mIdp.numHotseatIcons);
+                    mDp.numShownHotseatIcons);
             List<ItemInfo> predictions = workspaceResult.mHotseatPredictions == null
                     ? Collections.emptyList() : workspaceResult.mHotseatPredictions.items;
             int count = Math.min(ranks.size(), predictions.size());
@@ -484,7 +484,7 @@
             }
         } else {
             // Add hotseat icons
-            for (int i = 0; i < mIdp.numHotseatIcons; i++) {
+            for (int i = 0; i < mDp.numShownHotseatIcons; i++) {
                 WorkspaceItemInfo info = new WorkspaceItemInfo(mWorkspaceItemInfo);
                 info.container = Favorites.CONTAINER_HOTSEAT;
                 info.screenId = i;
diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java
index 988794c..a2c0f5c 100644
--- a/src/com/android/launcher3/icons/IconCache.java
+++ b/src/com/android/launcher3/icons/IconCache.java
@@ -39,6 +39,7 @@
 
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.LauncherFiles;
+import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.icons.ComponentWithLabel.ComponentCachingLogic;
@@ -304,6 +305,11 @@
         CacheEntry entry = getEntryForPackageLocked(
                 infoInOut.packageName, infoInOut.user, useLowResIcon);
         applyCacheEntry(entry, infoInOut);
+        if (infoInOut.category == PackageItemInfo.CONVERSATIONS) {
+            infoInOut.title = mContext.getString(R.string.widget_category_conversations);
+            infoInOut.contentDescription = mPackageManager.getUserBadgedLabel(
+                    infoInOut.title, infoInOut.user);
+        }
     }
 
     protected void applyCacheEntry(CacheEntry entry, ItemInfoWithIcon info) {
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTask.java b/src/com/android/launcher3/model/GridSizeMigrationTask.java
index 804e72e..6a75e62 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTask.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationTask.java
@@ -893,7 +893,7 @@
         String gridSizeString = getPointString(idp.numColumns, idp.numRows);
 
         return !gridSizeString.equals(prefs.getString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, ""))
-                || idp.numHotseatIcons != prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, -1);
+                || idp.numDatabaseHotseatIcons != prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, -1);
     }
 
     /** See {@link #migrateGridIfNeeded(Context, InvariantDeviceProfile)} */
@@ -928,7 +928,7 @@
                 .getBinder(Settings.EXTRA_VALUE)) {
 
             int srcHotseatCount = prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT,
-                    idp.numHotseatIcons);
+                    idp.numDatabaseHotseatIcons);
             Point sourceSize = parsePoint(prefs.getString(
                     KEY_MIGRATION_SRC_WORKSPACE_SIZE, gridSizeString));
 
@@ -948,10 +948,10 @@
 
             HashSet<String> validPackages = getValidPackages(context);
             // Hotseat.
-            if (srcHotseatCount != idp.numHotseatIcons
+            if (srcHotseatCount != idp.numDatabaseHotseatIcons
                     && new GridSizeMigrationTask(context, transaction.getDb(), validPackages,
                             migrateForPreview, srcHotseatCount,
-                            idp.numHotseatIcons).migrateHotseat()) {
+                            idp.numDatabaseHotseatIcons).migrateHotseat()) {
                 dbChanged = true;
             }
 
@@ -991,7 +991,7 @@
                 // Save current configuration, so that the migration does not run again.
                 prefs.edit()
                         .putString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, gridSizeString)
-                        .putInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, idp.numHotseatIcons)
+                        .putInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, idp.numDatabaseHotseatIcons)
                         .apply();
             }
         }
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
index c6c0791..cee9304 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
@@ -110,7 +110,7 @@
         String gridSizeString = getPointString(idp.numColumns, idp.numRows);
 
         return !gridSizeString.equals(prefs.getString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, ""))
-                || idp.numHotseatIcons != prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, -1);
+                || idp.numDatabaseHotseatIcons != prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, -1);
     }
 
     /** See {@link #migrateGridIfNeeded(Context, InvariantDeviceProfile)} */
@@ -148,7 +148,8 @@
         SharedPreferences prefs = Utilities.getPrefs(context);
         String gridSizeString = getPointString(idp.numColumns, idp.numRows);
         HashSet<String> validPackages = getValidPackages(context);
-        int srcHotseatCount = prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, idp.numHotseatIcons);
+        int srcHotseatCount = prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT,
+                idp.numDatabaseHotseatIcons);
 
         if (migrateForPreview) {
             if (!LauncherSettings.Settings.call(
@@ -177,11 +178,11 @@
             DbReader destReader = new DbReader(t.getDb(),
                     migrateForPreview ? LauncherSettings.Favorites.PREVIEW_TABLE_NAME
                             : LauncherSettings.Favorites.TABLE_NAME,
-                    context, validPackages, idp.numHotseatIcons);
+                    context, validPackages, idp.numDatabaseHotseatIcons);
 
             Point targetSize = new Point(idp.numColumns, idp.numRows);
             GridSizeMigrationTaskV2 task = new GridSizeMigrationTaskV2(context, t.getDb(),
-                    srcReader, destReader, idp.numHotseatIcons, targetSize);
+                    srcReader, destReader, idp.numDatabaseHotseatIcons, targetSize);
             task.migrate();
 
             if (!migrateForPreview) {
@@ -202,7 +203,7 @@
                 // Save current configuration, so that the migration does not run again.
                 prefs.edit()
                         .putString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, gridSizeString)
-                        .putInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, idp.numHotseatIcons)
+                        .putInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, idp.numDatabaseHotseatIcons)
                         .apply();
             }
         }
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index 19d9af9..897b02e 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -410,10 +410,10 @@
             final GridOccupancy hotseatOccupancy =
                     occupied.get(LauncherSettings.Favorites.CONTAINER_HOTSEAT);
 
-            if (item.screenId >= mIDP.numHotseatIcons) {
+            if (item.screenId >= mIDP.numDatabaseHotseatIcons) {
                 Log.e(TAG, "Error loading shortcut " + item
                         + " into hotseat position " + item.screenId
-                        + ", position out of bounds: (0 to " + (mIDP.numHotseatIcons - 1)
+                        + ", position out of bounds: (0 to " + (mIDP.numDatabaseHotseatIcons - 1)
                         + ")");
                 return false;
             }
@@ -429,7 +429,7 @@
                     return true;
                 }
             } else {
-                final GridOccupancy occupancy = new GridOccupancy(mIDP.numHotseatIcons, 1);
+                final GridOccupancy occupancy = new GridOccupancy(mIDP.numDatabaseHotseatIcons, 1);
                 occupancy.cells[item.screenId][0] = true;
                 occupied.put(LauncherSettings.Favorites.CONTAINER_HOTSEAT, occupancy);
                 return true;
diff --git a/src/com/android/launcher3/model/ModelWriter.java b/src/com/android/launcher3/model/ModelWriter.java
index 312435d..080ce20 100644
--- a/src/com/android/launcher3/model/ModelWriter.java
+++ b/src/com/android/launcher3/model/ModelWriter.java
@@ -91,7 +91,7 @@
         // in the hotseat
         if (container == Favorites.CONTAINER_HOTSEAT) {
             item.screenId = mHasVerticalHotseat
-                    ? LauncherAppState.getIDP(mContext).numHotseatIcons - cellY - 1 : cellX;
+                    ? LauncherAppState.getIDP(mContext).numDatabaseHotseatIcons - cellY - 1 : cellX;
         } else {
             item.screenId = screenId;
         }
diff --git a/src/com/android/launcher3/model/data/PackageItemInfo.java b/src/com/android/launcher3/model/data/PackageItemInfo.java
index 7617d7e..a81fe6a 100644
--- a/src/com/android/launcher3/model/data/PackageItemInfo.java
+++ b/src/com/android/launcher3/model/data/PackageItemInfo.java
@@ -16,27 +16,47 @@
 
 package com.android.launcher3.model.data;
 
+import androidx.annotation.IntDef;
+
 import com.android.launcher3.LauncherSettings;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Objects;
 
 /**
  * Represents a {@link Package} in the widget tray section.
  */
 public class PackageItemInfo extends ItemInfoWithIcon {
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({NO_CATEGORY, CONVERSATIONS})
+    public @interface Category{}
+    /** The package is not categorized in the widget tray. */
+    public static final int NO_CATEGORY = 0;
+    /** The package is categorized to conversations widget in the widget tray. */
+    public static final int CONVERSATIONS = 1;
 
     /**
      * Package name of the {@link PackageItemInfo}.
      */
-    public String packageName;
+    public final String packageName;
+
+    /** Represents a widget category shown in the widget tray section. */
+    @Category public final int category;
 
     public PackageItemInfo(String packageName) {
+        this(packageName, NO_CATEGORY);
+    }
+
+    public PackageItemInfo(String packageName, @Category int category) {
         this.packageName = packageName;
+        this.category = category;
         this.itemType = LauncherSettings.Favorites.ITEM_TYPE_NON_ACTIONABLE;
     }
 
     public PackageItemInfo(PackageItemInfo copy) {
         this.packageName = copy.packageName;
+        this.category = copy.category;
         this.itemType = LauncherSettings.Favorites.ITEM_TYPE_NON_ACTIONABLE;
     }
 
diff --git a/src/com/android/launcher3/provider/ImportDataTask.java b/src/com/android/launcher3/provider/ImportDataTask.java
index a5462a6..c9af2fe 100644
--- a/src/com/android/launcher3/provider/ImportDataTask.java
+++ b/src/com/android/launcher3/provider/ImportDataTask.java
@@ -284,8 +284,9 @@
             insertOperations.clear();
         }
 
-        IntSparseArrayMap<Object> hotseatItems = GridSizeMigrationTask.removeBrokenHotseatItems(mContext);
-        int myHotseatCount = LauncherAppState.getIDP(mContext).numHotseatIcons;
+        IntSparseArrayMap<Object> hotseatItems = GridSizeMigrationTask
+                .removeBrokenHotseatItems(mContext);
+        int myHotseatCount = LauncherAppState.getIDP(mContext).numDatabaseHotseatIcons;
         if (hotseatItems.size() < myHotseatCount) {
             // Insufficient hotseat items. Add a few more.
             HotseatParserCallback parserCallback = new HotseatParserCallback(
diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java
index 53183bf..223f4f1 100644
--- a/src/com/android/launcher3/provider/RestoreDbTask.java
+++ b/src/com/android/launcher3/provider/RestoreDbTask.java
@@ -103,7 +103,7 @@
      */
     private void backupWorkspace(Context context, SQLiteDatabase db) throws Exception {
         InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
-        new GridBackupTable(context, db, idp.numHotseatIcons, idp.numColumns, idp.numRows)
+        new GridBackupTable(context, db, idp.numDatabaseHotseatIcons, idp.numColumns, idp.numRows)
                 .doBackup(getDefaultProfileId(db), GridBackupTable.OPTION_REQUIRES_SANITIZATION);
     }
 
@@ -111,7 +111,7 @@
             @NonNull DatabaseHelper helper, @NonNull BackupManager backupManager)
             throws Exception {
         final InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
-        GridBackupTable backupTable = new GridBackupTable(context, db, idp.numHotseatIcons,
+        GridBackupTable backupTable = new GridBackupTable(context, db, idp.numDatabaseHotseatIcons,
                 idp.numColumns, idp.numRows);
         if (backupTable.restoreFromRawBackupIfAvailable(getDefaultProfileId(db))) {
             int itemsDeleted = sanitizeDB(helper, db, backupManager);
diff --git a/src/com/android/launcher3/settings/NotificationDotsPreference.java b/src/com/android/launcher3/settings/NotificationDotsPreference.java
index afcf882..0ee2744 100644
--- a/src/com/android/launcher3/settings/NotificationDotsPreference.java
+++ b/src/com/android/launcher3/settings/NotificationDotsPreference.java
@@ -85,6 +85,12 @@
                 Settings.Secure.getUriFor(NOTIFICATION_ENABLED_LISTENERS),
                 false, mListenerListObserver);
         updateUI();
+
+        // Update intent
+        Bundle extras = new Bundle();
+        extras.putString(EXTRA_FRAGMENT_ARG_KEY, "notification_badging");
+        setIntent(new Intent("android.settings.NOTIFICATION_SETTINGS")
+                .putExtra(EXTRA_SHOW_FRAGMENT_ARGS, extras));
     }
 
     private void updateUI() {
diff --git a/src/com/android/launcher3/settings/PreferenceHighlighter.java b/src/com/android/launcher3/settings/PreferenceHighlighter.java
index 8ba8146..96ee216 100644
--- a/src/com/android/launcher3/settings/PreferenceHighlighter.java
+++ b/src/com/android/launcher3/settings/PreferenceHighlighter.java
@@ -24,16 +24,18 @@
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
+import android.graphics.RectF;
 import android.util.Property;
 import android.view.View;
 
-import com.android.launcher3.util.Themes;
-
+import androidx.preference.Preference;
 import androidx.recyclerview.widget.RecyclerView;
 import androidx.recyclerview.widget.RecyclerView.ItemDecoration;
 import androidx.recyclerview.widget.RecyclerView.State;
 import androidx.recyclerview.widget.RecyclerView.ViewHolder;
 
+import com.android.launcher3.util.Themes;
+
 /**
  * Utility class for highlighting a preference
  */
@@ -62,14 +64,16 @@
     private final Paint mPaint = new Paint();
     private final RecyclerView mRv;
     private final int mIndex;
+    private final Preference mPreference;
+    private final RectF mDrawRect = new RectF();
 
     private boolean mHighLightStarted = false;
     private int mHighlightColor = END_COLOR;
 
-
-    public PreferenceHighlighter(RecyclerView rv, int index) {
+    public PreferenceHighlighter(RecyclerView rv, int index, Preference preference) {
         mRv = rv;
         mIndex = index;
+        mPreference = preference;
     }
 
     @Override
@@ -92,7 +96,8 @@
         if (!mHighLightStarted) {
             // Start highlight
             int colorTo = setColorAlphaBound(Themes.getColorAccent(mRv.getContext()), 66);
-            ObjectAnimator anim = ObjectAnimator.ofArgb(this, HIGHLIGHT_COLOR, END_COLOR, colorTo);
+            ObjectAnimator anim = ObjectAnimator.ofArgb(this, HIGHLIGHT_COLOR, END_COLOR,
+                    colorTo);
             anim.setDuration(HIGHLIGHT_FADE_IN_DURATION);
             anim.setRepeatMode(ValueAnimator.REVERSE);
             anim.setRepeatCount(4);
@@ -108,7 +113,11 @@
 
         View view = holder.itemView;
         mPaint.setColor(mHighlightColor);
-        c.drawRect(0, view.getY(), parent.getWidth(), view.getY() + view.getHeight(), mPaint);
+        mDrawRect.set(0, view.getY(), parent.getWidth(), view.getY() + view.getHeight());
+        if (mPreference instanceof HighlightDelegate) {
+            ((HighlightDelegate) mPreference).offsetHighlight(view, mDrawRect);
+        }
+        c.drawRect(mDrawRect, mPaint);
     }
 
     private void removeHighlight() {
@@ -124,4 +133,16 @@
         });
         anim.start();
     }
+
+    /**
+     * Interface to be implemented by a preference to customize the highlight are
+     */
+    public interface HighlightDelegate {
+
+        /**
+         * Allows the preference to update the highlight area
+         */
+        void offsetHighlight(View prefView, RectF bounds);
+
+    }
 }
diff --git a/src/com/android/launcher3/settings/SettingsActivity.java b/src/com/android/launcher3/settings/SettingsActivity.java
index 5b42ac7..216510b 100644
--- a/src/com/android/launcher3/settings/SettingsActivity.java
+++ b/src/com/android/launcher3/settings/SettingsActivity.java
@@ -237,7 +237,9 @@
             RecyclerView list = getListView();
             PreferencePositionCallback callback = (PreferencePositionCallback) list.getAdapter();
             int position = callback.getPreferenceAdapterPosition(mHighLightKey);
-            return position >= 0 ? new PreferenceHighlighter(list, position) : null;
+            return position >= 0 ? new PreferenceHighlighter(
+                    list, position, screen.findPreference(mHighLightKey))
+                    : null;
         }
 
         private void requestAccessibilityFocus(@NonNull final RecyclerView rv) {
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutView.java b/src/com/android/launcher3/shortcuts/DeepShortcutView.java
index 1c1418c..cc658c9 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutView.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutView.java
@@ -30,12 +30,13 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.popup.PopupContainerWithArrow;
+import com.android.launcher3.views.BubbleTextHolder;
 
 /**
  * A {@link android.widget.FrameLayout} that contains a {@link DeepShortcutView}.
  * This lets us animate the DeepShortcutView (icon and text) separately from the background.
  */
-public class DeepShortcutView extends FrameLayout {
+public class DeepShortcutView extends FrameLayout implements BubbleTextHolder {
 
     private static final Point sTempPoint = new Point();
 
@@ -64,6 +65,7 @@
         mIconView = findViewById(R.id.icon);
     }
 
+    @Override
     public BubbleTextView getBubbleText() {
         return mBubbleText;
     }
diff --git a/src/com/android/launcher3/views/ArrowTipView.java b/src/com/android/launcher3/views/ArrowTipView.java
index 1f12a2f..89ff821 100644
--- a/src/com/android/launcher3/views/ArrowTipView.java
+++ b/src/com/android/launcher3/views/ArrowTipView.java
@@ -21,6 +21,7 @@
 import android.graphics.Paint;
 import android.graphics.drawable.ShapeDrawable;
 import android.os.Handler;
+import android.util.Log;
 import android.util.TypedValue;
 import android.view.Gravity;
 import android.view.MotionEvent;
@@ -29,6 +30,8 @@
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import androidx.annotation.Nullable;
+import androidx.annotation.Px;
 import androidx.core.content.ContextCompat;
 
 import com.android.launcher3.AbstractFloatingView;
@@ -43,6 +46,7 @@
  */
 public class ArrowTipView extends AbstractFloatingView {
 
+    private static final String TAG = ArrowTipView.class.getSimpleName();
     private static final long AUTO_CLOSE_TIMEOUT_MILLIS = 10 * 1000;
     private static final long SHOW_DELAY_MS = 200;
     private static final long SHOW_DURATION_MS = 300;
@@ -105,7 +109,8 @@
                 arrowLp.width, arrowLp.height, false));
         Paint arrowPaint = arrowDrawable.getPaint();
         TypedValue typedValue = new TypedValue();
-        context.getTheme().resolveAttribute(android.R.attr.colorAccent, typedValue, true);
+        context.getTheme()
+                .resolveAttribute(android.R.attr.colorAccent, typedValue, true);
         arrowPaint.setColor(ContextCompat.getColor(getContext(), typedValue.resourceId));
         // The corner path effect won't be reflected in the shadow, but shouldn't be noticeable.
         arrowPaint.setPathEffect(new CornerPathEffect(
@@ -165,6 +170,60 @@
     }
 
     /**
+     * Show the ArrowTipView (tooltip) custom aligned.
+     *
+     * @param text The text to be shown in the tooltip.
+     * @param arrowXCoord The X coordinate for the arrow on the tip. The arrow is usually in the
+     *                    center of ArrowTipView unless the ArrowTipView goes beyond screen margin.
+     * @param yCoord The Y coordinate of the bottom of the tooltip.
+     * @return The tool tip view.
+     */
+    @Nullable public ArrowTipView showAtLocation(String text, int arrowXCoord, int yCoord) {
+        ViewGroup parent = mActivity.getDragLayer();
+        @Px int parentViewWidth = parent.getWidth();
+        @Px int textViewWidth = getContext().getResources()
+                .getDimensionPixelSize(R.dimen.widget_picker_education_tip_width);
+        @Px int minViewMargin = getContext().getResources()
+                .getDimensionPixelSize(R.dimen.widget_picker_education_tip_min_margin);
+        if (parentViewWidth < textViewWidth + 2 * minViewMargin) {
+            Log.w(TAG, "Cannot display tip on a small screen of size: " + parentViewWidth);
+            return null;
+        }
+
+        TextView textView = findViewById(R.id.text);
+        textView.setText(text);
+        textView.setWidth(textViewWidth);
+        parent.addView(this);
+        requestLayout();
+
+        post(() -> setY(yCoord - getHeight()));
+        post(() -> {
+            float halfWidth = getWidth() / 2f;
+            float xCoord;
+            if (arrowXCoord - halfWidth < minViewMargin) {
+                xCoord = minViewMargin;
+            } else if (arrowXCoord + halfWidth > parentViewWidth - minViewMargin) {
+                xCoord = parentViewWidth - minViewMargin - getWidth();
+            } else {
+                xCoord = arrowXCoord - halfWidth;
+            }
+            setX(xCoord);
+            findViewById(R.id.arrow).setX(arrowXCoord - xCoord);
+            requestLayout();
+        });
+
+        setAlpha(0);
+        animate()
+                .alpha(1f)
+                .withLayer()
+                .setStartDelay(SHOW_DELAY_MS)
+                .setDuration(SHOW_DURATION_MS)
+                .setInterpolator(Interpolators.DEACCEL)
+                .start();
+        return this;
+    }
+
+    /**
      * Register a callback fired when toast is hidden
      */
     public ArrowTipView setOnClosedCallback(Runnable runnable) {
diff --git a/src/com/android/launcher3/views/BubbleTextHolder.java b/src/com/android/launcher3/views/BubbleTextHolder.java
new file mode 100644
index 0000000..47d3563
--- /dev/null
+++ b/src/com/android/launcher3/views/BubbleTextHolder.java
@@ -0,0 +1,25 @@
+/*
+ * 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.views;
+
+import com.android.launcher3.BubbleTextView;
+
+/**
+ * Views that contain {@link BubbleTextView} should implement this interface.
+ */
+public interface BubbleTextHolder {
+    BubbleTextView getBubbleText();
+}
diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
index 23c3722..96268ce 100644
--- a/src/com/android/launcher3/views/FloatingIconView.java
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -210,8 +210,8 @@
     public static void getLocationBoundsForView(Launcher launcher, View v, boolean isOpening,
             RectF outRect, Rect outViewBounds) {
         boolean ignoreTransform = !isOpening;
-        if (v instanceof DeepShortcutView) {
-            v = ((DeepShortcutView) v).getBubbleText();
+        if (v instanceof BubbleTextHolder) {
+            v = ((BubbleTextHolder) v).getBubbleText();
             ignoreTransform = false;
         } else if (v.getParent() instanceof DeepShortcutView) {
             v = ((DeepShortcutView) v.getParent()).getIconView();
diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
index bbb0d92..155a285 100644
--- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
@@ -170,9 +170,9 @@
         WidgetCell widget = (WidgetCell) LayoutInflater.from(getContext())
                 .inflate(R.layout.widget_cell, parent, false);
 
-        WidgetImageView preview = widget.findViewById(R.id.widget_preview);
-        preview.setOnClickListener(this);
-        preview.setOnLongClickListener(this);
+        View previewContainer = widget.findViewById(R.id.widget_preview_container);
+        previewContainer.setOnClickListener(this);
+        previewContainer.setOnLongClickListener(this);
         widget.setAnimatePreview(false);
 
         parent.addView(widget);
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index a4257a2..90cd515 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -41,6 +41,7 @@
 
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
+import androidx.core.view.ViewCompat;
 import androidx.recyclerview.widget.RecyclerView;
 
 import com.android.launcher3.DeviceProfile;
@@ -51,6 +52,7 @@
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.compat.AccessibilityManagerCompat;
 import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.views.ArrowTipView;
 import com.android.launcher3.views.RecyclerViewFastScroller;
 import com.android.launcher3.views.TopRoundedCornerView;
 import com.android.launcher3.widget.BaseWidgetSheet;
@@ -66,6 +68,7 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.function.Predicate;
+import java.util.stream.IntStream;
 
 /**
  * Popup for showing the full list of available widgets
@@ -78,11 +81,13 @@
 
     private static final long DEFAULT_OPEN_DURATION = 267;
     private static final long FADE_IN_DURATION = 150;
+    private static final long EDUCATION_TIP_DELAY_MS = 200;
     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 static final String WIDGETS_EDUCATION_TIP_SEEN = "launcher.widgets_education_tip_seen";
 
     private final Rect mInsets = new Rect();
     private final boolean mHasWorkProfile;
@@ -92,6 +97,35 @@
             mCurrentUser.equals(entry.mPkgItem.user);
     private final Predicate<WidgetsListBaseEntry> mWorkWidgetsFilter =
             mPrimaryWidgetsFilter.negate();
+    private final OnLayoutChangeListener mLayoutChangeListenerToShowTips =
+            new OnLayoutChangeListener() {
+                @Override
+                public void onLayoutChange(View v, int left, int top, int right, int bottom,
+                        int oldLeft, int oldTop, int oldRight, int oldBottom) {
+                    if (hasSeenEducationTip()) {
+                        removeOnLayoutChangeListener(this);
+                        return;
+                    }
+
+                    // Widgets are loaded asynchronously, We are adding a delay because we only want
+                    // to show the tip when the widget preview has finished loading and rendering in
+                    // this view.
+                    removeCallbacks(mShowEducationTipTask);
+                    postDelayed(mShowEducationTipTask, EDUCATION_TIP_DELAY_MS);
+                }
+            };
+
+    private final Runnable mShowEducationTipTask = () -> {
+        if (hasSeenEducationTip()) {
+            removeOnLayoutChangeListener(mLayoutChangeListenerToShowTips);
+            return;
+        }
+        View viewForTip = getViewToShowEducationTip();
+        if (viewForTip != null && ViewCompat.isLaidOut(viewForTip)) {
+            removeOnLayoutChangeListener(mLayoutChangeListenerToShowTips);
+            showEducationTipOnView(viewForTip);
+        }
+    };
     private final int mTabsHeight;
     private final int mWidgetCellHorizontalPadding;
 
@@ -170,6 +204,10 @@
 
         mSearchAndRecommendationViewHolder.mSearchBar.initialize(
                 mLauncher.getPopupDataProvider(), /* searchModeListener= */ this);
+
+        if (!hasSeenEducationTip()) {
+            addOnLayoutChangeListener(mLayoutChangeListenerToShowTips);
+        }
     }
 
     @Override
@@ -392,6 +430,7 @@
         mAdapters.get(AdapterHolder.SEARCH).mWidgetsListAdapter.setWidgetsOnSearch(entries);
         updateNoSearchResultsView(
                 mAdapters.get(AdapterHolder.SEARCH).mWidgetsListAdapter.getItemCount() == 0);
+        mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView.scrollToTop();
     }
 
     private void setViewVisibilityBasedOnSearch(boolean isInSearchMode) {
@@ -563,6 +602,49 @@
         mSearchAndRecommendationViewHolder.mSearchBar.clearSearchBarFocus();
     }
 
+    private void showEducationTipOnView(View view) {
+        mLauncher.getSharedPrefs().edit().putBoolean(WIDGETS_EDUCATION_TIP_SEEN, true).apply();
+        int[] coords = new int[2];
+        view.getLocationOnScreen(coords);
+        ArrowTipView arrowTipView = new ArrowTipView(mLauncher);
+        arrowTipView.showAtLocation(
+                getContext().getString(R.string.long_press_widget_to_add),
+                /* arrowXCoord= */coords[0] + view.getWidth() / 2,
+                /* yCoord= */coords[1]);
+    }
+
+    @Nullable private View getViewToShowEducationTip() {
+        if (mSearchAndRecommendationViewHolder.mRecommendedWidgetsTable.getVisibility() == VISIBLE
+                && mSearchAndRecommendationViewHolder.mRecommendedWidgetsTable.getChildCount() > 0
+        ) {
+            return ((ViewGroup) mSearchAndRecommendationViewHolder.mRecommendedWidgetsTable
+                    .getChildAt(0)).getChildAt(0);
+        }
+
+        AdapterHolder adapterHolder = mAdapters.get(mIsInSearchMode
+                ? AdapterHolder.SEARCH
+                : mViewPager == null
+                        ? AdapterHolder.PRIMARY
+                        : mViewPager.getCurrentPage());
+        WidgetsRowViewHolder viewHolderForTip =
+                (WidgetsRowViewHolder) IntStream.range(
+                                0, adapterHolder.mWidgetsListAdapter.getItemCount())
+                        .mapToObj(adapterHolder.mWidgetsRecyclerView::
+                                findViewHolderForAdapterPosition)
+                        .filter(viewHolder -> viewHolder instanceof WidgetsRowViewHolder)
+                        .findFirst()
+                        .orElse(null);
+        if (viewHolderForTip != null) {
+            return ((ViewGroup) viewHolderForTip.mTableContainer.getChildAt(0)).getChildAt(0);
+        }
+
+        return null;
+    }
+
+    private boolean hasSeenEducationTip() {
+        return mLauncher.getSharedPrefs().getBoolean(WIDGETS_EDUCATION_TIP_SEEN, false);
+    }
+
     /** A holder class for holding adapters & their corresponding recycler view. */
     private final class AdapterHolder {
         static final int PRIMARY = 0;
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListHeader.java b/src/com/android/launcher3/widget/picker/WidgetsListHeader.java
index 497c72e..8794a4a 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListHeader.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListHeader.java
@@ -156,6 +156,11 @@
     private void applyDrawables(Drawable icon) {
         icon.setBounds(0, 0, mIconSize, mIconSize);
 
+        LinearLayout.LayoutParams layoutParams =
+                (LinearLayout.LayoutParams) mAppIcon.getLayoutParams();
+        layoutParams.width = mIconSize;
+        layoutParams.height = mIconSize;
+        mAppIcon.setLayoutParams(layoutParams);
         mAppIcon.setImageDrawable(icon);
 
         // If the current icon is a placeholder color, animate its update.
diff --git a/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java b/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java
index 6569fb0..b95bb16 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java
@@ -17,8 +17,10 @@
 
 import android.content.Context;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.Gravity;
 import android.view.LayoutInflater;
+import android.view.View;
 import android.view.ViewGroup;
 import android.widget.TableLayout;
 import android.widget.TableRow;
@@ -31,14 +33,15 @@
 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 static final String TAG = "WidgetsRecommendationTableLayout";
+    private static final float DOWN_SCALE_RATIO = 0.9f;
+    private static final float MAX_DOWN_SCALE_RATIO = 0.5f;
     private final DeviceProfile mDeviceProfile;
     private final float mWidgetCellTextViewsHeight;
 
@@ -119,9 +122,9 @@
                 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);
+        View previewContainer = widget.findViewById(R.id.widget_preview_container);
+        previewContainer.setOnClickListener(mWidgetCellOnClickListener);
+        previewContainer.setOnLongClickListener(mWidgetCellOnLongClickListener);
         widget.setAnimatePreview(false);
 
         parent.addView(widget);
@@ -131,6 +134,10 @@
     private RecommendationTableData fitRecommendedWidgetsToTableSpace(
             float previewScale,
             List<ArrayList<WidgetItem>> recommendedWidgetsInTable) {
+        if (previewScale < MAX_DOWN_SCALE_RATIO) {
+            Log.w(TAG, "Hide recommended widgets. Can't down scale previews to " + previewScale);
+            return new RecommendationTableData(List.of(), previewScale);
+        }
         // A naive estimation of the widgets recommendation table height without inflation.
         float totalHeight = 0;
         for (int i = 0; i < recommendedWidgetsInTable.size(); i++) {
@@ -157,7 +164,7 @@
                             /* toIndex= */recommendedWidgetsInTable.size() - 1));
         }
 
-        float nextPreviewScale = previewScale * SCALE_DOWN_RATIO;
+        float nextPreviewScale = previewScale * DOWN_SCALE_RATIO;
         return fitRecommendedWidgetsToTableSpace(nextPreviewScale, recommendedWidgetsInTable);
     }
 
diff --git a/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
index b016b4f..69672f2 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
@@ -243,4 +243,19 @@
          */
         int getHeaderViewHeight();
     }
+
+    @Override
+    public void scrollToTop() {
+        if (mScrollbar != null) {
+            mScrollbar.reattachThumbToScroll();
+        }
+
+        if (getLayoutManager() instanceof LinearLayoutManager) {
+            if (getCurrentScrollY() == 0) {
+                // We are at the top, so don't scrollToPosition (would cause unnecessary relayout).
+                return;
+            }
+        }
+        scrollToPosition(0);
+    }
 }
diff --git a/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarController.java b/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarController.java
index d35a75b..a8294c0 100644
--- a/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarController.java
+++ b/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarController.java
@@ -102,7 +102,7 @@
     @Override
     public void clearSearchResult() {
         mSearchAlgorithm.cancel(/* interruptActiveRequests= */ true);
-        mInput.getText().clear();
+        mInput.setText("");
         clearFocus();
         mSearchModeListener.exitSearchMode();
     }
diff --git a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
index f82f2cc..be18e54 100644
--- a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
+++ b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
@@ -36,6 +36,7 @@
 import com.android.launcher3.widget.picker.WidgetsDiffReporter;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -58,6 +59,9 @@
     private static final String TAG = "WidgetsModel";
     private static final boolean DEBUG = false;
 
+    private static final ComponentName CONVERSATION_WIDGET = ComponentName.createRelative(
+            "com.android.systemui", ".people.widget.PeopleSpaceWidgetProvider");
+
     /* Map of widgets and shortcuts that are tracked per package. */
     private final Map<PackageItemInfo, List<WidgetItem>> mWidgetsList = new HashMap<>();
 
@@ -156,7 +160,7 @@
 
         // Temporary list for {@link PackageItemInfos} to avoid having to go through
         // {@link mPackageItemInfos} to locate the key to be used for {@link #mWidgetsList}
-        HashMap<PackageUserKey, PackageItemInfo> tmpPackageItemInfos = new HashMap<>();
+        HashMap<WidgetPackageOrCategoryKey, PackageItemInfo> tmpPackageItemInfos = new HashMap<>();
 
         // Clear the lists only if this is an update on all widgets and shortcuts. If packageUser
         // isn't null, only updates the shortcuts and widgets for the app represented in
@@ -168,11 +172,11 @@
         mWidgetsList.putAll(rawWidgetsShortcuts.stream()
                 .filter(new WidgetValidityCheck(app))
                 .collect(Collectors.groupingBy(item -> {
-                    PackageUserKey packageUserKey = new PackageUserKey(
-                            item.componentName.getPackageName(), item.user);
+                    WidgetPackageOrCategoryKey packageUserKey = getWidgetPackageOrCategoryKey(item);
                     PackageItemInfo pInfo = tmpPackageItemInfos.get(packageUserKey);
                     if (pInfo == null) {
-                        pInfo = new PackageItemInfo(packageUserKey.mPackageName);
+                        pInfo = new PackageItemInfo(item.componentName.getPackageName(),
+                                packageUserKey.mCategory);
                         pInfo.user = item.user;
                         tmpPackageItemInfos.put(packageUserKey,  pInfo);
                     }
@@ -224,6 +228,13 @@
         return null;
     }
 
+    private WidgetPackageOrCategoryKey getWidgetPackageOrCategoryKey(WidgetItem item) {
+        if (CONVERSATION_WIDGET.equals(item.componentName)) {
+            return new WidgetPackageOrCategoryKey(PackageItemInfo.CONVERSATIONS, item.user);
+        }
+        return new WidgetPackageOrCategoryKey(item.componentName.getPackageName(), item.user);
+    }
+
     private static class WidgetValidityCheck implements Predicate<WidgetItem> {
 
         private final InvariantDeviceProfile mIdp;
@@ -265,4 +276,40 @@
             return true;
         }
     }
+
+    /** A hash key for grouping widgets by package name or category. */
+    private static class WidgetPackageOrCategoryKey {
+        /**
+         * The package name of the widget provider.
+         *
+         * <p>This shouldn't be empty if {@link #mCategory} has a value,
+         * {@link PackageItemInfo#NO_CATEGORY}.
+         */
+        public final String mPackage;
+        /** A widget category. */
+        @PackageItemInfo.Category public final int mCategory;
+        public final UserHandle mUser;
+        private final int mHashCode;
+
+        WidgetPackageOrCategoryKey(String packageName, UserHandle user) {
+            this(packageName,  PackageItemInfo.NO_CATEGORY, user);
+        }
+
+        WidgetPackageOrCategoryKey(@PackageItemInfo.Category int category, UserHandle user) {
+            this("", category, user);
+        }
+
+        private WidgetPackageOrCategoryKey(String packageName,
+                @PackageItemInfo.Category int category, UserHandle user) {
+            mPackage = packageName;
+            mCategory = category;
+            mUser = user;
+            mHashCode = Arrays.hashCode(new Object[]{mPackage, mCategory, mUser});
+        }
+
+        @Override
+        public int hashCode() {
+            return mHashCode;
+        }
+    }
 }
\ No newline at end of file
diff --git a/tests/res/layout/test_layout_appwidget_dynamic_colors.xml b/tests/res/layout/test_layout_appwidget_dynamic_colors.xml
index 21625c6..56c343e 100644
--- a/tests/res/layout/test_layout_appwidget_dynamic_colors.xml
+++ b/tests/res/layout/test_layout_appwidget_dynamic_colors.xml
@@ -14,7 +14,7 @@
             android:layout_width="0dp"
             android:layout_height="wrap_content"
             android:layout_weight="1"
-            android:text="prim"/>
+            android:text="neut1"/>
         <ImageView
             android:layout_width="24dp"
             android:layout_height="24dp"
@@ -28,7 +28,7 @@
             android:layout_width="0dp"
             android:layout_height="wrap_content"
             android:layout_weight="1"
-            android:text="second"/>
+            android:text="accent1"/>
         <ImageView
             android:layout_width="24dp"
             android:layout_height="24dp"
@@ -42,11 +42,11 @@
             android:layout_width="0dp"
             android:layout_height="wrap_content"
             android:layout_weight="1"
-            android:text="neutral"/>
+            android:text="accent2"/>
         <ImageView
             android:layout_width="24dp"
             android:layout_height="24dp"
-            android:background="@android:color/system_neutral2_500"/>
+            android:background="@android:color/system_accent2_500"/>
     </LinearLayout>
 
     </LinearLayout>
\ No newline at end of file
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index d4e8f1f..e7e245f 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -250,7 +250,7 @@
 
     @Before
     public void setUp() throws Exception {
-        Assert.assertTrue("Keyguard is visible",
+        Assert.assertTrue("Keyguard is visible, which is likely caused by a crash in SysUI",
                 TestHelpers.wait(
                         Until.gone(By.res(SYSTEMUI_PACKAGE, "keyguard_status_view")), 60000));
 
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 475a4ff..cf935f3 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -365,7 +365,7 @@
 
             if (hasSystemUiObject("keyguard_status_view")) return "Phone is locked";
 
-            if (!mDevice.wait(Until.hasObject(By.textStartsWith("")), WAIT_TIME_MS)) {
+            if (!mDevice.wait(Until.hasObject(getAnyObjectSelector()), WAIT_TIME_MS)) {
                 return "Screen is empty";
             }
 
@@ -388,7 +388,7 @@
     }
 
     private String getVisiblePackages() {
-        return mDevice.findObjects(By.textStartsWith(""))
+        return mDevice.findObjects(getAnyObjectSelector())
                 .stream()
                 .map(LauncherInstrumentation::getApplicationPackageSafe)
                 .distinct()
@@ -692,6 +692,9 @@
                     try (LauncherInstrumentation.Closable c = addContextLayer(
                             "Swiped up from context menu to home")) {
                         waitUntilLauncherObjectGone(CONTEXT_MENU_RES_ID);
+                        // Swiping up can temporarily bring Nexus Launcher if the current
+                        // Launcher is a Launcher3 one. Wait for the current launcher to reappear.
+                        waitForLauncherObject(getAnyObjectSelector());
                     }
                 }
                 if (hasLauncherObject(WORKSPACE_RES_ID)) {
@@ -736,9 +739,13 @@
         }
     }
 
+    private static BySelector getAnyObjectSelector() {
+        return By.textStartsWith("");
+    }
+
     boolean isLauncherVisible() {
         mDevice.waitForIdle();
-        return hasLauncherObject(By.textStartsWith(""));
+        return hasLauncherObject(getAnyObjectSelector());
     }
 
     /**