Merge "Sync split dismiss anim scrim fadee timing with taskbar to home anim duration" into main
diff --git a/Android.bp b/Android.bp
index 19d2a58..e2dd48d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -112,9 +112,6 @@
     resource_dirs: [],
     manifest: "tests/tapl/AndroidManifest.xml",
     platform_apis: true,
-    lint: {
-        baseline_filename: "lint-baseline.xml",
-    },
 }
 
 java_library_static {
@@ -132,9 +129,6 @@
         ],
     },
     static_libs: ["libprotobuf-java-lite"],
-    lint: {
-        baseline_filename: "lint-baseline.xml",
-    },
 }
 
 java_library_static {
@@ -153,9 +147,6 @@
         "libprotobuf-java-lite",
         "launcher_log_protos_lite",
     ],
-    lint: {
-        baseline_filename: "lint-baseline.xml",
-    },
 }
 
 java_library {
@@ -167,9 +158,6 @@
 
     sdk_version: "current",
     min_sdk_version: min_launcher3_sdk_version,
-    lint: {
-        baseline_filename: "lint-baseline.xml",
-    },
 }
 
 // Library with all the dependencies for building Launcher3
@@ -196,7 +184,7 @@
     sdk_version: "current",
     min_sdk_version: min_launcher3_sdk_version,
     lint: {
-        baseline_filename: "lint-baseline-res-lib.xml",
+        baseline_filename: "lint-baseline2.xml",
     },
 }
 
@@ -218,7 +206,7 @@
     min_sdk_version: min_launcher3_sdk_version,
     manifest: "AndroidManifest-common.xml",
     lint: {
-        baseline_filename: "lint-baseline-common-deps-lib.xml",
+        baseline_filename: "lint-baseline2.xml",
     },
 }
 
@@ -265,7 +253,7 @@
         "AndroidManifest-common.xml",
     ],
     lint: {
-        baseline_filename: "lint-baseline-launcher3.xml",
+        baseline_filename: "lint-baseline.xml",
     },
 }
 
@@ -289,9 +277,6 @@
     ],
     manifest: "quickstep/AndroidManifest.xml",
     min_sdk_version: "current",
-    lint: {
-        baseline_filename: "lint-baseline.xml",
-    },
 }
 
 // Library with all the dependencies for building Launcher Go
@@ -323,9 +308,6 @@
         "AndroidManifest-common.xml",
     ],
     min_sdk_version: "current",
-    lint: {
-        baseline_filename: "lint-baseline-go-res-lib.xml",
-    },
 }
 
 // Build rule for Quickstep library
@@ -354,9 +336,6 @@
     manifest: "quickstep/AndroidManifest.xml",
     platform_apis: true,
     min_sdk_version: "current",
-    lint: {
-        baseline_filename: "lint-baseline-launcher3.xml",
-    },
 }
 
 // Build rule for Launcher3 Go app for Android Go devices.
@@ -399,9 +378,6 @@
     jacoco: {
         include_filter: ["com.android.launcher3.*"],
     },
-    lint: {
-        baseline_filename: "lint-baseline.xml",
-    },
 
 }
 
@@ -438,9 +414,6 @@
     jacoco: {
         include_filter: ["com.android.launcher3.*"],
     },
-    lint: {
-        baseline_filename: "lint-baseline.xml",
-    },
 
 }
 
@@ -491,8 +464,5 @@
     jacoco: {
         include_filter: ["com.android.launcher3.*"],
     },
-    lint: {
-        baseline_filename: "lint-baseline.xml",
-    },
 
 }
diff --git a/aconfig/launcher.aconfig b/aconfig/launcher.aconfig
index a62f809..3166d82 100644
--- a/aconfig/launcher.aconfig
+++ b/aconfig/launcher.aconfig
@@ -71,13 +71,6 @@
 }
 
 flag {
-    name: "enable_launcher_br_metrics"
-    namespace: "launcher"
-    description: "Enables logging of Launcher restore metrics to the Backup & Restore team"
-    bug: "307527314"
-}
-
-flag {
     name: "enable_unfolded_two_pane_picker"
     namespace: "launcher"
     description: "Enables two pane widget picker for unfolded foldables"
@@ -97,3 +90,18 @@
     description: "Enables support for archived apps in Launcher3, such as empty progress bar etc."
     bug: "210590852"
 }
+
+flag {
+    name: "enable_private_space_install_shortcut"
+    namespace: "launcher"
+    description: "Enables long-press shortcut to install a copy of an app to Private space"
+    bug: "316118005"
+}
+
+flag {
+    name: "enable_launcher_br_metrics_fixed"
+    namespace: "launcher"
+    description: "Enables logging of Launcher restore metrics to the Backup & Restore team"
+    bug: "307527314"
+    is_fixed_read_only: true
+}
diff --git a/lint-baseline-common-deps-lib.xml b/lint-baseline-common-deps-lib.xml
deleted file mode 100644
index e52f8fb..0000000
--- a/lint-baseline-common-deps-lib.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-<?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
deleted file mode 100644
index c5669f2..0000000
--- a/lint-baseline-go-res-lib.xml
+++ /dev/null
@@ -1,576 +0,0 @@
-<?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
deleted file mode 100644
index 107a346..0000000
--- a/lint-baseline-launcher3.xml
+++ /dev/null
@@ -1,609 +0,0 @@
-<?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 28 (current min is 26): `android.os.UserManager#requestQuietModeEnabled`"
-        errorLine1="            showConfirm |= !userManager.requestQuietModeEnabled(!toState, userProfile);"
-        errorLine2="                                        ~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/apps/Launcher3/src/com/android/launcher3/allapps/WorkModeSwitch.java"
-            line="110"
-            column="41"/>
-    </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/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>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 26): `android.appwidget.AppWidgetHostView#setColorResources`"
-        errorLine1="                setColorResources(mWallpaperColorResources);"
-        errorLine2="                ~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/apps/Launcher3/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java"
-            line="528"
-            column="17"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 27 (current min is 26): `android.app.WallpaperManager#getWallpaperColors`"
-        errorLine1="                    : WallpaperManager.getInstance(context).getWallpaperColors(FLAG_SYSTEM);"
-        errorLine2="                                                            ~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/apps/Launcher3/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java"
-            line="288"
-            column="61"/>
-    </issue>
-
-</issues>
diff --git a/lint-baseline-res-lib.xml b/lint-baseline-res-lib.xml
deleted file mode 100644
index e52f8fb..0000000
--- a/lint-baseline-res-lib.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-<?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.xml b/lint-baseline.xml
index 23a22be..fe005ca 100644
--- a/lint-baseline.xml
+++ b/lint-baseline.xml
@@ -1,148 +1,169 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.0.0-dev" type="baseline" dependencies="true" variant="all" version="8.0.0-dev">
+<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01">
 
     <issue
         id="NewApi"
-        message="Call requires API level 28 (current min is 26): `android.app.Person#getKey`">
-        <location
-            file="packages/apps/Launcher3/src/com/android/launcher3/notification/NotificationKeyData.java"
-            line="72"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 29 (current min is 26): `android.content.res.Resources#getFloat`">
+        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"/>
+            line="73"
+            column="40"/>
     </issue>
 
     <issue
         id="NewApi"
-        message="Call requires API level 30 (current min is 26): `android.graphics.Outline#setPath`">
-        <location
-            file="packages/apps/Launcher3/src/com/android/launcher3/popup/RoundedArrowDrawable.java"
-            line="114"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 30 (current min is 26): `android.view.View#getWindowInsetsController`">
-        <location
-            file="packages/apps/Launcher3/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java"
-            line="902"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 30 (current min is 26): `android.view.WindowInsets.Type#ime`">
-        <location
-            file="packages/apps/Launcher3/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java"
-            line="902"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 30 (current min is 26): `android.view.WindowInsetsController#hide`">
-        <location
-            file="packages/apps/Launcher3/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java"
-            line="902"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 26): `android.appwidget.AppWidgetHostView#resetColorResources`">
+        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="114"/>
+            line="117"
+            column="13"/>
     </issue>
 
     <issue
         id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.appwidget.AppWidgetHostView#setColorResources`">
+        message="Call requires API level 31 (current min is 30): `android.appwidget.AppWidgetHostView#setColorResources`"
+        errorLine1="            view.setColorResources(mWallpaperColorResources);"
+        errorLine2="                 ~~~~~~~~~~~~~~~~~">
         <location
             file="packages/apps/Launcher3/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java"
-            line="415"/>
+            line="433"
+            column="18"/>
     </issue>
 
     <issue
         id="NewApi"
-        message="Field requires API level 28 (current min is 26): `android.appwidget.AppWidgetProviderInfo#widgetFeatures`">
-        <location
-            file="packages/apps/Launcher3/src/com/android/launcher3/widget/WidgetAddFlowHandler.java"
-            line="93"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Field requires API level 29 (current min is 26): `android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_PAGE_LEFT`">
-        <location
-            file="packages/apps/Launcher3/src/com/android/launcher3/PagedView.java"
-            line="1814"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Field requires API level 29 (current min is 26): `android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_PAGE_LEFT`">
-        <location
-            file="packages/apps/Launcher3/src/com/android/launcher3/PagedView.java"
-            line="1824"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Field requires API level 29 (current min is 26): `android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_PAGE_RIGHT`">
-        <location
-            file="packages/apps/Launcher3/src/com/android/launcher3/PagedView.java"
-            line="1815"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Field requires API level 29 (current min is 26): `android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_PAGE_RIGHT`">
-        <location
-            file="packages/apps/Launcher3/src/com/android/launcher3/PagedView.java"
-            line="1823"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Method reference requires API level 28 (current min is 26): `Person::getKey`">
-        <location
-            file="packages/apps/Launcher3/src/com/android/launcher3/model/data/WorkspaceItemInfo.java"
-            line="195"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Method reference requires API level 28 (current min is 26): `Person::getKey`">
+        message="Call requires API level 28 (current min is 26): `android.app.Person#getKey`"
+        errorLine1="        return people.stream().filter(person -&gt; person.getKey() != null)"
+        errorLine2="                                                       ~~~~~~">
         <location
             file="packages/apps/Launcher3/src/com/android/launcher3/notification/NotificationKeyData.java"
-            line="73"/>
+            line="72"
+            column="56"/>
     </issue>
 
     <issue
         id="NewApi"
-        message="`?android:attr/dialogCornerRadius` requires API level 28 (current min is 26)">
+        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/res/drawable/add_item_dialog_background.xml"
-            line="6"/>
+            file="packages/apps/Launcher3/src/com/android/launcher3/notification/NotificationKeyData.java"
+            line="73"
+            column="22"/>
     </issue>
 
     <issue
         id="NewApi"
-        message="`?android:attr/dialogCornerRadius` requires API level 28 (current min is 26)">
+        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/res/drawable/add_item_dialog_background.xml"
-            line="7"/>
+            file="packages/apps/Launcher3/src/com/android/launcher3/PagedView.java"
+            line="1814"
+            column="17"/>
     </issue>
 
     <issue
         id="NewApi"
-        message="`@android:dimen/system_app_widget_background_radius` requires API level 31 (current min is 26)">
+        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/res/drawable/widget_resize_frame.xml"
-            line="20"/>
+            file="packages/apps/Launcher3/src/com/android/launcher3/PagedView.java"
+            line="1815"
+            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="1823"
+            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="1824"
+            column="19"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 30 (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="114"
+            column="17"/>
+    </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 30 (current min is 26): `android.view.View#getWindowInsetsController`"
+        errorLine1="        WindowInsetsController insetsController = getWindowInsetsController();"
+        errorLine2="                                                  ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java"
+            line="820"
+            column="51"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 30 (current min is 26): `android.view.WindowInsets.Type#ime`"
+        errorLine1="            insetsController.hide(WindowInsets.Type.ime());"
+        errorLine2="                                                    ~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java"
+            line="822"
+            column="53"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 30 (current min is 26): `android.view.WindowInsetsController#hide`"
+        errorLine1="            insetsController.hide(WindowInsets.Type.ime());"
+        errorLine2="                             ~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java"
+            line="822"
+            column="30"/>
+    </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="194"
+            column="42"/>
     </issue>
 
 </issues>
\ No newline at end of file
diff --git a/lint-baseline2.xml b/lint-baseline2.xml
new file mode 100644
index 0000000..84f1b15
--- /dev/null
+++ b/lint-baseline2.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01">
+
+    <issue
+        id="NewApi"
+        message="`?android:attr/dialogCornerRadius` requires API level 28 (current min is 26)"
+        errorLine1='        android:topLeftRadius="?android:attr/dialogCornerRadius"'
+        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="?android:attr/dialogCornerRadius" /&gt;'
+        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/res/drawable/add_item_dialog_background.xml"
+            line="7"
+            column="9"/>
+    </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="@android:dimen/system_app_widget_background_radius" /&gt;'
+        errorLine2="             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/res/drawable/widget_resize_frame.xml"
+            line="20"
+            column="14"/>
+    </issue>
+
+</issues>
\ No newline at end of file
diff --git a/quickstep/res/values-de/strings.xml b/quickstep/res/values-de/strings.xml
index 3d4ce43..12c72e1 100644
--- a/quickstep/res/values-de/strings.xml
+++ b/quickstep/res/values-de/strings.xml
@@ -65,7 +65,7 @@
     <string name="home_gesture_intro_title" msgid="836590312858441830">"Den Startbildschirm aufrufen"</string>
     <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Wenn du zum Startbildschirm gehen möchtest, wische einfach vom unteren Displayrand nach oben."</string>
     <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Wische mit zwei Fingern vom unteren Displayrand nach oben. So gelangst du immer zum Startbildschirm."</string>
-    <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Zum StartU+00AD­bildschirm"</string>
+    <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Zum Start­bildschirm"</string>
     <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Wische vom unteren Displayrand nach oben"</string>
     <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Gut gemacht!"</string>
     <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Wische vom unteren Displayrand nach oben"</string>
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 2ebbe6f..8c9dc6a 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -1087,7 +1087,12 @@
         }
 
         backgroundRadiusAnim.addListener(
-                AnimatorListeners.forEndCallback(depthController::dispose));
+                AnimatorListeners.forEndCallback(() -> {
+                    // reset the depth to match the main depth controller's depth
+                    depthController.stateDepth
+                            .setValue(mLauncher.getDepthController().stateDepth.getValue());
+                    depthController.dispose();
+                }));
 
         return backgroundRadiusAnim;
     }
@@ -1161,6 +1166,7 @@
         SystemUiProxy.INSTANCE.get(mLauncher)
                 .registerRemoteTransition(mLauncherOpenTransition, homeCheck);
         if (mBackAnimationController != null) {
+            mBackAnimationController.registerComponentCallbacks();
             mBackAnimationController.registerBackCallbacks(mHandler);
         }
     }
@@ -1168,6 +1174,7 @@
     public void onActivityDestroyed() {
         unregisterRemoteAnimations();
         unregisterRemoteTransitions();
+        mLauncher.removeOnDeviceProfileChangeListener(this);
         SystemUiProxy.INSTANCE.get(mLauncher).setStartingWindowListener(null);
         ORDERED_BG_EXECUTOR.execute(() -> mLauncher.getContentResolver()
                 .unregisterContentObserver(mAnimationRemovalObserver));
@@ -1200,6 +1207,7 @@
         mWallpaperOpenTransitionRunner = null;
         if (mBackAnimationController != null) {
             mBackAnimationController.unregisterBackCallbacks();
+            mBackAnimationController.unregisterComponentCallbacks();
             mBackAnimationController = null;
         }
     }
@@ -1607,7 +1615,7 @@
                 || mLauncher.getWorkspace().isOverlayShown()
                 || shouldPlayFallbackClosingAnimation(appTargets);
 
-        boolean playWorkspaceReveal = true;
+        boolean playWorkspaceReveal = !fromPredictiveBack;
         boolean skipAllAppsScale = false;
         if (fromUnlock) {
             anim.play(getUnlockWindowAnimator(appTargets, wallpaperTargets));
@@ -1649,7 +1657,7 @@
         // targets list because it is already visible). In that case, we force
         // invisibility on touch down, and only reset it after the animation to home
         // is initialized.
-        if (launcherIsForceInvisibleOrOpening) {
+        if (launcherIsForceInvisibleOrOpening || fromPredictiveBack) {
             addCujInstrumentation(anim, playFallBackAnimation
                     ? Cuj.CUJ_LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK
                     : Cuj.CUJ_LAUNCHER_APP_CLOSE_TO_HOME);
@@ -1666,7 +1674,7 @@
             // Only register the content animation for cancellation when state changes
             mLauncher.getStateManager().setCurrentAnimation(anim);
 
-            if (mLauncher.isInState(LauncherState.ALL_APPS)) {
+            if (mLauncher.isInState(LauncherState.ALL_APPS) && !fromPredictiveBack) {
                 Pair<AnimatorSet, Runnable> contentAnimator =
                         getLauncherContentAnimator(false, LAUNCHER_RESUME_START_DELAY,
                                 skipAllAppsScale);
@@ -1677,10 +1685,8 @@
                         contentAnimator.second.run();
                     }
                 });
-            } else {
-                if (playWorkspaceReveal) {
+            } else if (playWorkspaceReveal) {
                     anim.play(new WorkspaceRevealAnim(mLauncher, false).getAnimators());
-                }
             }
         }
 
diff --git a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
index ea1d286..f012197 100644
--- a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
+++ b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
@@ -56,6 +56,11 @@
 
     private final T mActivityContext;
     private int mNumPredictedAppsPerRow;
+    // Vertical padding of the icon that contributes to the expected cell height.
+    private final int mVerticalPadding;
+    // Extra padding that is used in the top app rows (prediction and search) that is not used in
+    // the regular A-Z list. This only applies to single line label.
+    private final int mTopRowExtraHeight;
 
     // Helper to drawing the focus indicator.
     private final FocusIndicatorHelper mFocusHelper;
@@ -78,6 +83,10 @@
         mFocusHelper = new SimpleFocusIndicatorHelper(this);
         mActivityContext = ActivityContext.lookupContext(context);
         mNumPredictedAppsPerRow = mActivityContext.getDeviceProfile().numShownAllAppsColumns;
+        mTopRowExtraHeight = getResources().getDimensionPixelSize(
+                R.dimen.all_apps_search_top_row_extra_height);
+        mVerticalPadding = getResources().getDimensionPixelSize(
+                R.dimen.all_apps_predicted_icon_vertical_padding);
         updateVisibility();
     }
 
@@ -126,13 +135,11 @@
         int iconHeight = deviceProfile.allAppsIconSizePx;
         int iconPadding = deviceProfile.allAppsIconDrawablePaddingPx;
         int textHeight = Utilities.calculateTextHeight(deviceProfile.allAppsIconTextSizePx);
-        int verticalPadding = getResources().getDimensionPixelSize(
-                R.dimen.all_apps_predicted_icon_vertical_padding);
-        int totalHeight = iconHeight + iconPadding + textHeight + verticalPadding * 2;
-        if (FeatureFlags.enableTwolineAllapps()) {
-            // Add extra textHeight to the existing total height.
-            totalHeight += textHeight;
-        }
+        int totalHeight = iconHeight + iconPadding + textHeight + mVerticalPadding * 2;
+        // Prediction row height will be 4dp bigger than the regular apps in A-Z list when two line
+        // is not enabled. Otherwise, the extra height will increase by just the textHeight.
+        int extraHeight = FeatureFlags.enableTwolineAllapps() ? textHeight : mTopRowExtraHeight;
+        totalHeight += extraHeight;
         return getVisibility() == GONE ? 0 : totalHeight + getPaddingTop() + getPaddingBottom();
     }
 
diff --git a/quickstep/src/com/android/launcher3/model/AppEventProducer.java b/quickstep/src/com/android/launcher3/model/AppEventProducer.java
index 7209bd8..8dded8f 100644
--- a/quickstep/src/com/android/launcher3/model/AppEventProducer.java
+++ b/quickstep/src/com/android/launcher3/model/AppEventProducer.java
@@ -191,7 +191,7 @@
     @Nullable
     AppTarget toAppTarget(LauncherAtom.ItemInfo info) {
         int iconInfoType = getIconInfoTypeFromItemInfo(info);
-        UserCache userCache = UserCache.getInstance(mContext);
+        UserCache userCache = UserCache.INSTANCE.get(mContext);
         UserHandle userHandle = userCache.getUserProfiles().stream()
                 .filter(user -> userCache.getUserInfo(user).type == iconInfoType)
                 .findFirst()
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
index a7e8118..b7e1092 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
@@ -15,6 +15,8 @@
  */
 package com.android.launcher3.statehandlers;
 
+import static android.view.View.VISIBLE;
+
 import static com.android.launcher3.LauncherState.BACKGROUND_APP;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
@@ -269,6 +271,9 @@
         if (dragLayer != null) {
             dragLayer.setVisibility(visibility);
         }
+        if (mLauncher instanceof QuickstepLauncher ql && ql.getTaskbarUIController() != null) {
+            ql.getTaskbarUIController().onLauncherVisibilityChanged(visibility == VISIBLE);
+        }
     }
 
     private void markLauncherPaused() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
index 6e88780..2421c94 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
@@ -15,23 +15,31 @@
  */
 package com.android.launcher3.taskbar;
 
+import static android.window.SplashScreen.SPLASH_SCREEN_STYLE_UNDEFINED;
+
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 
 import android.animation.Animator;
+import android.app.ActivityOptions;
 import android.view.KeyEvent;
 import android.view.View;
+import android.view.animation.AnimationUtils;
+import android.window.RemoteTransition;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.AnimatorListeners;
 import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
 import com.android.launcher3.taskbar.overlay.TaskbarOverlayDragLayer;
 import com.android.quickstep.SystemUiProxy;
 import com.android.quickstep.util.GroupTask;
+import com.android.quickstep.util.SlideInRemoteTransition;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.QuickStepContract;
 
 import java.io.PrintWriter;
 import java.util.List;
@@ -142,19 +150,28 @@
             return -1;
         }
 
+        TaskbarActivityContext context = mControllers.taskbarActivityContext;
+        RemoteTransition remoteTransition = new RemoteTransition(new SlideInRemoteTransition(
+                Utilities.isRtl(mControllers.taskbarActivityContext.getResources()),
+                context.getDeviceProfile().overviewPageSpacing,
+                QuickStepContract.getWindowCornerRadius(context),
+                AnimationUtils.loadInterpolator(
+                        context, android.R.interpolator.fast_out_extra_slow_in)));
         if (mOnDesktop) {
             UI_HELPER_EXECUTOR.execute(() ->
                     SystemUiProxy.INSTANCE.get(mKeyboardQuickSwitchView.getContext())
                             .showDesktopApp(task.task1.key.id));
         } else if (task.task2 == null) {
-            UI_HELPER_EXECUTOR.execute(() ->
-                    ActivityManagerWrapper.getInstance().startActivityFromRecents(
-                            task.task1.key,
-                            mControllers.taskbarActivityContext.getActivityLaunchOptions(
-                                    taskView == null ? mKeyboardQuickSwitchView : taskView, null)
-                                    .options));
+            UI_HELPER_EXECUTOR.execute(() -> {
+                ActivityOptions activityOptions = mControllers.taskbarActivityContext
+                        .makeDefaultActivityOptions(SPLASH_SCREEN_STYLE_UNDEFINED).options;
+                activityOptions.setRemoteTransition(remoteTransition);
+
+                ActivityManagerWrapper.getInstance().startActivityFromRecents(
+                        task.task1.key, activityOptions);
+            });
         } else {
-            mControllers.uiController.launchSplitTasks(task);
+            mControllers.uiController.launchSplitTasks(task, remoteTransition);
         }
         return -1;
     }
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index 159a6ef..20e977b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -20,6 +20,7 @@
 import static com.android.launcher3.taskbar.TaskbarEduTooltipControllerKt.TOOLTIP_STEP_FEATURES;
 import static com.android.launcher3.taskbar.TaskbarLauncherStateController.FLAG_VISIBLE;
 import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS;
+import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
 
 import android.animation.Animator;
 import android.animation.AnimatorSet;
@@ -27,6 +28,7 @@
 import android.util.Log;
 import android.view.TaskTransitionSpec;
 import android.view.WindowManagerGlobal;
+import android.window.RemoteTransition;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -40,11 +42,13 @@
 import com.android.launcher3.logging.InstanceId;
 import com.android.launcher3.logging.InstanceIdSequence;
 import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.statehandlers.DesktopVisibilityController;
 import com.android.launcher3.taskbar.bubbles.BubbleBarController;
 import com.android.launcher3.uioverrides.QuickstepLauncher;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.MultiPropertyFactory;
 import com.android.launcher3.util.OnboardingPrefs;
+import com.android.quickstep.LauncherActivityInterface;
 import com.android.quickstep.RecentsAnimationCallbacks;
 import com.android.quickstep.util.GroupTask;
 import com.android.quickstep.views.RecentsView;
@@ -199,6 +203,16 @@
             return null;
         }
 
+        DesktopVisibilityController desktopController =
+                LauncherActivityInterface.INSTANCE.getDesktopVisibilityController();
+        final boolean onDesktop =
+                isDesktopModeSupported()
+                        && desktopController != null
+                        && desktopController.areFreeformTasksVisible();
+        if (onDesktop) {
+            isVisible = false;
+        }
+
         mTaskbarLauncherStateController.updateStateForFlag(FLAG_VISIBLE, isVisible);
         return mTaskbarLauncherStateController.applyState(fromInit ? 0 : duration, startAnimation);
     }
@@ -390,8 +404,9 @@
     }
 
     @Override
-    public void launchSplitTasks(@NonNull GroupTask groupTask) {
-        mLauncher.launchSplitTasks(groupTask);
+    public void launchSplitTasks(
+            @NonNull GroupTask groupTask, @Nullable RemoteTransition remoteTransition) {
+        mLauncher.launchSplitTasks(groupTask, remoteTransition);
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index 709d3ba..6d4fc18 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -61,6 +61,7 @@
 import android.graphics.Region;
 import android.graphics.Region.Op;
 import android.graphics.drawable.AnimatedVectorDrawable;
+import android.graphics.drawable.Drawable;
 import android.graphics.drawable.PaintDrawable;
 import android.graphics.drawable.RotateDrawable;
 import android.inputmethodservice.InputMethodService;
@@ -96,6 +97,7 @@
 import com.android.launcher3.util.TouchController;
 import com.android.launcher3.util.window.WindowManagerProxy;
 import com.android.launcher3.views.BaseDragLayer;
+import com.android.systemui.shared.navigationbar.KeyButtonRipple;
 import com.android.systemui.shared.rotation.FloatingRotationButton;
 import com.android.systemui.shared.rotation.RotationButton;
 import com.android.systemui.shared.rotation.RotationButtonController;
@@ -665,6 +667,11 @@
 
         for (ImageView button : mAllButtons) {
             button.setImageTintList(ColorStateList.valueOf(iconColor));
+            Drawable background = button.getBackground();
+            if (background instanceof KeyButtonRipple) {
+                ((KeyButtonRipple) background).setDarkIntensity(
+                        mTaskbarNavButtonDarkIntensity.value);
+            }
         }
     }
 
@@ -751,6 +758,7 @@
                             mA11yButton, res, isInKidsMode, isInSetup, isThreeButtonNav,
                             mContext.isPhoneMode(), mWindowManagerProxy.getRotation(mContext));
             navButtonLayoutter.layoutButtons(mContext, isA11yButtonPersistent());
+            updateButtonsBackground();
             updateNavButtonColor();
             return;
         }
@@ -870,7 +878,27 @@
                 }
             }
         }
+    }
 
+    private void updateButtonsBackground() {
+        boolean clipped = !mContext.isPhoneButtonNavMode();
+        mNavButtonContainer.setClipToPadding(clipped);
+        mNavButtonContainer.setClipChildren(clipped);
+        mNavButtonsView.setClipToPadding(clipped);
+        mNavButtonsView.setClipChildren(clipped);
+
+        for (ImageView button : mAllButtons) {
+            updateButtonBackground(button, mContext.isPhoneButtonNavMode());
+        }
+    }
+
+    private static void updateButtonBackground(View view, boolean isPhoneButtonNavMode) {
+        if (isPhoneButtonNavMode) {
+            view.setBackground(new KeyButtonRipple(view.getContext(), view,
+                    R.dimen.key_button_ripple_max_width));
+        } else {
+            view.setBackgroundResource(R.drawable.taskbar_icon_click_feedback_roundrect);
+        }
     }
 
     public void onDestroy() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
index e5a6021..382276a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
@@ -15,6 +15,8 @@
  */
 package com.android.launcher3.taskbar;
 
+import static android.view.Display.DEFAULT_DISPLAY;
+
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN;
 
 import android.animation.Animator;
@@ -83,6 +85,7 @@
 
     // States that affect whether region sampling is enabled or not
     private boolean mIsStashed;
+    private boolean mIsLumaSamplingEnabled;
     private boolean mTaskbarHidden;
 
     private float mTranslationYForSwipe;
@@ -234,8 +237,21 @@
     /** Called when taskbar is stashed or unstashed. */
     public void onIsStashedChanged() {
         mIsStashed = isStashedHandleVisible();
+        updateSamplingState();
+    }
+
+    public void onNavigationBarLumaSamplingEnabled(int displayId, boolean enable) {
+        if (DEFAULT_DISPLAY != displayId) {
+            return;
+        }
+
+        mIsLumaSamplingEnabled = enable;
+        updateSamplingState();
+    }
+
+    private void updateSamplingState() {
         updateRegionSamplingWindowVisibility();
-        if (mIsStashed) {
+        if (shouldSample()) {
             mStashedHandleView.updateSampledRegion(mStashedHandleBounds);
             mRegionSamplingHelper.start(mStashedHandleView.getSampledRegion());
         } else {
@@ -243,6 +259,10 @@
         }
     }
 
+    private boolean shouldSample() {
+        return mIsStashed && mIsLumaSamplingEnabled;
+    }
+
     protected void updateStashedHandleHintScale() {
         mStashedHandleView.setScaleX(mTaskbarStashedHandleHintScale.value);
         mStashedHandleView.setScaleY(mTaskbarStashedHandleHintScale.value);
@@ -282,7 +302,7 @@
     }
 
     private void updateRegionSamplingWindowVisibility() {
-        mRegionSamplingHelper.setWindowVisible(mIsStashed && !mTaskbarHidden);
+        mRegionSamplingHelper.setWindowVisible(shouldSample() && !mTaskbarHidden);
     }
 
     public boolean isStashedHandleVisible() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 300dd0d..7ad2c68 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -377,6 +377,8 @@
         onSystemBarAttributesChanged(sharedState.systemBarAttrsDisplayId,
                 sharedState.systemBarAttrsBehavior);
         onNavButtonsDarkIntensityChanged(sharedState.navButtonsDarkIntensity);
+        onNavigationBarLumaSamplingEnabled(sharedState.mLumaSamplingDisplayId,
+                sharedState.mIsLumaSamplingEnabled);
 
         if (ENABLE_TASKBAR_NAVBAR_UNIFICATION) {
             // W/ the flag not set this entire class gets re-created, which resets the value of
@@ -456,6 +458,10 @@
         return mTransientTaskbarBounds;
     }
 
+    protected float getCurrentTaskbarWidth() {
+        return mControllers.taskbarViewController.getCurrentVisualTaskbarWidth();
+    }
+
     @Override
     public StatsLogManager getStatsLogManager() {
         // Used to mock, can't mock a default interface method directly
@@ -841,6 +847,11 @@
                 .updateValue(darkIntensity);
     }
 
+    public void onNavigationBarLumaSamplingEnabled(int displayId, boolean enable) {
+        mControllers.stashedHandleViewController.onNavigationBarLumaSamplingEnabled(displayId,
+                enable);
+    }
+
     /**
      * Called to update a {@link AutohideSuspendFlag} with a new value.
      */
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
index 30f8d56..e290c3f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
@@ -203,12 +203,7 @@
         val newBackgroundHeight =
             mapRange(progress, backgroundHeightWhileAnimating, maxTransientTaskbarHeight)
         val fullWidth = transientBackgroundBounds.width()
-
-        // .9f is here to restrict min width of the background while animating, so transient
-        // background keeps it pill shape until animation end.
-        val animationWidth =
-            if (DisplayController.isTransientTaskbar(context)) fullWidth.toFloat() * .9f
-            else fullWidth.toFloat()
+        val animationWidth = context.currentTaskbarWidth
         val backgroundWidthWhileAnimating =
             if (isAnimatingPinning) animationWidth else stashedHandleWidth.toFloat()
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt
index dab9950..12f1e63 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt
@@ -181,7 +181,7 @@
                     measuredWidth.toFloat(),
                     measuredHeight.toFloat(),
                     (measuredWidth - arrowWidth) / 2, // arrowOffsetX
-                    0f, // arrowOffsetY
+                    -mArrowOffsetVertical.toFloat(), // arrowOffsetY
                     false, // isPointingUp
                     true, // leftAligned
                     context.getColor(R.color.popup_shade_first),
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
index f9fc983..491938d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
@@ -169,6 +169,7 @@
         mBackgroundRenderer.setBackgroundProgress(mTaskbarBackgroundProgress);
         mBackgroundRenderer.draw(canvas);
         super.dispatchDraw(canvas);
+        mControllerCallbacks.drawDebugUi(canvas);
     }
 
     /**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
index 3823c5a..3f5402f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
@@ -19,8 +19,10 @@
 import static com.android.launcher3.taskbar.TaskbarPinningController.PINNING_TRANSIENT;
 
 import android.content.res.Resources;
+import android.graphics.Canvas;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.os.SystemProperties;
 import android.view.ViewTreeObserver;
 
 import com.android.launcher3.DeviceProfile;
@@ -39,6 +41,9 @@
 public class TaskbarDragLayerController implements TaskbarControllers.LoggableTaskbarController,
         TaskbarControllers.BackgroundRendererController {
 
+    private static final boolean DEBUG = SystemProperties.getBoolean(
+            "persist.debug.draw_taskbar_debug_ui", false);
+
     private final TaskbarActivityContext mActivity;
     private final TaskbarDragLayer mTaskbarDragLayer;
     private final int mFolderMargin;
@@ -299,5 +304,15 @@
                     mTaskbarStashViaTouchController,
             };
         }
+
+        /**
+         * Draws debug UI on top of everything in TaskbarDragLayer.
+         */
+        public void drawDebugUi(Canvas canvas) {
+            if (!DEBUG) {
+                return;
+            }
+            mControllers.taskbarInsetsController.drawDebugTouchableRegionBounds(canvas);
+        }
     }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
index 1a34b7a..633383d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
@@ -15,7 +15,11 @@
  */
 package com.android.launcher3.taskbar
 
+import android.graphics.Canvas
+import android.graphics.Color
 import android.graphics.Insets
+import android.graphics.Paint
+import android.graphics.Rect
 import android.graphics.Region
 import android.inputmethodservice.InputMethodService.ENABLE_HIDE_IME_CAPTION_BAR
 import android.os.Binder
@@ -47,6 +51,7 @@
 import com.android.launcher3.util.DisplayController
 import java.io.PrintWriter
 import kotlin.jvm.optionals.getOrNull
+import kotlin.math.max
 
 /** Handles the insets that Taskbar provides to underlying apps and the IME. */
 class TaskbarInsetsController(val context: TaskbarActivityContext) : LoggableTaskbarController {
@@ -58,7 +63,8 @@
 
     /** The bottom insets taskbar provides to the IME when IME is visible. */
     val taskbarHeightForIme: Int = context.resources.getDimensionPixelSize(R.dimen.taskbar_ime_size)
-    private val touchableRegion: Region = Region()
+    // The touchableRegion we will set unless some other state takes precedence.
+    private val defaultTouchableRegion: Region = Region()
     private val insetsOwner: IBinder = Binder()
     private val deviceProfileChangeListener = { _: DeviceProfile ->
         onTaskbarOrBubblebarWindowHeightOrInsetsChanged()
@@ -69,6 +75,7 @@
             context,
             this::onTaskbarOrBubblebarWindowHeightOrInsetsChanged
         )
+    private val debugTouchableRegion = DebugTouchableRegion()
 
     // Initialized in init.
     private lateinit var controllers: TaskbarControllers
@@ -100,9 +107,11 @@
             }
 
         windowLayoutParams.providedInsets =
-            if (enableTaskbarNoRecreate()) {
-                getProvidedInsets(controllers.sharedState!!.insetsFrameProviders!!,
-                        insetsRoundedCornerFlag)
+            if (enableTaskbarNoRecreate() && controllers.sharedState != null) {
+                getProvidedInsets(
+                    controllers.sharedState!!.insetsFrameProviders,
+                    insetsRoundedCornerFlag
+                )
             } else {
                 getProvidedInsets(insetsRoundedCornerFlag)
             }
@@ -122,7 +131,7 @@
             } else {
                 0
             }
-        val touchableHeight = Math.max(taskbarTouchableHeight, bubblesTouchableHeight)
+        val touchableHeight = max(taskbarTouchableHeight, bubblesTouchableHeight)
 
         if (
             controllers.bubbleControllers.isPresent &&
@@ -130,14 +139,14 @@
         ) {
             val iconBounds =
                 controllers.bubbleControllers.get().bubbleBarViewController.bubbleBarBounds
-            touchableRegion.set(
+            defaultTouchableRegion.set(
                 iconBounds.left,
                 iconBounds.top,
                 iconBounds.right,
                 iconBounds.bottom
             )
         } else {
-            touchableRegion.set(
+            defaultTouchableRegion.set(
                 0,
                 windowLayoutParams.height - touchableHeight,
                 context.deviceProfile.widthPx,
@@ -164,19 +173,18 @@
 
     /**
      * This is for when ENABLE_TASKBAR_NO_RECREATION is enabled. We generate one instance of
-     * providedInsets and use it across the entire lifecycle of TaskbarManager. The only thing
-     * we need to reset is nav bar flags based on insetsRoundedCornerFlag.
+     * providedInsets and use it across the entire lifecycle of TaskbarManager. The only thing we
+     * need to reset is nav bar flags based on insetsRoundedCornerFlag.
      */
-    private fun getProvidedInsets(providedInsets: Array<InsetsFrameProvider>,
-                                  insetsRoundedCornerFlag: Int): Array<InsetsFrameProvider> {
+    private fun getProvidedInsets(
+        providedInsets: Array<InsetsFrameProvider>,
+        insetsRoundedCornerFlag: Int
+    ): Array<InsetsFrameProvider> {
         val navBarsFlag =
-                (if (context.isGestureNav) FLAG_SUPPRESS_SCRIM else 0) or insetsRoundedCornerFlag
+            (if (context.isGestureNav) FLAG_SUPPRESS_SCRIM else 0) or insetsRoundedCornerFlag
         for (provider in providedInsets) {
             if (provider.type == navigationBars()) {
-                provider.setFlags(
-                        navBarsFlag,
-                        FLAG_SUPPRESS_SCRIM or FLAG_INSETS_ROUNDED_CORNER
-                )
+                provider.setFlags(navBarsFlag, FLAG_SUPPRESS_SCRIM or FLAG_INSETS_ROUNDED_CORNER)
             }
         }
         return providedInsets
@@ -184,25 +192,22 @@
 
     /**
      * The inset types and number of insets provided have to match for both gesture nav and button
-     * nav. The values and the order of the elements in array are allowed to differ.
-     * Reason being WM does not allow types and number of insets changing for a given window once it
-     * is added into the hierarchy for performance reasons.
+     * nav. The values and the order of the elements in array are allowed to differ. Reason being WM
+     * does not allow types and number of insets changing for a given window once it is added into
+     * the hierarchy for performance reasons.
      */
     private fun getProvidedInsets(insetsRoundedCornerFlag: Int): Array<InsetsFrameProvider> {
         val navBarsFlag =
-                (if (context.isGestureNav) FLAG_SUPPRESS_SCRIM else 0) or insetsRoundedCornerFlag
+            (if (context.isGestureNav) FLAG_SUPPRESS_SCRIM else 0) or insetsRoundedCornerFlag
         return arrayOf(
-                InsetsFrameProvider(insetsOwner, 0, navigationBars())
-                        .setFlags(
-                                navBarsFlag,
-                                FLAG_SUPPRESS_SCRIM or FLAG_INSETS_ROUNDED_CORNER
-                        ),
-                InsetsFrameProvider(insetsOwner, 0, tappableElement()),
-                InsetsFrameProvider(insetsOwner, 0, mandatorySystemGestures()),
-                InsetsFrameProvider(insetsOwner, INDEX_LEFT, systemGestures())
-                        .setSource(SOURCE_DISPLAY),
-                InsetsFrameProvider(insetsOwner, INDEX_RIGHT, systemGestures())
-                        .setSource(SOURCE_DISPLAY)
+            InsetsFrameProvider(insetsOwner, 0, navigationBars())
+                .setFlags(navBarsFlag, FLAG_SUPPRESS_SCRIM or FLAG_INSETS_ROUNDED_CORNER),
+            InsetsFrameProvider(insetsOwner, 0, tappableElement()),
+            InsetsFrameProvider(insetsOwner, 0, mandatorySystemGestures()),
+            InsetsFrameProvider(insetsOwner, INDEX_LEFT, systemGestures())
+                .setSource(SOURCE_DISPLAY),
+            InsetsFrameProvider(insetsOwner, INDEX_RIGHT, systemGestures())
+                .setSource(SOURCE_DISPLAY)
         )
     }
 
@@ -216,46 +221,52 @@
             provider.insetsSize = getInsetsForGravity(tappableHeight, gravity)
         } else if (provider.type == systemGestures() && provider.index == INDEX_LEFT) {
             val leftIndexInset =
-                    if (context.isThreeButtonNav) 0
-                    else gestureNavSettingsObserver.getLeftSensitivityForCallingUser(res)
+                if (context.isThreeButtonNav) 0
+                else gestureNavSettingsObserver.getLeftSensitivityForCallingUser(res)
             provider.insetsSize = Insets.of(leftIndexInset, 0, 0, 0)
         } else if (provider.type == systemGestures() && provider.index == INDEX_RIGHT) {
             val rightIndexInset =
-                    if (context.isThreeButtonNav) 0
-                    else gestureNavSettingsObserver.getRightSensitivityForCallingUser(res)
+                if (context.isThreeButtonNav) 0
+                else gestureNavSettingsObserver.getRightSensitivityForCallingUser(res)
             provider.insetsSize = Insets.of(0, 0, rightIndexInset, 0)
         }
 
         // When in gesture nav, report the stashed height to the IME, to allow hiding the
         // IME navigation bar.
-        val imeInsetsSize = if (ENABLE_HIDE_IME_CAPTION_BAR && context.isGestureNav) {
-            getInsetsForGravity(controllers.taskbarStashController.stashedHeight, gravity);
-        } else {
-            getInsetsForGravity(taskbarHeightForIme, gravity)
-        }
+        val imeInsetsSize =
+            if (ENABLE_HIDE_IME_CAPTION_BAR && context.isGestureNav) {
+                getInsetsForGravity(controllers.taskbarStashController.stashedHeight, gravity)
+            } else {
+                getInsetsForGravity(taskbarHeightForIme, gravity)
+            }
         val imeInsetsSizeOverride =
-                arrayOf(
-                        InsetsFrameProvider.InsetsSizeOverride(TYPE_INPUT_METHOD, imeInsetsSize),
-                        InsetsFrameProvider.InsetsSizeOverride(TYPE_VOICE_INTERACTION,
-                                // No-op override to keep the size and types in sync with the
-                                // override below (insetsSizeOverrides must have the same length and
-                                // types after the window is added according to
-                                // WindowManagerService#relayoutWindow)
-                                provider.insetsSize)
+            arrayOf(
+                InsetsFrameProvider.InsetsSizeOverride(TYPE_INPUT_METHOD, imeInsetsSize),
+                InsetsFrameProvider.InsetsSizeOverride(
+                    TYPE_VOICE_INTERACTION,
+                    // No-op override to keep the size and types in sync with the
+                    // override below (insetsSizeOverrides must have the same length and
+                    // types after the window is added according to
+                    // WindowManagerService#relayoutWindow)
+                    provider.insetsSize
                 )
+            )
         // Use 0 tappableElement insets for the VoiceInteractionWindow when gesture nav is enabled.
         val visInsetsSizeForTappableElement =
-                if (context.isGestureNav) getInsetsForGravity(0, gravity)
-                else getInsetsForGravity(tappableHeight, gravity)
+            if (context.isGestureNav) getInsetsForGravity(0, gravity)
+            else getInsetsForGravity(tappableHeight, gravity)
         val insetsSizeOverrideForTappableElement =
-                arrayOf(
-                        InsetsFrameProvider.InsetsSizeOverride(TYPE_INPUT_METHOD, imeInsetsSize),
-                        InsetsFrameProvider.InsetsSizeOverride(TYPE_VOICE_INTERACTION,
-                                visInsetsSizeForTappableElement
-                        ),
-                )
-        if ((context.isGestureNav || ENABLE_TASKBAR_NAVBAR_UNIFICATION)
-                && provider.type == tappableElement()) {
+            arrayOf(
+                InsetsFrameProvider.InsetsSizeOverride(TYPE_INPUT_METHOD, imeInsetsSize),
+                InsetsFrameProvider.InsetsSizeOverride(
+                    TYPE_VOICE_INTERACTION,
+                    visInsetsSizeForTappableElement
+                ),
+            )
+        if (
+            (context.isGestureNav || ENABLE_TASKBAR_NAVBAR_UNIFICATION) &&
+                provider.type == tappableElement()
+        ) {
             provider.insetsSizeOverrides = insetsSizeOverrideForTappableElement
         } else if (provider.type != systemGestures()) {
             // We only override insets at the bottom of the screen
@@ -264,8 +275,8 @@
     }
 
     /**
-     * @return [Insets] where the [inset] is either used as a bottom inset or
-     * right/left inset if using 3 button nav
+     * @return [Insets] where the [inset] is either used as a bottom inset or right/left inset if
+     *   using 3 button nav
      */
     private fun getInsetsForGravity(inset: Int, gravity: Int): Insets {
         if ((gravity and Gravity.BOTTOM) == Gravity.BOTTOM) {
@@ -277,7 +288,7 @@
         val isSeascape = (gravity and Gravity.START) == Gravity.START
         val leftInset = if (isSeascape) inset else 0
         val rightInset = if (isSeascape) 0 else inset
-        return Insets.of(leftInset , 0, rightInset, 0)
+        return Insets.of(leftInset, 0, rightInset, 0)
     }
 
     /**
@@ -292,6 +303,8 @@
             context.dragLayer,
             insetsInfo.touchableRegion
         )
+        debugTouchableRegion.lastSetTouchableBounds.set(insetsInfo.touchableRegion.bounds)
+
         val bubbleBarVisible =
             controllers.bubbleControllers.isPresent &&
                 controllers.bubbleControllers.get().bubbleBarViewController.isBubbleBarVisible()
@@ -299,21 +312,28 @@
         if (context.dragLayer.alpha < AlphaUpdateListener.ALPHA_CUTOFF_THRESHOLD) {
             // Let touches pass through us.
             insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION)
+            debugTouchableRegion.lastSetTouchableReason = "Taskbar is invisible"
         } else if (
             controllers.navbarButtonsViewController.isImeVisible &&
                 controllers.taskbarStashController.isStashed
         ) {
+            // Let touches pass through us.
             insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION)
+            debugTouchableRegion.lastSetTouchableReason = "Stashed over IME"
         } else if (!controllers.uiController.isTaskbarTouchable) {
             // Let touches pass through us.
             insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION)
+            debugTouchableRegion.lastSetTouchableReason = "Taskbar is not touchable"
         } else if (controllers.taskbarDragController.isSystemDragInProgress) {
             // Let touches pass through us.
             insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION)
+            debugTouchableRegion.lastSetTouchableReason = "System drag is in progress"
         } else if (context.isTaskbarWindowFullscreen) {
             // Intercept entire fullscreen window.
             insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_FRAME)
             insetsIsTouchableRegion = false
+            debugTouchableRegion.lastSetTouchableReason = "Taskbar is fullscreen"
+            context.dragLayer.getBoundsInWindow(debugTouchableRegion.lastSetTouchableBounds, false)
         } else if (
             controllers.taskbarViewController.areIconsVisible() ||
                 context.isNavBarKidsModeActive ||
@@ -342,19 +362,33 @@
                     region.op(bubbleBarBounds, Region.Op.UNION)
                 }
                 insetsInfo.touchableRegion.set(region)
+                debugTouchableRegion.lastSetTouchableReason = "Transient Taskbar is in Overview"
+                debugTouchableRegion.lastSetTouchableBounds.set(region.bounds)
             } else {
-                insetsInfo.touchableRegion.set(touchableRegion)
+                insetsInfo.touchableRegion.set(defaultTouchableRegion)
+                debugTouchableRegion.lastSetTouchableReason = "Using default touchable region"
+                debugTouchableRegion.lastSetTouchableBounds.set(defaultTouchableRegion.bounds)
             }
             insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION)
             insetsIsTouchableRegion = false
         } else {
             insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION)
+            debugTouchableRegion.lastSetTouchableReason =
+                "Icons are not visible, but other components such as 3 buttons might be"
         }
         context.excludeFromMagnificationRegion(insetsIsTouchableRegion)
     }
 
+    /** Draws the last set touchableRegion as a red rectangle onto the given Canvas. */
+    fun drawDebugTouchableRegionBounds(canvas: Canvas) {
+        val paint = Paint()
+        paint.color = Color.RED
+        paint.style = Paint.Style.STROKE
+        canvas.drawRect(debugTouchableRegion.lastSetTouchableBounds, paint)
+    }
+
     override fun dumpLogs(prefix: String, pw: PrintWriter) {
-        pw.println(prefix + "TaskbarInsetsController:")
+        pw.println("${prefix}TaskbarInsetsController:")
         pw.println("$prefix\twindowHeight=${windowLayoutParams.height}")
         for (provider in windowLayoutParams.providedInsets) {
             pw.print(
@@ -373,5 +407,12 @@
             }
             pw.println()
         }
+        pw.println("$prefix\tlastSetTouchableBounds=${debugTouchableRegion.lastSetTouchableBounds}")
+        pw.println("$prefix\tlastSetTouchableReason=${debugTouchableRegion.lastSetTouchableReason}")
+    }
+
+    class DebugTouchableRegion {
+        val lastSetTouchableBounds = Rect()
+        var lastSetTouchableReason = ""
     }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index a2e5e81..33641a4 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -540,6 +540,14 @@
         }
     }
 
+    public void onNavigationBarLumaSamplingEnabled(int displayId, boolean enable) {
+        mSharedState.mLumaSamplingDisplayId = displayId;
+        mSharedState.mIsLumaSamplingEnabled = enable;
+        if (mTaskbarActivityContext != null) {
+            mTaskbarActivityContext.onNavigationBarLumaSamplingEnabled(displayId, enable);
+        }
+    }
+
     private void removeActivityCallbacksAndListeners() {
         if (mActivity != null) {
             mActivity.removeOnDeviceProfileChangeListener(mDebugActivityDeviceProfileChanged);
@@ -564,6 +572,7 @@
         UI_HELPER_EXECUTOR.execute(
                 () -> mTaskbarBroadcastReceiver.unregisterReceiverSafely(mContext));
         destroyExistingTaskbar();
+        removeTaskbarRootViewFromWindow();
         if (mUserUnlocked) {
             DisplayController.INSTANCE.get(mContext).removeChangeListener(mRecreationListener);
         }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarPinningController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarPinningController.kt
index 6cb28ee..2f2d636 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarPinningController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarPinningController.kt
@@ -20,10 +20,13 @@
 import android.view.View
 import androidx.annotation.VisibleForTesting
 import androidx.core.animation.doOnEnd
+import com.android.app.animation.Interpolators
 import com.android.launcher3.LauncherPrefs
 import com.android.launcher3.LauncherPrefs.Companion.TASKBAR_PINNING
 import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_DIVIDER_MENU_CLOSE
 import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_DIVIDER_MENU_OPEN
+import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_PINNED
+import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_UNPINNED
 import com.android.launcher3.taskbar.TaskbarDividerPopupView.Companion.createAndPopulate
 import java.io.PrintWriter
 
@@ -53,8 +56,10 @@
                 }
                 val animateToValue =
                     if (!launcherPrefs.get(TASKBAR_PINNING)) {
+                        statsLogManager.logger().log(LAUNCHER_TASKBAR_PINNED)
                         PINNING_PERSISTENT
                     } else {
+                        statsLogManager.logger().log(LAUNCHER_TASKBAR_UNPINNED)
                         PINNING_TRANSIENT
                     }
                 taskbarSharedState.taskbarWasPinned = animateToValue == PINNING_TRANSIENT
@@ -106,6 +111,7 @@
             taskbarViewController.taskbarIconTranslationXForPinning.animateToValue(animateToValue)
         )
 
+        animatorSet.interpolator = Interpolators.EMPHASIZED
         return animatorSet
     }
 
@@ -129,6 +135,6 @@
     companion object {
         const val PINNING_PERSISTENT = 1f
         const val PINNING_TRANSIENT = 0f
-        const val PINNING_ANIMATION_DURATION = 500L
+        const val PINNING_ANIMATION_DURATION = 600L
     }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java
index d09f74c..41b777b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java
@@ -52,6 +52,10 @@
     // TaskbarManager#onNavButtonsDarkIntensityChanged()
     public float navButtonsDarkIntensity;
 
+    // TaskbarManager#onNavigationBarLumaSamplingEnabled()
+    public int mLumaSamplingDisplayId;
+    public boolean mIsLumaSamplingEnabled;
+
     public boolean setupUIVisible = false;
 
     public boolean allAppsVisible = false;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index 7edf0d3..ecedf8a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -25,6 +25,7 @@
 import android.graphics.drawable.BitmapDrawable;
 import android.view.MotionEvent;
 import android.view.View;
+import android.window.RemoteTransition;
 
 import androidx.annotation.CallSuper;
 import androidx.annotation.NonNull;
@@ -302,7 +303,8 @@
     /**
      * Launches the given task in split-screen.
      */
-    public void launchSplitTasks(@NonNull GroupTask groupTask) { }
+    public void launchSplitTasks(
+            @NonNull GroupTask groupTask, @Nullable RemoteTransition remoteTransition) { }
 
     /**
      * Returns the matching view (if any) in the taskbar.
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index 33fb395..1f03f24 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -159,6 +159,8 @@
     private final int mTransientIconSize;
     private final int mPersistentIconSize;
 
+    private final float mTaskbarLeftRightMargin;
+
     public TaskbarViewController(TaskbarActivityContext activity, TaskbarView taskbarView) {
         mActivity = activity;
         mTransientTaskbarDp = mActivity.getTransientTaskbarDeviceProfile();
@@ -184,6 +186,9 @@
             mTaskbarThemedIconsBackgroundColor = ColorUtils.HSLToColor(colorHSL);
         }
         mIsRtl = Utilities.isRtl(mTaskbarView.getResources());
+        mTaskbarLeftRightMargin = mActivity.getResources().getDimensionPixelSize(
+                R.dimen.transient_taskbar_padding);
+
     }
 
     public void init(TaskbarControllers controllers) {
@@ -391,6 +396,26 @@
         }
     }
 
+    /**
+     * Calculates visual taskbar view width.
+     */
+    public float getCurrentVisualTaskbarWidth() {
+        if (mTaskbarView.getIconViews().length == 0) {
+            return 0;
+        }
+
+        View[] iconViews = mTaskbarView.getIconViews();
+
+        int leftIndex = mActivity.getDeviceProfile().isQsbInline && !mIsRtl ? 1 : 0;
+        int rightIndex = mActivity.getDeviceProfile().isQsbInline && mIsRtl
+                ? iconViews.length - 2
+                : iconViews.length - 1;
+
+        float left = iconViews[leftIndex].getX();
+        float right = iconViews[rightIndex].getRight() + iconViews[rightIndex].getTranslationX();
+
+        return right - left + (2 * mTaskbarLeftRightMargin);
+    }
 
     /**
      * Sets the translation of the TaskbarView during the swipe up gesture.
diff --git a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
index 784c560..3ebc8ed 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
@@ -80,6 +80,9 @@
                 if (android.os.Flags.allowPrivateProfile() && Flags.enablePrivateSpace()) {
                     LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
                     LauncherUserInfo launcherUserInfo = launcherApps.getLauncherUserInfo(user);
+                    if (launcherUserInfo == null) {
+                        continue;
+                    }
                     // UserTypes not supported in Launcher are deemed to be the current
                     // Foreground User.
                     int userType = switch (launcherUserInfo.getUserType()) {
@@ -129,8 +132,10 @@
     public static Intent getAppMarketActivityIntent(Context context, String packageName,
             UserHandle user) {
         LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
-        if (android.os.Flags.allowPrivateProfile() && Flags.enablePrivateSpace()
-                && Flags.privateSpaceAppInstallerButton()) {
+        if (android.os.Flags.allowPrivateProfile()
+                && Flags.enablePrivateSpace()
+                && (Flags.privateSpaceAppInstallerButton()
+                        || Flags.enablePrivateSpaceInstallShortcut())) {
             StartActivityParams params = new StartActivityParams((PendingIntent) null, 0);
             params.intentSender = launcherApps.getAppMarketActivityIntent(packageName, user);
             return ProxyActivityStarter.getLaunchIntent(context, params);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepAppWidgetHost.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepAppWidgetHost.java
index 6659fa0..45813ce 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepAppWidgetHost.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepAppWidgetHost.java
@@ -25,6 +25,7 @@
 import androidx.annotation.NonNull;
 
 import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.util.Executors;
 import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.widget.LauncherWidgetHolder;
 
@@ -55,7 +56,10 @@
 
     @Override
     public void onAppWidgetRemoved(int appWidgetId) {
-        mAppWidgetRemovedCallback.accept(appWidgetId);
+        // Route the call via model thread, in case it comes while a loader-bind is in progress
+        Executors.MODEL_EXECUTOR.execute(
+                () -> Executors.MAIN_EXECUTOR.execute(
+                        () -> mAppWidgetRemovedCallback.accept(appWidgetId)));
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index f45ddb6..0d4a7f0 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -40,6 +40,7 @@
 import static com.android.launcher3.popup.QuickstepSystemShortcut.getSplitSelectShortcutByPosition;
 import static com.android.launcher3.popup.SystemShortcut.APP_INFO;
 import static com.android.launcher3.popup.SystemShortcut.INSTALL;
+import static com.android.launcher3.popup.SystemShortcut.PRIVATE_PROFILE_INSTALL;
 import static com.android.launcher3.popup.SystemShortcut.WIDGETS;
 import static com.android.launcher3.taskbar.LauncherTaskbarUIController.ALL_APPS_PAGE_PROGRESS_INDEX;
 import static com.android.launcher3.taskbar.LauncherTaskbarUIController.MINUS_ONE_PAGE_PROGRESS_INDEX;
@@ -84,6 +85,7 @@
 import android.window.BackEvent;
 import android.window.OnBackAnimationCallback;
 import android.window.OnBackInvokedDispatcher;
+import android.window.RemoteTransition;
 import android.window.SplashScreen;
 
 import androidx.annotation.BinderThread;
@@ -94,6 +96,7 @@
 import com.android.app.viewcapture.SettingsAwareViewCapture;
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Flags;
 import com.android.launcher3.HomeTransitionController;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherSettings.Favorites;
@@ -414,6 +417,10 @@
         shortcuts.addAll(getSplitShortcuts());
         shortcuts.add(WIDGETS);
         shortcuts.add(INSTALL);
+
+        if (Flags.enablePrivateSpaceInstallShortcut()) {
+            shortcuts.add(PRIVATE_PROFILE_INSTALL);
+        }
         return shortcuts.stream();
     }
 
@@ -1271,7 +1278,8 @@
     /**
      * Launches the given {@link GroupTask} in splitscreen.
      */
-    public void launchSplitTasks(@NonNull GroupTask groupTask) {
+    public void launchSplitTasks(
+            @NonNull GroupTask groupTask, @Nullable RemoteTransition remoteTransition) {
         // Top/left and bottom/right tasks respectively.
         Task task1 = groupTask.task1;
         // task2 should never be null when calling this method. Allow a crash to catch invalid calls
@@ -1285,7 +1293,8 @@
                 /* freezeTaskList= */ false,
                 groupTask.mSplitBounds == null
                         ? SNAP_TO_50_50
-                        : groupTask.mSplitBounds.snapPosition);
+                        : groupTask.mSplitBounds.snapPosition,
+                remoteTransition);
     }
 
     /**
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java
index 1a75535..0fb2b17 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java
@@ -34,10 +34,10 @@
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.model.WidgetsModel;
 import com.android.launcher3.util.IntSet;
+import com.android.launcher3.util.SafeCloseable;
 import com.android.launcher3.widget.LauncherAppWidgetHostView;
 import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.widget.LauncherWidgetHolder;
-import com.android.launcher3.widget.custom.CustomWidgetManager;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -67,11 +67,11 @@
 
     private static AppWidgetHost sWidgetHost = null;
 
+    private final UpdateHandler mUpdateHandler = this::onWidgetUpdate;
     private final @Nullable RemoteViews.InteractionHandler mInteractionHandler;
 
     private final @NonNull IntConsumer mAppWidgetRemovedCallback;
 
-    private final ArrayList<ProviderChangedListener> mProviderChangedListeners = new ArrayList<>();
     // Map to all pending updated keyed with appWidgetId;
     private final SparseArray<PendingUpdate> mPendingUpdateMap = new SparseArray<>();
 
@@ -175,7 +175,10 @@
     @Override
     public void destroy() {
         try {
-            MAIN_EXECUTOR.submit(() -> sHolders.remove(this)).get();
+            MAIN_EXECUTOR.submit(() -> {
+                clearViews();
+                sHolders.remove(this);
+            }).get();
         } catch (Exception e) {
             Log.e(TAG, "Failed to remove self from holder list", e);
         }
@@ -188,26 +191,6 @@
     }
 
     /**
-     * Add a listener that is triggered when the providers of the widgets are changed
-     * @param listener The listener that notifies when the providers changed
-     */
-    @Override
-    public void addProviderChangeListener(
-            @NonNull LauncherWidgetHolder.ProviderChangedListener listener) {
-        MAIN_EXECUTOR.execute(() -> mProviderChangedListeners.add(listener));
-    }
-
-    /**
-     * Remove the specified listener from the host
-     * @param listener The listener that is to be removed from the host
-     */
-    @Override
-    public void removeProviderChangeListener(
-            LauncherWidgetHolder.ProviderChangedListener listener) {
-        MAIN_EXECUTOR.execute(() -> mProviderChangedListeners.remove(listener));
-    }
-
-    /**
      * Stop the host from updating the widget views
      */
     @Override
@@ -220,44 +203,41 @@
         setListeningFlag(false);
     }
 
-    /**
-     * Create a view for the specified app widget
-     * @param context The activity context for which the view is created
-     * @param appWidgetId The ID of the widget
-     * @param appWidget The {@link LauncherAppWidgetProviderInfo} of the widget
-     * @return A view for the widget
-     */
+    @Override
+    public SafeCloseable addOnUpdateListener(int appWidgetId,
+            LauncherAppWidgetProviderInfo appWidget, Runnable callback) {
+        UpdateHandler handler = new UpdateHandler() {
+            @Override
+            public <T> void onWidgetUpdate(int widgetId, UpdateKey<T> key, T data) {
+                if (KEY_VIEWS_UPDATE == key) {
+                    callback.run();
+                }
+            }
+        };
+        QuickstepWidgetHolderListener holderListener = getHolderListener(appWidgetId);
+        holderListener.addHolder(handler);
+        return () -> holderListener.mListeningHolders.remove(handler);
+    }
+
     @NonNull
     @Override
-    public LauncherAppWidgetHostView createView(@NonNull Context context, int appWidgetId,
-            @NonNull LauncherAppWidgetProviderInfo appWidget) {
-        if (appWidget.isCustomWidget()) {
-            LauncherAppWidgetHostView lahv = new LauncherAppWidgetHostView(context);
-            lahv.setAppWidget(appWidgetId, appWidget);
-            CustomWidgetManager.INSTANCE.get(context).onViewCreated(lahv);
-            return lahv;
-        }
-
-        LauncherAppWidgetHostView widgetView = getPendingView(appWidgetId);
-        if (widgetView != null) {
-            removePendingView(appWidgetId);
-        } else {
-            widgetView = new LauncherAppWidgetHostView(context);
-        }
+    protected LauncherAppWidgetHostView createViewInternal(
+            int appWidgetId, @NonNull LauncherAppWidgetProviderInfo appWidget) {
+        LauncherAppWidgetHostView widgetView = new LauncherAppWidgetHostView(mContext);
         widgetView.setInteractionHandler(mInteractionHandler);
         widgetView.setAppWidget(appWidgetId, appWidget);
-        mViews.put(appWidgetId, widgetView);
+        widgetView.updateAppWidget(getHolderListener(appWidgetId).addHolder(mUpdateHandler));
+        return widgetView;
+    }
 
+    private static QuickstepWidgetHolderListener getHolderListener(int appWidgetId) {
         QuickstepWidgetHolderListener listener = sListeners.get(appWidgetId);
         if (listener == null) {
             listener = new QuickstepWidgetHolderListener(appWidgetId);
             sWidgetHost.setListener(appWidgetId, listener);
             sListeners.put(appWidgetId, listener);
         }
-        RemoteViews remoteViews = listener.addHolder(this);
-        widgetView.updateAppWidget(remoteViews);
-
-        return widgetView;
+        return listener;
     }
 
     /**
@@ -267,7 +247,7 @@
     public void clearViews() {
         mViews.clear();
         for (int i = sListeners.size() - 1; i >= 0; i--) {
-            sListeners.valueAt(i).mListeningHolders.remove(this);
+            sListeners.valueAt(i).mListeningHolders.remove(mUpdateHandler);
         }
     }
 
@@ -275,7 +255,7 @@
             implements AppWidgetHost.AppWidgetHostListener {
 
         // Static listeners should use a set that is backed by WeakHashMap to avoid memory leak
-        private final Set<QuickstepWidgetHolder> mListeningHolders = Collections.newSetFromMap(
+        private final Set<UpdateHandler> mListeningHolders = Collections.newSetFromMap(
                 new WeakHashMap<>());
 
         private final int mWidgetId;
@@ -288,7 +268,7 @@
 
         @UiThread
         @Nullable
-        public RemoteViews addHolder(@NonNull QuickstepWidgetHolder holder) {
+        public RemoteViews addHolder(@NonNull UpdateHandler holder) {
             mListeningHolders.add(holder);
             return mRemoteViews;
         }
@@ -359,11 +339,15 @@
         }
     }
 
+    private interface UpdateKey<T> extends BiConsumer<AppWidgetHostView, T> { }
+
+    private interface UpdateHandler {
+        <T> void onWidgetUpdate(int widgetId, UpdateKey<T> key, T data);
+    }
+
     private static class PendingUpdate {
         public final IntSet changedViews = new IntSet();
         public AppWidgetProviderInfo providerInfo;
         public RemoteViews remoteViews;
     }
-
-    private interface UpdateKey<T> extends BiConsumer<AppWidgetHostView, T> { }
 }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
index d94cd89..5db5d17 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
@@ -106,7 +106,7 @@
     protected StateAnimationConfig getConfigForStates(
             LauncherState fromState, LauncherState toState) {
         final StateAnimationConfig config = new StateAnimationConfig();
-        config.userControlled = true;
+        config.animProps |= StateAnimationConfig.USER_CONTROLLED;
         if (fromState == NORMAL && toState == ALL_APPS) {
             AllAppsSwipeController.applyNormalToAllAppsAnimConfig(mLauncher, config);
         } else if (fromState == ALL_APPS && toState == NORMAL) {
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 42b18bd..cbf6ad6 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -1572,7 +1572,8 @@
                         mSwipePipToHomeAnimator.getTaskId(),
                         mSwipePipToHomeAnimator.getComponentName(),
                         mSwipePipToHomeAnimator.getDestinationBounds(),
-                        mSwipePipToHomeAnimator.getContentOverlay());
+                        mSwipePipToHomeAnimator.getContentOverlay(),
+                        mSwipePipToHomeAnimator.getAppBounds());
 
                 windowAnim = mSwipePipToHomeAnimators;
             } else {
diff --git a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
index b9029ea..877bdf8 100644
--- a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
+++ b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
@@ -23,13 +23,13 @@
 import static com.android.launcher3.BaseActivity.INVISIBLE_ALL;
 import static com.android.launcher3.BaseActivity.INVISIBLE_BY_PENDING_FLAGS;
 import static com.android.launcher3.BaseActivity.PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION;
-import static com.android.launcher3.LauncherPrefs.TASKBAR_PINNING;
-import static com.android.launcher3.config.FeatureFlags.enableTaskbarPinning;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.ValueAnimator;
+import android.content.ComponentCallbacks;
+import android.content.res.Configuration;
 import android.graphics.Matrix;
 import android.graphics.PointF;
 import android.graphics.Rect;
@@ -101,44 +101,45 @@
     private final RectF mCurrentRect = new RectF();
     private final QuickstepLauncher mLauncher;
     private final int mWindowScaleMarginX;
-    /** Max window translation in the Y axis. */
-    private final int mWindowMaxDeltaY;
-    private final float mWindowScaleEndCornerRadius;
-    private final float mWindowScaleStartCornerRadius;
+    private float mWindowScaleEndCornerRadius;
+    private float mWindowScaleStartCornerRadius;
     private final Interpolator mCancelInterpolator;
     private final Interpolator mProgressInterpolator = new DecelerateInterpolator();
     private final PointF mInitialTouchPos = new PointF();
 
     private RemoteAnimationTarget mBackTarget;
     private View mLauncherTargetView;
-    private SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
+    private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
     private boolean mSpringAnimationInProgress = false;
     private boolean mAnimatorSetInProgress = false;
     private float mBackProgress = 0;
     private boolean mBackInProgress = false;
     private OnBackInvokedCallbackStub mBackCallback;
     private IRemoteAnimationFinishedCallback mAnimationFinishedCallback;
-    private BackProgressAnimator mProgressAnimator = new BackProgressAnimator();
+    private final BackProgressAnimator mProgressAnimator = new BackProgressAnimator();
     private SurfaceControl mScrimLayer;
     private ValueAnimator mScrimAlphaAnimator;
     private float mScrimAlpha;
     private boolean mOverridingStatusBarFlags;
 
+    private final ComponentCallbacks mComponentCallbacks = new ComponentCallbacks() {
+        @Override
+        public void onConfigurationChanged(Configuration newConfig) {
+            loadCornerRadius();
+        }
+
+        @Override
+        public void onLowMemory() {}
+    };
+
     public LauncherBackAnimationController(
             QuickstepLauncher launcher,
             QuickstepTransitionManager quickstepTransitionManager) {
         mLauncher = launcher;
         mQuickstepTransitionManager = quickstepTransitionManager;
-        mWindowScaleEndCornerRadius = QuickStepContract.supportsRoundedCornersOnWindows(
-                mLauncher.getResources())
-                ? mLauncher.getResources().getDimensionPixelSize(
-                        R.dimen.swipe_back_window_corner_radius)
-                : 0;
-        mWindowScaleStartCornerRadius = QuickStepContract.getWindowCornerRadius(mLauncher);
+        loadCornerRadius();
         mWindowScaleMarginX = mLauncher.getResources().getDimensionPixelSize(
                 R.dimen.swipe_back_window_scale_x_margin);
-        mWindowMaxDeltaY = mLauncher.getResources().getDimensionPixelSize(
-                R.dimen.swipe_back_window_max_delta_y);
         mCancelInterpolator =
                 AnimationUtils.loadInterpolator(mLauncher, R.interpolator.standard_interpolator);
     }
@@ -301,11 +302,10 @@
         mInitialTouchPos.set(backEvent.getTouchX(), backEvent.getTouchY());
 
         mStartRect.set(appTarget.windowConfiguration.getMaxBounds());
-        if (mLauncher.getDeviceProfile().isTaskbarPresent && enableTaskbarPinning()
-                && LauncherPrefs.get(mLauncher).get(TASKBAR_PINNING)) {
-            int insetBottom = mStartRect.bottom - appTarget.contentInsets.bottom;
-            mStartRect.set(mStartRect.left, mStartRect.top, mStartRect.right, insetBottom);
-        }
+
+        // inset bottom in case of pinned taskbar being present
+        mStartRect.inset(0, 0, 0, appTarget.contentInsets.bottom);
+
         mLauncherTargetView = mQuickstepTransitionManager.findLauncherView(
                 new RemoteAnimationTarget[]{ mBackTarget });
         setLauncherTargetViewVisible(false);
@@ -551,6 +551,30 @@
         anim.start();
     }
 
+    private void loadCornerRadius() {
+        mWindowScaleEndCornerRadius = QuickStepContract.supportsRoundedCornersOnWindows(
+                mLauncher.getResources())
+                ? mLauncher.getResources().getDimensionPixelSize(
+                R.dimen.swipe_back_window_corner_radius)
+                : 0;
+        mWindowScaleStartCornerRadius = QuickStepContract.getWindowCornerRadius(mLauncher);
+    }
+
+    /**
+     * Called when launcher is destroyed. Unregisters component callbacks to avoid memory leaks.
+     */
+    public void unregisterComponentCallbacks() {
+        mLauncher.unregisterComponentCallbacks(mComponentCallbacks);
+    }
+
+    /**
+     * Registers component callbacks with the launcher to receive configuration change events.
+     */
+    public void registerComponentCallbacks() {
+        mLauncher.registerComponentCallbacks(mComponentCallbacks);
+    }
+
+
     private void resetScrim() {
         removeScrimLayer();
         mScrimAlpha = 0;
diff --git a/quickstep/src/com/android/quickstep/LauncherRestoreEventLoggerImpl.kt b/quickstep/src/com/android/quickstep/LauncherRestoreEventLoggerImpl.kt
index 0913fca..27bd03d 100644
--- a/quickstep/src/com/android/quickstep/LauncherRestoreEventLoggerImpl.kt
+++ b/quickstep/src/com/android/quickstep/LauncherRestoreEventLoggerImpl.kt
@@ -5,10 +5,9 @@
 import android.app.backup.BackupRestoreEventLogger.BackupRestoreDataType
 import android.app.backup.BackupRestoreEventLogger.BackupRestoreError
 import android.content.Context
-import com.android.launcher3.Flags.enableLauncherBrMetrics
+import com.android.launcher3.Flags.enableLauncherBrMetricsFixed
 import com.android.launcher3.LauncherSettings.Favorites
 import com.android.launcher3.backuprestore.LauncherRestoreEventLogger
-import com.android.launcher3.config.FeatureFlags.ENABLE_LAUNCHER_BR_METRICS
 
 /**
  * Concrete implementation for wrapper to log Restore event metrics for both success and failure to
@@ -45,7 +44,7 @@
         count: Int,
         @BackupRestoreError error: String?
     ) {
-        if (enableLauncherBrMetrics() || ENABLE_LAUNCHER_BR_METRICS.get()) {
+        if (enableLauncherBrMetricsFixed()) {
             restoreEventLogger.logItemsRestoreFailed(dataType, count, error)
         }
     }
@@ -57,7 +56,7 @@
      * @param count the number of data items restored.
      */
     override fun logLauncherItemsRestored(@BackupRestoreDataType dataType: String, count: Int) {
-        if (enableLauncherBrMetrics() || ENABLE_LAUNCHER_BR_METRICS.get()) {
+        if (enableLauncherBrMetricsFixed()) {
             restoreEventLogger.logItemsRestored(dataType, count)
         }
     }
@@ -68,7 +67,7 @@
      * @param favoritesId The id of the item type from [Favorites] that was restored.
      */
     override fun logSingleFavoritesItemRestored(favoritesId: Int) {
-        if (enableLauncherBrMetrics() || ENABLE_LAUNCHER_BR_METRICS.get()) {
+        if (enableLauncherBrMetricsFixed()) {
             restoreEventLogger.logItemsRestored(favoritesIdToDataType(favoritesId), 1)
         }
     }
@@ -80,7 +79,7 @@
      * @param count number of items that restored.
      */
     override fun logFavoritesItemsRestored(favoritesId: Int, count: Int) {
-        if (enableLauncherBrMetrics() || ENABLE_LAUNCHER_BR_METRICS.get()) {
+        if (enableLauncherBrMetricsFixed()) {
             restoreEventLogger.logItemsRestored(favoritesIdToDataType(favoritesId), count)
         }
     }
@@ -95,7 +94,7 @@
         favoritesId: Int,
         @BackupRestoreError error: String?
     ) {
-        if (enableLauncherBrMetrics() || ENABLE_LAUNCHER_BR_METRICS.get()) {
+        if (enableLauncherBrMetricsFixed()) {
             restoreEventLogger.logItemsRestoreFailed(favoritesIdToDataType(favoritesId), 1, error)
         }
     }
@@ -112,7 +111,7 @@
         count: Int,
         @BackupRestoreError error: String?
     ) {
-        if (enableLauncherBrMetrics() || ENABLE_LAUNCHER_BR_METRICS.get()) {
+        if (enableLauncherBrMetricsFixed()) {
             restoreEventLogger.logItemsRestoreFailed(
                 favoritesIdToDataType(favoritesId),
                 count,
@@ -126,7 +125,7 @@
      * done restoring items for Launcher.
      */
     override fun reportLauncherRestoreResults() {
-        if (enableLauncherBrMetrics() || ENABLE_LAUNCHER_BR_METRICS.get()) {
+        if (enableLauncherBrMetricsFixed()) {
             BackupManager(context).reportDelayedRestoreResult(restoreEventLogger)
         }
     }
diff --git a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
index 1b8dc14..65c8662 100644
--- a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
+++ b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
@@ -1,5 +1,6 @@
 package com.android.quickstep;
 
+import static com.android.launcher3.taskbar.TaskbarThresholdUtils.getFromNavThreshold;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 
 import android.app.Activity;
@@ -10,7 +11,6 @@
 
 import androidx.annotation.Nullable;
 
-import com.android.launcher3.R;
 import com.android.launcher3.taskbar.TaskbarActivityContext;
 import com.android.launcher3.testing.TestInformationHandler;
 import com.android.launcher3.testing.shared.TestProtocol;
@@ -99,7 +99,7 @@
             case TestProtocol.REQUEST_TASKBAR_FROM_NAV_THRESHOLD: {
                 final Resources resources = mContext.getResources();
                 response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD,
-                        resources.getDimensionPixelSize(R.dimen.taskbar_from_nav_threshold));
+                        getFromNavThreshold(resources, mDeviceProfile));
                 return response;
             }
 
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index 94ed5b9..a8c6809 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -630,10 +630,11 @@
      * should be responsible for cleaning up the overlay.
      */
     public void stopSwipePipToHome(int taskId, ComponentName componentName, Rect destinationBounds,
-            SurfaceControl overlay) {
+            SurfaceControl overlay, Rect appBounds) {
         if (mPip != null) {
             try {
-                mPip.stopSwipePipToHome(taskId, componentName, destinationBounds, overlay);
+                mPip.stopSwipePipToHome(taskId, componentName, destinationBounds, overlay,
+                        appBounds);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed call stopSwipePipToHome");
             }
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index 69db91b..4f885af 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -38,6 +38,7 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.UiThread;
 
+import com.android.internal.util.ArrayUtils;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.util.DisplayController;
@@ -185,6 +186,16 @@
                 cleanUpRecentsAnimation(newCallbacks);
             }
 
+            private boolean isNonRecentsStartedTasksAppeared(
+                    RemoteAnimationTarget[] appearedTaskTargets) {
+                // For example, right after swiping from task X to task Y (e.g. from
+                // AbsSwipeUpHandler#startNewTask), and then task Y starts X immediately
+                // (e.g. in Y's onResume). The case will be: lastStartedTask=Y and appearedTask=X.
+                return mLastGestureState.getEndTarget() == GestureState.GestureEndTarget.NEW_TASK
+                        && ArrayUtils.find(appearedTaskTargets,
+                                mLastGestureState.mLastStartedTaskIdPredicate) == null;
+            }
+
             @Override
             public void onTasksAppeared(RemoteAnimationTarget[] appearedTaskTargets) {
                 RemoteAnimationTarget appearedTaskTarget = appearedTaskTargets[0];
@@ -213,7 +224,8 @@
                     nonAppTargets = new RemoteAnimationTarget[0];
                 }
                 if ((activityInterface.isInLiveTileMode()
-                            || mLastGestureState.getEndTarget() == RECENTS)
+                            || mLastGestureState.getEndTarget() == RECENTS
+                            || isNonRecentsStartedTasksAppeared(appearedTaskTargets))
                         && activityInterface.getCreatedActivity() != null) {
                     RecentsView recentsView =
                             activityInterface.getCreatedActivity().getOverviewPanel();
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 6f45caf..9972e59 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -372,6 +372,12 @@
                     taskbarManager.onNavButtonsDarkIntensityChanged(darkIntensity));
         }
 
+        @Override
+        public void onNavigationBarLumaSamplingEnabled(int displayId, boolean enable) {
+            executeForTaskbarManager(taskbarManager ->
+                    taskbarManager.onNavigationBarLumaSamplingEnabled(displayId, enable));
+        }
+
         private void executeForTouchInteractionService(
                 @NonNull Consumer<TouchInteractionService> tisConsumer) {
             TouchInteractionService tis = mTis.get();
diff --git a/quickstep/src/com/android/quickstep/util/SlideInRemoteTransition.kt b/quickstep/src/com/android/quickstep/util/SlideInRemoteTransition.kt
new file mode 100644
index 0000000..6544ba7
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/SlideInRemoteTransition.kt
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.util
+
+import android.animation.TimeInterpolator
+import android.animation.ValueAnimator
+import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME
+import android.graphics.Rect
+import android.os.IBinder
+import android.os.RemoteException
+import android.view.SurfaceControl
+import android.view.SurfaceControl.Transaction
+import android.window.IRemoteTransition
+import android.window.IRemoteTransitionFinishedCallback
+import android.window.TransitionInfo
+import com.android.launcher3.anim.AnimatorListeners.forEndCallback
+import com.android.launcher3.util.Executors
+import com.android.wm.shell.util.TransitionUtil
+
+/** Remote animation which slides the opening targets in and the closing targets out */
+class SlideInRemoteTransition(
+    private val isRtl: Boolean,
+    private val pageSpacing: Int,
+    private val cornerRadius: Float,
+    private val interpolator: TimeInterpolator,
+) : IRemoteTransition.Stub() {
+    private val animationDurationMs = 500L
+
+    override fun mergeAnimation(
+        iBinder: IBinder,
+        transitionInfo: TransitionInfo,
+        transaction: Transaction,
+        mergeTarget: IBinder,
+        finishCB: IRemoteTransitionFinishedCallback
+    ) {
+
+        try {
+            finishCB.onTransitionFinished(null, Transaction())
+        } catch (e: RemoteException) {
+            // Ignore
+        }
+    }
+
+    override fun startAnimation(
+        transition: IBinder,
+        info: TransitionInfo,
+        startT: Transaction,
+        finishCB: IRemoteTransitionFinishedCallback
+    ) {
+        val anim = ValueAnimator.ofFloat(0f, 1f)
+        anim.interpolator = interpolator
+        anim.duration = animationDurationMs
+
+        val closingStartBounds: HashMap<SurfaceControl, Rect> = HashMap()
+        val openingEndBounds: HashMap<SurfaceControl, Rect> = HashMap()
+        for (chg in info.changes) {
+            val leash = chg.leash
+            startT.show(leash)
+
+            val taskInfo = chg.taskInfo
+            if (taskInfo?.activityType == ACTIVITY_TYPE_HOME || taskInfo?.parentTaskId != -1) {
+                continue
+            }
+            if (TransitionUtil.isClosingType(chg.mode)) {
+                closingStartBounds[leash] = chg.startAbsBounds
+                startT.setCrop(leash, chg.startAbsBounds).setCornerRadius(leash, cornerRadius)
+            }
+            if (TransitionUtil.isOpeningType(chg.mode)) {
+                openingEndBounds[leash] = chg.endAbsBounds
+                startT.setCrop(leash, chg.endAbsBounds).setCornerRadius(leash, cornerRadius)
+            }
+        }
+        startT.apply()
+
+        anim.addUpdateListener {
+            val t = Transaction()
+            closingStartBounds.keys.forEach {
+                // Translate the surface from its original position on-screen to off-screen on the
+                // right (or left in RTL)
+                val startBounds = closingStartBounds[it]
+                val targetX = (if (isRtl) -1 else 1) * (startBounds!!.right + pageSpacing)
+                t.setPosition(it, anim.animatedValue as Float * targetX, 0f)
+            }
+            openingEndBounds.keys.forEach {
+                // Set the alpha in the update listener to prevent one visible frame at the
+                // beginning
+                t.setAlpha(it, 1f)
+                // Translate the surface from off-screen on the left (or left in RTL) to its final
+                // position on-screen
+                val endBounds = openingEndBounds[it]
+                val targetX = (if (isRtl) -1 else 1) * (endBounds!!.right + pageSpacing)
+                t.setPosition(it, (1f - anim.animatedValue as Float) * -targetX, 0f)
+            }
+            t.apply()
+        }
+        anim.addListener(
+            forEndCallback(
+                Runnable {
+                    val t = Transaction()
+                    try {
+                        finishCB.onTransitionFinished(null, t)
+                    } catch (e: RemoteException) {
+                        // Ignore
+                    }
+                }
+            )
+        )
+
+        Executors.MAIN_EXECUTOR.execute { anim.start() }
+    }
+
+    override fun onTransitionConsumed(transition: IBinder?, aborted: Boolean) {}
+}
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index 193cc2b..3c90e0c 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -498,6 +498,29 @@
     }
 
     /**
+     * Used to launch split screen from a split pair that already exists, optionally with a custom
+     * remote transition.
+     * <p>
+     * See {@link SplitSelectStateController#launchExistingSplitPair(
+     * GroupedTaskView, int, int, int, Consumer, boolean, int, RemoteTransition)}
+     */
+    public void launchExistingSplitPair(@Nullable GroupedTaskView groupedTaskView,
+            int firstTaskId, int secondTaskId, @StagePosition int stagePosition,
+            Consumer<Boolean> callback, boolean freezeTaskList,
+            @PersistentSnapPosition int snapPosition) {
+        launchExistingSplitPair(
+                groupedTaskView,
+                firstTaskId,
+                secondTaskId,
+                stagePosition,
+                callback,
+                freezeTaskList,
+                snapPosition,
+                /* remoteTransition= */ null);
+    }
+
+
+    /**
      * Used to launch split screen from a split pair that already exists (usually accessible through
      * Overview). This is different than {@link #launchTasks(Consumer, boolean, int, InstanceId)}
      * in that this only launches split screen that are existing tasks. This doesn't determine which
@@ -509,7 +532,7 @@
     public void launchExistingSplitPair(@Nullable GroupedTaskView groupedTaskView,
             int firstTaskId, int secondTaskId, @StagePosition int stagePosition,
             Consumer<Boolean> callback, boolean freezeTaskList,
-            @PersistentSnapPosition int snapPosition) {
+            @PersistentSnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition) {
         mLaunchingTaskView = groupedTaskView;
         final ActivityOptions options1 = ActivityOptions.makeBasic();
         if (freezeTaskList) {
@@ -518,10 +541,12 @@
         Bundle optionsBundle = options1.toBundle();
 
         if (TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) {
-            final RemoteTransition remoteTransition = getShellRemoteTransition(firstTaskId,
-                    secondTaskId, callback, "LaunchExistingPair");
+            final RemoteTransition transition = remoteTransition == null
+                    ? getShellRemoteTransition(
+                            firstTaskId, secondTaskId, callback, "LaunchExistingPair")
+                    : remoteTransition;
             mSystemUiProxy.startTasks(firstTaskId, optionsBundle, secondTaskId, null /* options2 */,
-                    stagePosition, snapPosition, remoteTransition, null /*shellInstanceId*/);
+                    stagePosition, snapPosition, transition, null /*shellInstanceId*/);
         } else {
             final RemoteAnimationAdapter adapter = getLegacyRemoteAdapter(firstTaskId,
                     secondTaskId, callback);
diff --git a/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java b/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java
index cb32c6c..b663970 100644
--- a/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java
@@ -53,6 +53,7 @@
 
     private final QuickstepLauncher mLauncher;
     private final SplitSelectStateController mController;
+    private final RecentsAnimationDeviceState mDeviceState;
     private final OverviewComponentObserver mOverviewComponentObserver;
 
     private final int mSplitPlaceholderSize;
@@ -62,10 +63,9 @@
             SplitSelectStateController controller) {
         mLauncher = launcher;
         mController = controller;
-        RecentsAnimationDeviceState deviceState = new RecentsAnimationDeviceState(
-                launcher.getApplicationContext());
+        mDeviceState = new RecentsAnimationDeviceState(launcher.getApplicationContext());
         mOverviewComponentObserver = new OverviewComponentObserver(launcher.getApplicationContext(),
-                deviceState);
+                mDeviceState);
 
         mSplitPlaceholderSize = mLauncher.getResources().getDimensionPixelSize(
                 R.dimen.split_placeholder_size);
@@ -97,6 +97,7 @@
 
     public void onDestroy() {
         mOverviewComponentObserver.onDestroy();
+        mDeviceState.destroy();
     }
 
     private class SplitWithKeyboardShortcutRecentsAnimationListener implements
diff --git a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
index a36b32c..6c89be1 100644
--- a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
+++ b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
@@ -264,6 +264,10 @@
         return mDestinationBounds;
     }
 
+    public Rect getAppBounds() {
+        return mAppBounds;
+    }
+
     @Nullable
     public SurfaceControl getContentOverlay() {
         return mPipContentOverlay == null ? null : mPipContentOverlay.getLeash();
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index baaa062..065a9c5 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -74,6 +74,7 @@
     private final boolean mIsRecentsRtl;
 
     private final Rect mTaskRect = new Rect();
+    private final Rect mFullTaskSize = new Rect();
     private final PointF mPivot = new PointF();
     private DeviceProfile mDp;
     @StagePosition
@@ -131,6 +132,47 @@
         mDp = dp;
         mLayoutValid = false;
         mOrientationState.setDeviceProfile(dp);
+        calculateTaskSize();
+    }
+
+    private void calculateTaskSize() {
+        if (mDp == null) {
+            return;
+        }
+
+        if (mIsGridTask) {
+            mSizeStrategy.calculateGridTaskSize(mContext, mDp, mFullTaskSize,
+                    mOrientationState.getOrientationHandler());
+        } else {
+            mSizeStrategy.calculateTaskSize(mContext, mDp, mFullTaskSize,
+                    mOrientationState.getOrientationHandler());
+        }
+
+        if (mSplitBounds != null) {
+            // The task rect changes according to the staged split task sizes, but recents
+            // fullscreen scale and pivot remains the same since the task fits into the existing
+            // sized task space bounds
+            mTaskRect.set(mFullTaskSize);
+            mOrientationState.getOrientationHandler()
+                    .setSplitTaskSwipeRect(mDp, mTaskRect, mSplitBounds, mStagePosition);
+            mTaskRect.offset(mTaskRectTranslationX, mTaskRectTranslationY);
+        } else if (mIsDesktopTask) {
+            // For desktop, tasks can take up only part of the screen size.
+            // Full task size represents the whole screen size, but scaled down to fit in recents.
+            // Task rect will represent the scaled down thumbnail position and is placed inside
+            // full task size as it is on the home screen.
+            PointF fullscreenTaskDimension = new PointF();
+            BaseActivityInterface.getTaskDimension(mContext, mDp, fullscreenTaskDimension);
+            // Calculate the scale down factor used in recents
+            float scale = mFullTaskSize.width() / fullscreenTaskDimension.x;
+            mTaskRect.set(mThumbnailPosition);
+            mTaskRect.scale(scale);
+            // Ensure the task rect is inside the full task rect
+            mTaskRect.offset(mFullTaskSize.left, mFullTaskSize.top);
+        } else {
+            mTaskRect.set(mFullTaskSize);
+            mTaskRect.offset(mTaskRectTranslationX, mTaskRectTranslationY);
+        }
     }
 
     /**
@@ -148,44 +190,11 @@
         if (mDp == null) {
             return 1;
         }
-
-        if (mIsGridTask) {
-            mSizeStrategy.calculateGridTaskSize(mContext, mDp, mTaskRect,
-                    mOrientationState.getOrientationHandler());
-        } else {
-            mSizeStrategy.calculateTaskSize(mContext, mDp, mTaskRect,
-                    mOrientationState.getOrientationHandler());
-        }
-
-        Rect fullTaskSize;
-        if (mSplitBounds != null) {
-            // The task rect changes according to the staged split task sizes, but recents
-            // fullscreen scale and pivot remains the same since the task fits into the existing
-            // sized task space bounds
-            fullTaskSize = new Rect(mTaskRect);
-            mOrientationState.getOrientationHandler()
-                    .setSplitTaskSwipeRect(mDp, mTaskRect, mSplitBounds, mStagePosition);
-            mTaskRect.offset(mTaskRectTranslationX, mTaskRectTranslationY);
-        } else if (mIsDesktopTask) {
-            // For desktop, tasks can take up only part of the screen size.
-            // Full task size represents the whole screen size, but scaled down to fit in recents.
-            // Task rect will represent the scaled down thumbnail position and is placed inside
-            // full task size as it is on the home screen.
-            fullTaskSize = new Rect(mTaskRect);
-            PointF fullscreenTaskDimension = new PointF();
-            BaseActivityInterface.getTaskDimension(mContext, mDp, fullscreenTaskDimension);
-            // Calculate the scale down factor used in recents
-            float scale = fullTaskSize.width() / fullscreenTaskDimension.x;
-            mTaskRect.set(mThumbnailPosition);
-            mTaskRect.scale(scale);
-            // Ensure the task rect is inside the full task rect
-            mTaskRect.offset(fullTaskSize.left, fullTaskSize.top);
-        } else {
-            fullTaskSize = new Rect(mTaskRect);
-            mTaskRect.offset(mTaskRectTranslationX, mTaskRectTranslationY);
-        }
-        fullTaskSize.offset(mTaskRectTranslationX + mPivotOffsetX, mTaskRectTranslationY);
-        return mOrientationState.getFullScreenScaleAndPivot(fullTaskSize, mDp, mPivot);
+        // Copy mFullTaskSize instead of updating it directly so it could be reused next time
+        // without recalculating
+        Rect scaleRect = new Rect(mFullTaskSize);
+        scaleRect.offset(mTaskRectTranslationX + mPivotOffsetX, mTaskRectTranslationY);
+        return mOrientationState.getFullScreenScaleAndPivot(scaleRect, mDp, mPivot);
     }
 
     /**
@@ -209,13 +218,13 @@
         mSplitBounds = splitInfo;
         if (mSplitBounds == null) {
             mStagePosition = STAGE_POSITION_UNDEFINED;
-            return;
+        } else {
+            mStagePosition = mThumbnailPosition.equals(splitInfo.leftTopBounds)
+                    ? STAGE_POSITION_TOP_OR_LEFT : STAGE_POSITION_BOTTOM_OR_RIGHT;
+            mPositionHelper.setSplitBounds(convertLauncherSplitBoundsToShell(mSplitBounds),
+                    mStagePosition);
         }
-        mStagePosition = mThumbnailPosition.equals(splitInfo.leftTopBounds) ?
-                STAGE_POSITION_TOP_OR_LEFT :
-                STAGE_POSITION_BOTTOM_OR_RIGHT;
-        mPositionHelper.setSplitBounds(convertLauncherSplitBoundsToShell(mSplitBounds),
-                mStagePosition);
+        calculateTaskSize();
     }
 
     /**
@@ -261,6 +270,8 @@
     public void setTaskRectTranslation(int taskRectTranslationX, int taskRectTranslationY) {
         mTaskRectTranslationX = taskRectTranslationX;
         mTaskRectTranslationY = taskRectTranslationY;
+        // Re-calculate task size after changing translation
+        calculateTaskSize();
     }
 
     /**
@@ -269,7 +280,7 @@
     public void addAppToOverviewAnim(PendingAnimation pa, TimeInterpolator interpolator) {
         pa.addFloat(fullScreenProgress, AnimatedFloat.VALUE, 1, 0, interpolator);
         if (enableGridOnlyOverview() && mDp.isTablet) {
-            int translationXToMiddle = mDp.widthPx / 2 - mTaskRect.centerX();
+            int translationXToMiddle = mDp.widthPx / 2 - mFullTaskSize.centerX();
             taskPrimaryTranslation.value = translationXToMiddle;
             mPivotOffsetX = translationXToMiddle;
         }
@@ -324,8 +335,8 @@
     public void applyWindowToHomeRotation(Matrix matrix) {
         matrix.postTranslate(mDp.windowX, mDp.windowY);
         postDisplayRotation(deltaRotation(
-                mOrientationState.getRecentsActivityRotation(),
-                mOrientationState.getDisplayRotation()),
+                        mOrientationState.getRecentsActivityRotation(),
+                        mOrientationState.getDisplayRotation()),
                 mDp.widthPx, mDp.heightPx, matrix);
     }
 
diff --git a/quickstep/src/com/android/quickstep/views/DesktopTaskView.java b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
index a10d2ed..6b00473 100644
--- a/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
@@ -17,6 +17,7 @@
 package com.android.quickstep.views;
 
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
 
 import android.content.Context;
@@ -53,6 +54,8 @@
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.wm.shell.Flags;
 
+import kotlin.Unit;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -60,7 +63,6 @@
 import java.util.List;
 import java.util.function.Consumer;
 
-import kotlin.Unit;
 
 /**
  * TaskView that contains all tasks that are part of the desktop.
@@ -91,6 +93,8 @@
 
     private View mBackgroundView;
 
+    private int mChildCountAtInflation;
+
     /** Check whether desktop windowing is enabled */
     public static boolean isDesktopModeSupported() {
         // Check for aconfig flag first
@@ -151,6 +155,8 @@
         Drawable iconBackground = getResources().getDrawable(R.drawable.bg_circle,
                 getContext().getTheme());
         mIconView.setDrawable(new LayerDrawable(new Drawable[]{iconBackground, icon}));
+
+        mChildCountAtInflation = getChildCount();
     }
 
     @Override
@@ -196,7 +202,9 @@
             for (int i = 0; i < diff; i++) {
                 TaskThumbnailView snapshotView = new TaskThumbnailView(getContext());
                 mSnapshotViews.add(snapshotView);
-                addView(snapshotView, new LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
+                // Add snapshots from to position after the initial child views.
+                addView(snapshotView, mChildCountAtInflation,
+                        new LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
             }
         }
 
diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuView.java b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
index cf89d2e..77033b2 100644
--- a/quickstep/src/com/android/quickstep/views/TaskMenuView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
@@ -115,9 +115,6 @@
             animateClose();
         } else {
             closeComplete();
-            if (enableOverviewIconMenu()) {
-                ((IconAppChipView) mTaskContainer.getIconView()).reset();
-            }
         }
     }
 
@@ -378,9 +375,18 @@
 
     private void closeComplete() {
         mIsOpen = false;
+        resetOverviewIconMenu();
         mActivity.getDragLayer().removeView(this);
     }
 
+    private void resetOverviewIconMenu() {
+        if (enableOverviewIconMenu()) {
+            ((IconAppChipView) mTaskContainer.getIconView()).reset();
+            setTranslationY(mMenuTranslationYBeforeOpen);
+            mTaskContainer.getIconView().asView().setTranslationY(mIconViewTranslationYBeforeOpen);
+        }
+    }
+
     private RoundedRectRevealOutlineProvider createOpenCloseOutlineProvider() {
         float radius = TaskCornerRadius.get(mContext);
         Rect fromRect = new Rect(
diff --git a/quickstep/tests/src/com/android/launcher3/model/AppEventProducerTest.java b/quickstep/tests/src/com/android/launcher3/model/AppEventProducerTest.java
index d6c1447..d4dd580 100644
--- a/quickstep/tests/src/com/android/launcher3/model/AppEventProducerTest.java
+++ b/quickstep/tests/src/com/android/launcher3/model/AppEventProducerTest.java
@@ -28,7 +28,6 @@
 
 import android.app.prediction.AppTarget;
 import android.content.ComponentName;
-import android.content.Context;
 import android.content.Intent;
 import android.os.Process;
 import android.os.UserHandle;
@@ -39,13 +38,12 @@
 import com.android.launcher3.logger.LauncherAtom;
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.pm.UserCache;
-import com.android.launcher3.util.ActivityContextWrapper;
+import com.android.launcher3.util.MainThreadInitializedObject.SandboxContext;
 import com.android.launcher3.util.UserIconInfo;
-import com.android.launcher3.util.rule.StaticMockitoRule;
 import com.android.systemui.shared.system.SysUiStatsLog;
 
+import org.junit.After;
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -65,22 +63,24 @@
     private static final UserIconInfo PRIVATE_ICON_INFO =
             new UserIconInfo(PRIVATE_HANDLE, UserIconInfo.TYPE_PRIVATE);
 
-    private Context mContext;
+    private SandboxContext mContext;
     private AppEventProducer mAppEventProducer;
     @Mock
     private UserCache mUserCache;
 
-    @Rule
-    public final StaticMockitoRule mStaticMockitoRule = new StaticMockitoRule(UserCache.class);
-
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mContext = new ActivityContextWrapper(getApplicationContext());
-        when(UserCache.getInstance(any(Context.class))).thenReturn(mUserCache);
+        mContext = new SandboxContext(getApplicationContext());
+        mContext.putObject(UserCache.INSTANCE, mUserCache);
         mAppEventProducer = new AppEventProducer(mContext, null);
     }
 
+    @After
+    public void tearDown() {
+        mContext.onDestroy();
+    }
+
     @Test
     public void buildAppTarget_containsCorrectUser() {
         when(mUserCache.getUserProfiles())
diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
index 0a325ac..39d6f03 100644
--- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
+++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
@@ -77,6 +77,7 @@
 import org.junit.runners.model.Statement;
 
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Consumer;
 import java.util.function.Function;
@@ -193,6 +194,7 @@
     @Test
     public void goToOverviewFromApp() {
         startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
+        waitForRecentsActivityStop();
 
         mLauncher.getLaunchedAppState().switchToOverview();
     }
@@ -229,6 +231,19 @@
     }
 
     private void waitForRecentsActivityStop() {
+        try {
+            final boolean recentsActivityIsNull = MAIN_EXECUTOR.submit(
+                    () -> RecentsActivity.ACTIVITY_TRACKER.getCreatedActivity() == null).get();
+            if (recentsActivityIsNull) {
+                // Null activity counts as a "stopped" one.
+                return;
+            }
+        } catch (ExecutionException e) {
+            throw new RuntimeException(e);
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        }
+
         Wait.atMost("Recents activity didn't stop",
                 () -> getFromRecents(recents -> !recents.isStarted()),
                 DEFAULT_UI_TIMEOUT, mLauncher);
@@ -241,6 +256,7 @@
         startAppFast(getAppPackageName());
         startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
         startTestActivity(2);
+        waitForRecentsActivityStop();
         Wait.atMost("Expected three apps in the task list",
                 () -> mLauncher.getRecentTasks().size() >= 3, DEFAULT_ACTIVITY_TIMEOUT, mLauncher);
 
@@ -282,7 +298,6 @@
 
         // Test dismissing all tasks.
         pressHomeAndGoToOverview().dismissAllTasks();
-        waitForRecentsActivityStop(); // dismissAllTasks() will close Recents
         assertTrue("Fallback Launcher not visible", TestHelpers.wait(Until.hasObject(By.pkg(
                 mOtherLauncherActivity.packageName).text(FALLBACK_LAUNCHER_TITLE)), WAIT_TIME_MS));
     }
diff --git a/quickstep/tests/src/com/android/quickstep/TaplDigitalWellBeingToastTest.java b/quickstep/tests/src/com/android/quickstep/TaplDigitalWellBeingToastTest.java
index 728fe67..4aa7cb0 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplDigitalWellBeingToastTest.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplDigitalWellBeingToastTest.java
@@ -17,8 +17,6 @@
 
 import static androidx.test.InstrumentationRegistry.getInstrumentation;
 
-import static com.android.launcher3.LauncherState.OVERVIEW;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -84,8 +82,7 @@
     }
 
     private DigitalWellBeingToast getToast() {
-        executeOnLauncher(launcher -> launcher.getStateManager().goToState(OVERVIEW));
-        waitForState("Launcher internal state didn't switch to Overview", () -> OVERVIEW);
+        mLauncher.getWorkspace().switchToOverview();
         final TaskView task = getOnceNotNull("No latest task", launcher -> getLatestTask(launcher));
 
         return getFromLauncher(launcher -> {
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsPersistentTaskbar.java b/quickstep/tests/src/com/android/quickstep/TaplTestsPersistentTaskbar.java
index c9e536a..a71d74a 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsPersistentTaskbar.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsPersistentTaskbar.java
@@ -15,18 +15,28 @@
  */
 package com.android.quickstep;
 
+import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL;
+import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT;
 import static com.android.quickstep.TaskbarModeSwitchRule.Mode.PERSISTENT;
 
+import android.graphics.Rect;
+
 import androidx.test.filters.LargeTest;
+import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
+import com.android.launcher3.util.rule.TestStabilityRule;
 import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
 import com.android.quickstep.TaskbarModeSwitchRule.TaskbarModeSwitch;
 
+import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.io.Closeable;
+import java.io.IOException;
+
 @LargeTest
 @RunWith(AndroidJUnit4.class)
 public class TaplTestsPersistentTaskbar extends AbstractTaplTestsTaskbar {
@@ -39,4 +49,29 @@
         // Width check is performed inside TAPL whenever getTaskbar() is called.
         getTaskbar();
     }
+
+    @Test
+    @TestStabilityRule.Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/320490387
+    @NavigationModeSwitch(mode = NavigationModeSwitchRule.Mode.THREE_BUTTON)
+    public void testThreeButtonsTaskbarBoundsAfterConfigChangeDuringIme() {
+        // Start off in light mode.
+        try (Closeable c = InstrumentationRegistry.getInstrumentation().getUiAutomation()
+                .executeShellCommand("cmd uimode night no")) {
+            Rect taskbarBoundsBefore = getTaskbar().getVisibleBounds();
+            startImeTestActivity();
+            // IME should stash the taskbar, which hides icons even in 3 button mode.
+            mLauncher.getLaunchedAppState().assertTaskbarHidden();
+            // Switch to dark mode (any configuration change here would do).
+            InstrumentationRegistry.getInstrumentation().getUiAutomation().executeShellCommand(
+                    "cmd uimode night yes").close();
+            // Close IME to check new taskbar bounds.
+            mLauncher.pressBack();
+            Rect taskbarBoundsAfter = getTaskbar().getVisibleBounds();
+            Assert.assertEquals(
+                    "Taskbar bounds are not the same after a configuration change while stashed.",
+                    taskbarBoundsBefore, taskbarBoundsAfter);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
 }
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsTransientTaskbar.java b/quickstep/tests/src/com/android/quickstep/TaplTestsTransientTaskbar.java
index 7109bbf..38d6046 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsTransientTaskbar.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsTransientTaskbar.java
@@ -25,6 +25,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
+import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
 import com.android.quickstep.TaskbarModeSwitchRule.TaskbarModeSwitch;
 
 import org.junit.Test;
@@ -69,6 +70,7 @@
     @Test
     @TaskbarModeSwitch(mode = TRANSIENT)
     @PortraitLandscape
+    @ScreenRecord // b/317798731
     public void testSwipeToStashAndUnstash() {
         getTaskbar().swipeDownToStash();
         mLauncher.getLaunchedAppState().swipeUpToUnstashTaskbar();
diff --git a/res/drawable/ic_install_to_private.xml b/res/drawable/ic_install_to_private.xml
new file mode 100644
index 0000000..7f00f8d
--- /dev/null
+++ b/res/drawable/ic_install_to_private.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="960"
+    android:viewportHeight="960"
+    android:tint="?attr/colorControlNormal">
+
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M420,600L540,600L517,471Q537,461 548.5,442Q560,423 560,400Q560,367 536.5,343.5Q513,320 480,320Q447,320 423.5,343.5Q400,367 400,400Q400,423 411.5,442Q423,461 443,471L420,600ZM480,880Q341,845 250.5,720.5Q160,596 160,444L160,200L480,80L800,200L800,444Q800,596 709.5,720.5Q619,845 480,880ZM480,796Q584,763 652,664Q720,565 720,444L720,255L480,165L240,255L240,444Q240,565 308,664Q376,763 480,796ZM480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480Z"/>
+</vector>
diff --git a/res/drawable/private_space_install_app_icon.xml b/res/drawable/private_space_install_app_icon.xml
new file mode 100644
index 0000000..4c167ba
--- /dev/null
+++ b/res/drawable/private_space_install_app_icon.xml
@@ -0,0 +1,31 @@
+<!--
+  ~ Copyright (C) 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="60dp"
+    android:height="60dp"
+    android:viewportWidth="60"
+    android:viewportHeight="60">
+    <group>
+        <clip-path
+            android:pathData="M30 0H30A30 30 0 0 1 60 30V30A30 30 0 0 1 30 60H30A30 30 0 0 1 0 30V30A30 30 0 0 1 30 0Z" />
+        <path
+            android:pathData="M30 0H30A30 30 0 0 1 60 30V30A30 30 0 0 1 30 60H30A30 30 0 0 1 0 30V30A30 30 0 0 1 30 0Z"
+            android:fillColor="@color/material_color_surface_bright" />
+        <path
+            android:pathData="M29 31h-6v-2h6v-6h2v6h6v2h-6v6h-2v-6Z"
+            android:fillColor="@color/material_color_on_surface_variant" />
+    </group>
+</vector>
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index ea6e0fa..44c29ec 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Privaat Ruimte-instellings"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Sluit/ontsluit Privaat Ruimte"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Privaat Ruimte-oorgang"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Oorvloei"</string>
 </resources>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index 893f4c4..b56c79a 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"የግል ቦታ ቅንብሮች"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"የግል ቦታን ቆልፍ/ክፈት"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"የግል ቦታ ሽግግር"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"ትርፍ ፍሰት"</string>
 </resources>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index a2a43db..1394661 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"إعدادات المساحة الخاصة"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"قفل المساحة الخاصة أو فتح قفلها"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"النقل إلى المساحة الخاصة"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"القائمة الكاملة"</string>
 </resources>
diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml
index 93c5e3a..9deda79 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"ব্যক্তিগত স্পে’চৰ ছেটিং"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"ব্যক্তিগত স্পে’চ লক/আনলক কৰক"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"ব্যক্তিগত স্পে’চৰ স্থানান্তৰণ"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"অ’ভাৰফ্ল’"</string>
 </resources>
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index 31d485d..971163e 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Şəxsi məkan ayarları"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Şəxsi məkanı kilidləyin/kiliddən çıxarın"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Şəxsi məkana keçid"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Kənara çıxma"</string>
 </resources>
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index aa08cc4..c13989c 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Podešavanja privatnog prostora"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Zaključaj/otključaj privatni prostor"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Prenos privatnog prostora"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Preklopno"</string>
 </resources>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index ae47fa0..31709c0 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Налады прыватнай вобласці"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Заблакіраваць (разблакіраваць) прыватную вобласць"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Пераход у прыватную вобласць"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Дадатковае меню"</string>
 </resources>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index 2f196ca..01ee2df 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Настройки за личното пространство"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Заключване/отключване на личното пространство"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Преминаване към личното пространство"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Препълване"</string>
 </resources>
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
index 8c08794..665a95f 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"ব্যক্তিগত স্পেসের সেটিংস"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"ব্যক্তিগত স্পেস লক/আনলক করুন"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"ব্যক্তিগত স্পেস ট্রানজিট করা"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"ওভারফ্লো"</string>
 </resources>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index 9641416..f0ee1b4 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Postavke privatnog prostora"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Zaključavanje/otključavanje privatnog prostora"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Prelazak u privatan prostor"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Preklopni meni"</string>
 </resources>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 25a8ce6..e1c9a2b 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Configuració d\'Espai privat"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Bloqueja o desbloqueja Espai privat"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Canvia a Espai privat"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Menú addicional"</string>
 </resources>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 4927508..7a5e838 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Nastavení soukromého prostoru"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Zamknout/odemknout soukromý prostor"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Převádění soukromého prostoru"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Rozbalovací nabídka"</string>
 </resources>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 415d1f3..9c46dd9 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Indstillinger for privat rum"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Lås/oplås det private område"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Ændringer af tilstanden for det private område"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Overløb"</string>
 </resources>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 04d9682..f27cb90 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Einstellungen für privaten Bereich"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Privaten Bereich sperren/entsperren"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Sperrzustand des privaten Bereichs wird gerade geändert"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Weitere Optionen"</string>
 </resources>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index bc8458b..d4740d4 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Ρυθμίσεις Ιδιωτικού χώρου"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Κλείδωμα/Ξεκλείδωμα Ιδιωτικού χώρου"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Μετάβαση στον Ιδιωτικό χώρο"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Υπερχείλιση"</string>
 </resources>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index c002ef9..85ab76b 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Private Space Settings"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Lock/Unlock Private Space"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Private Space transitioning"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Overflow"</string>
 </resources>
diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml
index 1ad42f3..bb03769 100644
--- a/res/values-en-rCA/strings.xml
+++ b/res/values-en-rCA/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Private Space Settings"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Lock/Unlock Private Space"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Private Space Transitioning"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Overflow"</string>
 </resources>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index c002ef9..85ab76b 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Private Space Settings"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Lock/Unlock Private Space"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Private Space transitioning"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Overflow"</string>
 </resources>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index c002ef9..85ab76b 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Private Space Settings"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Lock/Unlock Private Space"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Private Space transitioning"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Overflow"</string>
 </resources>
diff --git a/res/values-en-rXC/strings.xml b/res/values-en-rXC/strings.xml
index 81bb474..ef85c5c 100644
--- a/res/values-en-rXC/strings.xml
+++ b/res/values-en-rXC/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‎‎‏‏‎‎‎‏‎‎‎‎‎‎‎‎‎‎‏‎‏‎‎‎‏‎‎‏‏‏‏‏‏‎‎‏‎‎‏‏‎‏‎‏‏‎‎‎‎‎‏‏‏‏‏‎Private Space Settings‎‏‎‎‏‎"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‎‎‏‏‎‎‏‎‎‎‎‏‏‏‎‎‎‎‎‏‏‎‎‏‏‎‎‏‎‎‏‏‎‏‏‎‎‏‎‎‎‎‏‎‎‎‏‎‎‎‏‎‏‏‎Lock/Unlock Private Space‎‏‎‎‏‎"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‏‎‎‏‎‎‎‏‎‎‎‏‏‎‎‎‏‎‏‏‏‎‎‏‎‎‎‏‏‏‏‏‎‎‏‎‎‎‎‏‎‎‎‏‏‎‎‎‏‎‎‏‏‎‎‎Private Space Transitioning‎‏‎‎‏‎"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‎‏‏‎‏‏‎‎‏‎‎‏‎‎‏‎‏‎‎‏‎‏‎‏‏‏‏‎‏‎‎‎‏‎‎‏‎‏‏‎‎‏‎‎‎‏‏‎‏‎‏‏‎‏‎‎‎‎Overflow‎‏‎‎‏‎"</string>
 </resources>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index d37b1e9..bce0430 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Configuración de Espacio privado"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Bloquear o desbloquear Espacio privado"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Pasar a Espacio privado"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Ampliada"</string>
 </resources>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 7db8239..6bb325c 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Ajustes del espacio privado"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Bloquear/Desbloquear espacio privado"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Cambiar a espacio privado"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Desplegable"</string>
 </resources>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index 9e4a534..cabd7ab 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Privaatse ruumi seaded"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Privaatse ruumi lukustamine/avamine"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Privaatse ruumi üleviimine"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Ületäide"</string>
 </resources>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index 4123096..ff68d74 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Eremu pribatuaren ezarpenak"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Blokeatu/Desblokeatu eremu pribatua"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Eremu pribaturako trantsizioa"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Luzapena"</string>
 </resources>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 161c7c5..444d550 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"تنظیمات «فضای خصوصی»"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"قفل/ باز کردن «فضای خصوصی»"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"انتقال «فضای خصوصی»"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"سرریز"</string>
 </resources>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index 87e3040..d812e99 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Yksityisen tilan asetukset"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Lukitse yksityinen tila / avaa sen lukitus"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Yksityisen tilan siirtäminen"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Ylivuoto"</string>
 </resources>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index 0e8fe9c..0fc6129 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Paramètres de l\'Espace privé"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Verrouiller/Déverrouiller l\'Espace privé"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Transition vers l\'Espace privé"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Menu à développer"</string>
 </resources>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 9aa6ad2..fb658f5 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Paramètres d\'Espace privé"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Verrouiller/Déverrouiller Espace privé"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Transition vers Espace privé"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Dépassement"</string>
 </resources>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index 4eda3cf..4a455f5 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Configuración do espazo privado"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Bloquear ou desbloquear o espazo privado"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Transición ao espazo privado"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Menú adicional"</string>
 </resources>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index deaee86..be0fdba 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"ખાનગી સ્પેસના સેટિંગ"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"ખાનગી સ્પેસને લૉક/અનલૉક કરો"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"ખાનગી સ્પેસ પર સ્થાનાંતરણ"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"ઓવરફ્લો"</string>
 </resources>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 52bc50a..f7972eb 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"प्राइवेट स्पेस सेटिंग"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"प्राइवेट स्पेस को लॉक करें/अनलॉक करें"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"प्राइवेट स्पेस की सेटिंग में बदलाव किया जा रहा है"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"ओवरफ़्लो"</string>
 </resources>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index 9694428..9f6ae3a 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Postavke privatnog prostora"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Zaključavanje/otključavanje privatnog prostora"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Prelazak na privatni prostor"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Dodatni izbornik"</string>
 </resources>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index 7c83ec0..8e0fa14 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Privát terület beállításai"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Privát terület zárolása/zárolásának feloldása"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Átállás privát területre…"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Túlcsordulás"</string>
 </resources>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index 7aa432b..c909b54 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Անձնական տարածքի կարգավորումներ"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Կողպել/ապակողպել անձնական տարածքը"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Անցում անձնական տարածք"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Լրացուցիչ ընտրացանկ"</string>
 </resources>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index b7ce5e3..d7bb8dc 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Setelan Ruang Pribadi"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Kunci/Buka Kunci Ruang Pribadi"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Ruang Pribadi Bertransisi"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Menu tambahan"</string>
 </resources>
diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml
index 4ba0623..1c9759f 100644
--- a/res/values-is/strings.xml
+++ b/res/values-is/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Stillingar einkarýmis"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Læsaeinkarými/taka einkarými úr lás"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Einkarými að breytast"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Yfirflæði"</string>
 </resources>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 644d680..fd7c4bf 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Impostazioni dello Spazio privato"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Blocca/sblocca Spazio privato"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Transizione dello Spazio privato in corso…"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Extra"</string>
 </resources>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index c91e57c..79a6e88 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"הגדרות המרחב הפרטי"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"נעילה או ביטול הנעילה של המרחב הפרטי"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"מעבר למרחב הפרטי"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"אפשרויות נוספות"</string>
 </resources>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 00b061f..ac63dab 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"プライベート スペースの設定"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"プライベート スペースをロック / ロック解除する"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"プライベート スペース移行中"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"オーバーフロー"</string>
 </resources>
diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml
index d428447..eeb33fb 100644
--- a/res/values-ka/strings.xml
+++ b/res/values-ka/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"პირადი სივრცის პარამეტრები"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"პირადი სივრცის ჩაკეტვა/განბლოკვა"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"პირად სივრცეზე გადასვლა"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"გადავსება"</string>
 </resources>
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
index 4f6c6fd..85e7b6c 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Жеке бөлме параметрлері"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Жеке бөлмені құлыптау/оның құлпын ашу"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Жеке бөлмеге өту"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Қосымша мәзір"</string>
 </resources>
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
index 91622f0..49c2b35 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"ការកំណត់ Private Space"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"ចាក់សោ/ដោះសោ Private Space"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"ការផ្លាស់ប្ដូរ Private Space"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"ម៉ឺនុយបន្ថែម"</string>
 </resources>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index ad208f2..21e226c 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"ಖಾಸಗಿ ಸ್ಪೇಸ್ ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"ಖಾಸಗಿ ಸ್ಪೇಸ್ ಅನ್ನು ಲಾಕ್/ಅನ್‌ಲಾಕ್ ಮಾಡಿ"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"ಖಾಸಗಿ ಸ್ಪೇಸ್ ಪರಿವರ್ತನೆಯಾಗುತ್ತಿದೆ"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"ಓವರ್‌ಫ್ಲೋ"</string>
 </resources>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index b2b0c0c..643a513 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"비공개 스페이스 설정"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"비공개 스페이스 잠금/잠금 해제"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"비공개 스페이스 전환"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"오버플로"</string>
 </resources>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index 78e96a6..2274389 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Жеке чөйрөнүн параметрлери"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Жеке чөйрөнү кулпулоо/кулпусун ачуу"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Жеке чөйрөгө өтүү"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Кошумча меню"</string>
 </resources>
diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml
index bb99150..175e8f5 100644
--- a/res/values-lo/strings.xml
+++ b/res/values-lo/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"ການຕັ້ງຄ່າພື້ນທີ່ສ່ວນຕົວ"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"ລັອກ/ປົດລັອກພື້ນທີ່ສ່ວນຕົວ"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"ການປ່ຽນແປງພື້ນທີ່ສ່ວນຕົວ"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"ການດຳເນີນການເພີ່ມເຕີມ"</string>
 </resources>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index 075bbb2..ef56c1a 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Privačios erdvės nustatymai"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Užrakinti ir (arba) atrakinti privačią erdvę"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Privačios erdvės perkėlimas"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Perpildymas"</string>
 </resources>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index 5edee25..f2e8a87 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Privātās mapes iestatījumi"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Bloķēt/atbloķēt privāto mapi"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Pāriet uz privāto mapi"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Pārpilde"</string>
 </resources>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index a95b7bf..cfbd3ba 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Поставки за „Приватен простор“"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Заклучување/отклучување на „Приватен простор“"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Префрлање на „Приватен простор“"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Проширено балонче"</string>
 </resources>
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
index 1cdcb09..2ae3c2f 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"സ്വകാര്യ സ്‌പേസ് ക്രമീകരണം"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"സ്വകാര്യ സ്‌പേസ് ലോക്ക് ചെയ്യുക/അൺലോക്ക് ചെയ്യുക"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"പ്രൈവറ്റ് സ്‌പേസ് ട്രാൻസിഷനിംഗ്"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"ഓവർഫ്ലോ"</string>
 </resources>
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
index 2d53dea..31dda29 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Private Space-н тохиргоо"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Private Space-г түгжих/түгжээг тайлах"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Private Space-н шилжилт"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Урт цэс"</string>
 </resources>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index 9e44733..635a422 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"खाजगी स्पेस ची सेटिंग्ज"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"खाजगी स्पेस लॉक/अनलॉक करा"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"खाजगी स्पेस वर स्विच करणे"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"ओव्हरफ्लो"</string>
 </resources>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index c90082a..566ea85 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Tetapan Ruang Peribadi"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Kunci/Buka kunci Ruang Peribadi"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Peralihan Ruang Peribadi"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Limpahan"</string>
 </resources>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index d1bef97..ced9f61 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"သီးသန့်ချတ်ခန်း ဆက်တင်များ"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"သီးသန့်ချတ်ခန်း လော့ခ်ချ/ဖွင့်ရန်"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"သီးသန့်ချတ်ခန်း အပြောင်းအလဲ"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"မီနူးအပို"</string>
 </resources>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index cd2fb1f..cb0fd95 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Innstillinger for Private Space"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Lås / lås opp Private Space"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Private Space-overgang"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Overflyt"</string>
 </resources>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index b9397a0..d8df060 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"निजी स्पेससम्बन्धी सेटिङ"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"निजी स्पेस लक/अनलक गर्नुहोस्"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"निजी स्पेस ट्रान्जिसन गरिँदै छ"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"ओभरफ्लो"</string>
 </resources>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 84d5c15..86a5f40 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Instellingen voor privéruimte"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Privéruimte vergrendelen/ontgrendelen"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Overschakelen naar privéruimte"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Overloop"</string>
 </resources>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index 3fa8754..c726e50 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"ପ୍ରାଇଭେଟ ସ୍ପେସ ସେଟିଂସ"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"ପ୍ରାଇଭେଟ ସ୍ପେସକୁ ଲକ/ଅନଲକ କରନ୍ତୁ"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"ପ୍ରାଇଭେଟ ସ୍ପେସ ଟ୍ରାଞ୍ଜିସନିଂ"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"ଓଭରଫ୍ଲୋ"</string>
 </resources>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index 98bc36d..46e750a 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"ਨਿੱਜੀ ਸਪੇਸ ਸੰਬੰਧੀ ਸੈਟਿੰਗਾਂ"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"ਨਿੱਜੀ ਸਪੇਸ ਨੂੰ ਲਾਕ/ਅਣਲਾਕ ਕਰੋ"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"ਨਿੱਜੀ ਸਪੇਸ ਨੂੰ ਤਬਦੀਲ ਕਰਨਾ"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"ਓਵਰਫ਼ਲੋ"</string>
 </resources>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 30a953c..4b6a612 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Ustawienia obszaru prywatnego"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Zablokuj/odblokuj obszar prywatny"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Przenoszenie obszaru prywatnego"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Rozwiń menu"</string>
 </resources>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index af760f8..4803138 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Definições do espaço privado"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Bloquear/desbloquear espaço privado"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Transição do espaço privado"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Menu adicional"</string>
 </resources>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 4359e9a..40af95d 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Configurações do Espaço particular"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Bloquear/desbloquear o Espaço particular"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Espaço particular em transição"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Balão flutuante"</string>
 </resources>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 836ac99..2d66f9b 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Setări spațiu privat"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Blochează / deblochează spațiul privat"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Tranziție pentru spațiul privat"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Suplimentar"</string>
 </resources>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index bb7b0a0..063126d 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Настройки личного пространства"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Блокировка и разблокировка личного пространства"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Переход к личному пространству"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Дополнительное меню"</string>
 </resources>
diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml
index 401e0fb..e5660ce 100644
--- a/res/values-si/strings.xml
+++ b/res/values-si/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"පෞද්ගලික අවකාශ සැකසීම්"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"පෞද්ගලික අවකාශය අගුළු දමන්න/අගුළු හරින්න"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"පෞද්ගලික අවකාශ සංක්‍රමණය"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"පිටාර යාම"</string>
 </resources>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index fbe64a8..89c64bf 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Nastavenia súkromného priestoru"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Súkromný priestor zamykania a odomykania"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Prechod súkromného priestoru"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Rozšírená ponuka"</string>
 </resources>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index d91e83f..5f18f26 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Nastavitve zasebnega prostora"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Zaklepanje/odklepanje zasebnega prostora"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Preklapljanje zasebnega prostora"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Oblaček z dodatnimi elementi"</string>
 </resources>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index a3bd673..c06fc7e 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Cilësimet e \"Hapësirës private\""</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Kyç/Shkyç \"Hapësirën private\""</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Kalimi te \"Hapësira private\""</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Tejkalimi"</string>
 </resources>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index 5242848..eb3bc64 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Подешавања приватног простора"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Закључај/откључај приватни простор"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Пренос приватног простора"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Преклопно"</string>
 </resources>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 1e1436f..6824cc1 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Inställningar för privat rum"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Lås eller lås upp ditt privata rum"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Överföring av privat rum"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Fler alternativ"</string>
 </resources>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 2486cae..788a06a 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Mipangilio ya Nafasi ya Faragha"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Funga/Fungua Nafasi ya Faragha"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Mabadiliko ya Nafasi ya Faragha"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Menyu ya vipengee vya ziada"</string>
 </resources>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index cb0ca67..6c89de9 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"தனிப்பட்ட சேமிப்பிட அமைப்புகள்"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"தனிப்பட்ட சேமிப்பிடத்தை லாக்/அன்லாக் செய்யும்"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"தனிப்பட்ட சேமிப்பிடத்திற்கு மாற்றுகிறது"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"கூடுதல் விருப்பங்களைக் காட்டும்"</string>
 </resources>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index 27cec72..e58039f 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"ప్రైవేట్ స్పేస్ సెట్టింగ్‌లు"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"ప్రైవేట్ స్పేస్‌ను లాక్/అన్‌లాక్ చేయండి"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"ప్రైవేట్ స్పేస్ కేటాయించడం జరుగుతుంది"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"ఓవర్‌ఫ్లో"</string>
 </resources>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 78cbcfa..0eff887 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"การตั้งค่าพื้นที่ส่วนตัว"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"ล็อก/ปลดล็อกพื้นที่ส่วนตัว"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"การเปลี่ยนไปใช้พื้นที่ส่วนตัว"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"การดำเนินการเพิ่มเติม"</string>
 </resources>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index acf60ff..3db2aba 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Mga Setting ng Pribadong Space"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"I-lock/I-unlock ang Pribadong Space"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Pag-transition ng Pribadong Space"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Overflow"</string>
 </resources>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index 1449202..78fc596 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Gizli Alan Ayarları"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Gizli Alanı Kilitleyin/Kilidini Açın"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Gizli Alana Geçiş"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Taşma"</string>
 </resources>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index ad325cc..b69558f 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Налаштування приватного простору"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Заблокувати/розблокувати приватний простір"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Перехід у приватний простір"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Додаткове меню"</string>
 </resources>
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
index 1c0e3ae..549f2a1 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"نجی اسپیس کی ترتیبات"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"نجی اسپیس کو مقفل کریں/غیر مقفل کریں"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"نجی اسپیس کی منتقلی"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"اوورفلو"</string>
 </resources>
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
index 8e6d392..7cacd96 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Shaxsiy xona sozlamalari"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Shaxsiy xonani ochish/qulflash"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Maxfiy joyga almashtirish"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Kengaytirish"</string>
 </resources>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index 8ed3089..7f4487c 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Cài đặt không gian riêng tư"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Khoá/mở khoá không gian riêng tư"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Chuyển đổi sang không gian riêng tư"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Bong bóng bổ sung"</string>
 </resources>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 53b85f2..cfae584 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"私密空间设置"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"锁定/解锁私密空间"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"私密空间转换"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"菜单"</string>
 </resources>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index e6aaef5..e94a808 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"「私人空間」設定"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"鎖定/解鎖「私人空間」"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"轉為「私人空間」"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"顯示更多"</string>
 </resources>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 462b651..957bf45 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"私人空間設定"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"鎖定/取消鎖定私人空間"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"轉換私人空間狀態"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"溢位"</string>
 </resources>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index efc0824..3051d09 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -178,5 +178,9 @@
     <string name="ps_container_settings" msgid="6059734123353320479">"Amasethingi Esikhala Esiyimfihlo"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Khiya/Vula Isikhala Esiyimfihlo"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Ukuguqulwa Kwendawo Yangasese"</string>
+    <!-- no translation found for ps_add_button_label (8611055839242385935) -->
+    <skip />
+    <!-- no translation found for ps_add_button_content_description (3254274107740952556) -->
+    <skip />
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Ukugcwala kakhulu"</string>
 </resources>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 8d84c90..e54539f 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -189,6 +189,10 @@
          defaults to 2 * numAllAppsColumns -->
         <attr name="numExtendedAllAppsColumns" format="integer" />
 
+        <!-- Number of rows to calculate the cell height for all apps when it's necessary.
+          Defaults to numRows. Requires FeatureFlags.ENABLE_RESPONSIVE_WORKSPACE to be enabled. -->
+        <attr name="numAllAppsRowsForCellHeightCalculation" format="integer" />
+
         <!-- numHotseatIcons defaults to numColumns, if not specified -->
         <attr name="numHotseatIcons" format="integer" />
         <!-- Number of icons to use when extending the hotseat size,
@@ -219,26 +223,32 @@
         <!-- File that contains the specs for the workspace.
         Needs FeatureFlags.ENABLE_RESPONSIVE_WORKSPACE enabled -->
         <attr name="workspaceSpecsId" format="reference" />
+        <!-- defaults to workspaceSpecsId, if not specified -->
         <attr name="workspaceSpecsTwoPanelId" format="reference" />
         <!-- File that contains the specs for all apps.
         Needs FeatureFlags.ENABLE_RESPONSIVE_WORKSPACE enabled -->
         <attr name="allAppsSpecsId" format="reference" />
+        <!-- defaults to allAppsSpecsId, if not specified -->
         <attr name="allAppsSpecsTwoPanelId" format="reference" />
         <!-- File that contains the specs for the workspace.
         Needs FeatureFlags.ENABLE_RESPONSIVE_WORKSPACE enabled -->
         <attr name="folderSpecsId" format="reference" />
+        <!-- defaults to folderSpecsId, if not specified -->
         <attr name="folderSpecsTwoPanelId" format="reference" />
         <!-- File that contains the specs for hotseat bar.
         Needs FeatureFlags.ENABLE_RESPONSIVE_WORKSPACE enabled -->
         <attr name="hotseatSpecsId" format="reference" />
+        <!-- defaults to hotseatSpecsId, if not specified -->
         <attr name="hotseatSpecsTwoPanelId" format="reference" />
         <!-- File that contains the specs for workspace icon and text size.
         Needs FeatureFlags.ENABLE_RESPONSIVE_WORKSPACE enabled -->
         <attr name="workspaceCellSpecsId" format="reference" />
+        <!-- defaults to workspaceCellSpecsId, if not specified -->
         <attr name="workspaceCellSpecsTwoPanelId" format="reference" />
         <!-- File that contains the specs for all apps icon and text size.
         Needs FeatureFlags.ENABLE_RESPONSIVE_WORKSPACE enabled -->
         <attr name="allAppsCellSpecsId" format="reference" />
+        <!-- defaults to allAppsCellSpecsId, if not specified -->
         <attr name="allAppsCellSpecsTwoPanelId" format="reference" />
 
         <!-- By default all categories are enabled -->
diff --git a/res/values/config.xml b/res/values/config.xml
index 2980635..33eb4c7 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -201,7 +201,6 @@
 
     <!-- Swipe back to home related -->
     <dimen name="swipe_back_window_scale_x_margin">10dp</dimen>
-    <dimen name="swipe_back_window_max_delta_y">160dp</dimen>
     <dimen name="swipe_back_window_corner_radius">40dp</dimen>
 
     <!-- The duration of the bottom sheet opening and closing animation -->
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 6d115b2..603e697 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -103,6 +103,7 @@
     <dimen name="all_apps_search_bar_content_overlap">24dp</dimen>
     <dimen name="all_apps_search_bar_bottom_padding">30dp</dimen>
     <dimen name="all_apps_empty_search_message_top_offset">40dp</dimen>
+    <dimen name="all_apps_search_top_row_extra_height">4dp</dimen>
     <dimen name="all_apps_header_pill_height">48dp</dimen>
     <dimen name="all_apps_header_pill_corner_radius">12dp</dimen>
     <dimen name="all_apps_header_tab_height">48dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 6a4a9a4..34677f6 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -171,6 +171,8 @@
     <string name="uninstall_drop_target_label">Uninstall</string>
     <!-- Label for app info drop target. [CHAR_LIMIT=20] -->
     <string name="app_info_drop_target_label">App info</string>
+    <!-- Label for install to private profile shortcut label. [CHAR_LIMIT=20] -->
+    <string name="install_private_system_shortcut_label">Install in private</string>
     <!-- Label for install drop target. [CHAR_LIMIT=20] -->
     <string name="install_drop_target_label">Install</string>
     <!-- Label for install dismiss prediction. -->
@@ -461,6 +463,10 @@
     <string name="ps_container_lock_unlock_button">Lock/Unlock Private Space</string>
     <!-- Description for Private Space Transition button -->
     <string name="ps_container_transition">Private Space Transitioning</string>
+    <!-- Title for Private Space install app icon -->
+    <string name="ps_add_button_label">Install apps</string>
+    <!-- Content description for install app icon -->
+    <string name="ps_add_button_content_description">Install apps to Private Space</string>
 
     <!-- Strings for bubble bar -->
     <!-- content description for the overflow bubble [CHAR_LIMIT=none] -->
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
index f8ed4df..b6f6615 100644
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -39,7 +39,6 @@
 import com.android.launcher3.util.DisplayController.Info;
 import com.android.launcher3.util.OnColorHintListener;
 import com.android.launcher3.util.Themes;
-import com.android.launcher3.util.TraceHelper;
 import com.android.launcher3.util.WallpaperColorHints;
 import com.android.launcher3.util.WindowBounds;
 
@@ -55,16 +54,12 @@
     public static final Object AUTO_CANCEL_ACTION_MODE = new Object();
 
     private ActionMode mCurrentActionMode;
-    protected boolean mIsSafeModeEnabled;
 
     private int mThemeRes = R.style.AppTheme;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-
-        mIsSafeModeEnabled = TraceHelper.allowIpcs("isSafeMode",
-                () -> getPackageManager().isSafeMode());
         DisplayController.INSTANCE.get(this).addChangeListener(this);
 
         // Update theme
@@ -183,6 +178,6 @@
 
     @Override
     public boolean isAppBlockedForSafeMode() {
-        return mIsSafeModeEnabled;
+        return LauncherAppState.getInstance(this).isSafeModeEnabled();
     }
 }
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index a57eaa3..5b497f2 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -22,6 +22,7 @@
 import static com.android.launcher3.config.FeatureFlags.ENABLE_ICON_LABEL_AUTO_SCALING;
 import static com.android.launcher3.graphics.PreloadIconDrawable.newPendingIcon;
 import static com.android.launcher3.icons.BitmapInfo.FLAG_NO_BADGE;
+import static com.android.launcher3.icons.BitmapInfo.FLAG_SKIP_USER_BADGE;
 import static com.android.launcher3.icons.BitmapInfo.FLAG_THEMED;
 import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
 import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_INCREMENTAL_DOWNLOAD_ACTIVE;
@@ -164,6 +165,8 @@
     @ViewDebug.ExportedProperty(category = "launcher")
     private boolean mHideBadge = false;
     @ViewDebug.ExportedProperty(category = "launcher")
+    private boolean mSkipUserBadge = false;
+    @ViewDebug.ExportedProperty(category = "launcher")
     private boolean mIsIconVisible = true;
     @ViewDebug.ExportedProperty(category = "launcher")
     private int mTextColor;
@@ -268,6 +271,10 @@
         mHideBadge = hideBadge;
     }
 
+    public void setSkipUserBadge(boolean skipUserBadge) {
+        mSkipUserBadge = skipUserBadge;
+    }
+
     /**
      * Resets the view so it can be recycled.
      */
@@ -397,6 +404,9 @@
         if (mHideBadge || mDisplay == DISPLAY_SEARCH_RESULT_SMALL) {
             flags |= FLAG_NO_BADGE;
         }
+        if (mSkipUserBadge) {
+            flags |= FLAG_SKIP_USER_BADGE;
+        }
         FastBitmapDrawable iconDrawable = info.newIcon(getContext(), flags);
         mDotParams.appColor = iconDrawable.getIconColor();
         mDotParams.dotColor = Themes.getAttrColor(getContext(), R.attr.notificationDotColor);
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 72d2213..834ba04 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -640,8 +640,8 @@
                     DimensionType.WIDTH, numShownAllAppsColumns, availableWidthPx,
                     mResponsiveWorkspaceWidthSpec);
             mResponsiveAllAppsHeightSpec = allAppsSpecs.getCalculatedSpec(responsiveAspectRatio,
-                    DimensionType.HEIGHT, inv.numRows,  heightPx - mInsets.top,
-                    mResponsiveWorkspaceHeightSpec);
+                    DimensionType.HEIGHT, inv.numAllAppsRowsForCellHeightCalculation,
+                    heightPx - mInsets.top, mResponsiveWorkspaceHeightSpec);
 
             ResponsiveSpecsProvider folderSpecs = ResponsiveSpecsProvider.create(
                     new ResourceHelper(context,
@@ -988,16 +988,6 @@
         float workspaceCellPaddingY = getCellSize().y - iconSizePx - iconDrawablePaddingPx
                 - iconTextHeight;
 
-        if (mIsResponsiveGrid) {
-            iconTextSizePx = 0;
-            iconDrawablePaddingPx = 0;
-            int iconSizeWithOverlap = getIconSizeWithOverlap(iconSizePx);
-            cellYPaddingPx = Math.max(0, getCellSize().y - iconSizeWithOverlap) / 2;
-            autoResizeAllAppsCells();
-
-            return;
-        }
-
         // We want enough space so that the text is closer to its corresponding icon.
         if (workspaceCellPaddingY < iconTextHeight) {
             iconTextSizePx = 0;
@@ -1115,25 +1105,28 @@
                 iconSizePx = mIconSizeSteps.getIconSmallerThan(cellWidthPx);
             }
 
-            iconDrawablePaddingPx = getNormalizedIconDrawablePadding();
+            if (isVerticalLayout) {
+                iconDrawablePaddingPx = 0;
+                iconTextSizePx = 0;
+            } else {
+                iconDrawablePaddingPx = getNormalizedIconDrawablePadding();
+            }
 
             CellContentDimensions cellContentDimensions = new CellContentDimensions(iconSizePx,
                     iconDrawablePaddingPx,
                     iconTextSizePx);
-            if (isVerticalLayout) {
-                if (cellHeightPx < iconSizePx) {
-                    cellContentDimensions.setIconSizePx(
-                            mIconSizeSteps.getIconSmallerThan(cellHeightPx));
-                }
-            } else {
-                cellContentDimensions.resizeToFitCellHeight(cellHeightPx, mIconSizeSteps);
-            }
+            int cellContentHeight = cellContentDimensions.resizeToFitCellHeight(cellHeightPx,
+                    mIconSizeSteps);
             iconSizePx = cellContentDimensions.getIconSizePx();
             iconDrawablePaddingPx = cellContentDimensions.getIconDrawablePaddingPx();
             iconTextSizePx = cellContentDimensions.getIconTextSizePx();
-            int cellContentHeight = cellContentDimensions.getCellContentHeight();
 
-            cellYPaddingPx = Math.max(0, cellHeightPx - cellContentHeight) / 2;
+            if (isVerticalLayout) {
+                cellYPaddingPx = Math.max(0, getCellSize().y - getIconSizeWithOverlap(iconSizePx))
+                        / 2;
+            } else {
+                cellYPaddingPx = Math.max(0, cellHeightPx - cellContentHeight) / 2;
+            }
         } else if (mIsScalableGrid) {
             iconDrawablePaddingPx = (int) (getNormalizedIconDrawablePadding() * iconScale);
             cellWidthPx = pxFromDp(inv.minCellSize[mTypeIndex].x, mMetrics, scale);
@@ -1217,7 +1210,7 @@
             updateAllAppsIconSize(scale, res);
         }
         updateAllAppsContainerWidth();
-        if (isVerticalBarLayout()) {
+        if (isVerticalLayout && !mIsResponsiveGrid) {
             hideWorkspaceLabelsIfNotEnoughSpace();
         }
         if (FeatureFlags.enableTwolineAllapps()) {
@@ -1341,7 +1334,7 @@
 
         if (allAppsCellHeightPx < cellContentDimensions.getCellContentHeight()) {
             if (isVerticalBarLayout()) {
-                if (allAppsCellHeightPx < iconSizePx) {
+                if (allAppsCellHeightPx < allAppsIconSizePx) {
                     cellContentDimensions.setIconSizePx(
                             mIconSizeSteps.getIconSmallerThan(allAppsCellHeightPx));
                 }
@@ -1355,6 +1348,10 @@
         }
 
         allAppsCellHeightPx += mResponsiveAllAppsHeightSpec.getGutterPx();
+
+        if (isVerticalBarLayout()) {
+            autoResizeAllAppsCells();
+        }
     }
 
     /**
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 1cbc5b6..42d4d50 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -170,6 +170,7 @@
      * Number of columns in the all apps list.
      */
     public int numAllAppsColumns;
+    public int numAllAppsRowsForCellHeightCalculation;
     public int numDatabaseAllAppsColumns;
     public @StyleRes int allAppsStyle;
 
@@ -393,6 +394,8 @@
         workspaceCellSpecsTwoPanelId = closestProfile.mWorkspaceCellSpecsTwoPanelId;
         allAppsCellSpecsId = closestProfile.mAllAppsCellSpecsId;
         allAppsCellSpecsTwoPanelId = closestProfile.mAllAppsCellSpecsTwoPanelId;
+        numAllAppsRowsForCellHeightCalculation =
+                closestProfile.mNumAllAppsRowsForCellHeightCalculation;
         this.deviceType = deviceType;
 
         inlineNavButtonsEndSpacing = closestProfile.inlineNavButtonsEndSpacing;
@@ -423,6 +426,7 @@
         allAppsStyle = closestProfile.allAppsStyle;
 
         numAllAppsColumns = closestProfile.numAllAppsColumns;
+
         numDatabaseAllAppsColumns = deviceType == TYPE_MULTI_DISPLAY
                 ? closestProfile.numDatabaseAllAppsColumns : closestProfile.numAllAppsColumns;
 
@@ -821,6 +825,7 @@
 
         private final @StyleRes int allAppsStyle;
         private final int numAllAppsColumns;
+        private final int mNumAllAppsRowsForCellHeightCalculation;
         private final int numDatabaseAllAppsColumns;
         private final int numHotseatIcons;
         private final int numDatabaseHotseatIcons;
@@ -943,34 +948,37 @@
                         R.styleable.GridDisplayOption_workspaceSpecsId, INVALID_RESOURCE_HANDLE);
                 mWorkspaceSpecsTwoPanelId = a.getResourceId(
                         R.styleable.GridDisplayOption_workspaceSpecsTwoPanelId,
-                        INVALID_RESOURCE_HANDLE);
+                        mWorkspaceSpecsId);
                 mAllAppsSpecsId = a.getResourceId(
                         R.styleable.GridDisplayOption_allAppsSpecsId, INVALID_RESOURCE_HANDLE);
                 mAllAppsSpecsTwoPanelId = a.getResourceId(
                         R.styleable.GridDisplayOption_allAppsSpecsTwoPanelId,
-                        INVALID_RESOURCE_HANDLE);
+                        mAllAppsSpecsId);
                 mFolderSpecsId = a.getResourceId(
                         R.styleable.GridDisplayOption_folderSpecsId, INVALID_RESOURCE_HANDLE);
                 mFolderSpecsTwoPanelId = a.getResourceId(
                         R.styleable.GridDisplayOption_folderSpecsTwoPanelId,
-                        INVALID_RESOURCE_HANDLE);
+                        mFolderSpecsId);
                 mHotseatSpecsId = a.getResourceId(
                         R.styleable.GridDisplayOption_hotseatSpecsId, INVALID_RESOURCE_HANDLE);
                 mHotseatSpecsTwoPanelId = a.getResourceId(
                         R.styleable.GridDisplayOption_hotseatSpecsTwoPanelId,
-                        INVALID_RESOURCE_HANDLE);
+                        mHotseatSpecsId);
                 mWorkspaceCellSpecsId = a.getResourceId(
                         R.styleable.GridDisplayOption_workspaceCellSpecsId,
                         INVALID_RESOURCE_HANDLE);
                 mWorkspaceCellSpecsTwoPanelId = a.getResourceId(
                         R.styleable.GridDisplayOption_workspaceCellSpecsTwoPanelId,
-                        INVALID_RESOURCE_HANDLE);
+                        mWorkspaceCellSpecsId);
                 mAllAppsCellSpecsId = a.getResourceId(
                         R.styleable.GridDisplayOption_allAppsCellSpecsId,
                         INVALID_RESOURCE_HANDLE);
                 mAllAppsCellSpecsTwoPanelId = a.getResourceId(
                         R.styleable.GridDisplayOption_allAppsCellSpecsTwoPanelId,
-                        INVALID_RESOURCE_HANDLE);
+                        mAllAppsCellSpecsId);
+                mNumAllAppsRowsForCellHeightCalculation = a.getInt(
+                        R.styleable.GridDisplayOption_numAllAppsRowsForCellHeightCalculation,
+                        numRows);
             } else {
                 mWorkspaceSpecsId = INVALID_RESOURCE_HANDLE;
                 mWorkspaceSpecsTwoPanelId = INVALID_RESOURCE_HANDLE;
@@ -984,6 +992,7 @@
                 mWorkspaceCellSpecsTwoPanelId = INVALID_RESOURCE_HANDLE;
                 mAllAppsCellSpecsId = INVALID_RESOURCE_HANDLE;
                 mAllAppsCellSpecsTwoPanelId = INVALID_RESOURCE_HANDLE;
+                mNumAllAppsRowsForCellHeightCalculation = numRows;
             }
 
             int inlineForRotation = a.getInt(R.styleable.GridDisplayOption_inlineQsb,
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index e0e35a4..b5dd10e 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -89,7 +89,6 @@
 import static com.android.launcher3.logging.StatsLogManager.StatsLatencyLogger.LatencyType.WARM;
 import static com.android.launcher3.model.ItemInstallQueue.FLAG_ACTIVITY_PAUSED;
 import static com.android.launcher3.model.ItemInstallQueue.FLAG_DRAG_AND_DROP;
-import static com.android.launcher3.model.data.LauncherAppWidgetInfo.CUSTOM_WIDGET_ID;
 import static com.android.launcher3.popup.SystemShortcut.APP_INFO;
 import static com.android.launcher3.popup.SystemShortcut.INSTALL;
 import static com.android.launcher3.popup.SystemShortcut.WIDGETS;
@@ -196,7 +195,6 @@
 import com.android.launcher3.model.ItemInstallQueue;
 import com.android.launcher3.model.ModelWriter;
 import com.android.launcher3.model.StringCache;
-import com.android.launcher3.model.WidgetsModel;
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.FolderInfo;
 import com.android.launcher3.model.data.ItemInfo;
@@ -208,7 +206,6 @@
 import com.android.launcher3.popup.ArrowPopup;
 import com.android.launcher3.popup.PopupDataProvider;
 import com.android.launcher3.popup.SystemShortcut;
-import com.android.launcher3.qsb.QsbContainerView;
 import com.android.launcher3.statemanager.StateManager;
 import com.android.launcher3.statemanager.StateManager.StateHandler;
 import com.android.launcher3.statemanager.StatefulActivity;
@@ -252,6 +249,8 @@
 import com.android.launcher3.widget.PendingAddWidgetInfo;
 import com.android.launcher3.widget.PendingAppWidgetHostView;
 import com.android.launcher3.widget.WidgetAddFlowHandler;
+import com.android.launcher3.widget.WidgetInflater;
+import com.android.launcher3.widget.WidgetInflater.InflationResult;
 import com.android.launcher3.widget.WidgetManagerHelper;
 import com.android.launcher3.widget.custom.CustomWidgetManager;
 import com.android.launcher3.widget.model.WidgetsListBaseEntry;
@@ -333,6 +332,7 @@
 
     private WidgetManagerHelper mAppWidgetManager;
     private LauncherWidgetHolder mAppWidgetHolder;
+    private WidgetInflater mWidgetInflater;
 
     private final int[] mTmpAddItemCellCoordinates = new int[2];
 
@@ -515,8 +515,10 @@
         mStateManager = new StateManager<>(this, NORMAL);
 
         setupViews();
+        updateDisallowBack();
 
         mAppWidgetManager = new WidgetManagerHelper(this);
+        mWidgetInflater = new WidgetInflater(this);
         mAppWidgetHolder = createAppWidgetHolder();
         mAppWidgetHolder.startListening();
         mAppWidgetHolder.addProviderChangeListener(() -> refreshAndBindWidgetsForPackageUser(null));
@@ -1011,7 +1013,7 @@
         AppWidgetHostView boundWidget = null;
         if (resultCode == RESULT_OK) {
             animationType = Workspace.COMPLETE_TWO_STAGE_WIDGET_DROP_ANIMATION;
-            final AppWidgetHostView layout = mAppWidgetHolder.createView(this, appWidgetId,
+            final AppWidgetHostView layout = mAppWidgetHolder.createView(appWidgetId,
                     requestArgs.getWidgetHandler().getProviderInfo(this));
             boundWidget = layout;
             onCompleteRunnable = () -> {
@@ -1464,7 +1466,7 @@
 
         if (hostView == null) {
             // Perform actual inflation because we're live
-            hostView = mAppWidgetHolder.createView(this, appWidgetId, appWidgetInfo);
+            hostView = mAppWidgetHolder.createView(appWidgetId, appWidgetInfo);
         }
 
         LauncherAppWidgetInfo launcherInfo;
@@ -2309,162 +2311,30 @@
 
     private View inflateAppWidget(LauncherAppWidgetInfo item,
             @Nullable LauncherRestoreEventLogger restoreEventLogger) {
-        if (item.hasOptionFlag(LauncherAppWidgetInfo.OPTION_SEARCH_WIDGET)) {
-            item.providerName = QsbContainerView.getSearchComponentName(this);
-            if (item.providerName == null) {
-                getModelWriter().deleteItemFromDatabase(item,
-                        "search widget removed because search component cannot be found");
-                return null;
-            }
-        }
-        final AppWidgetHostView view;
-        if (mIsSafeModeEnabled) {
-            view = new PendingAppWidgetHostView(this, item, mIconCache, true);
-            prepareAppWidget(view, item);
-            return view;
-        }
-
         TraceHelper.INSTANCE.beginSection("BIND_WIDGET_id=" + item.appWidgetId);
         try {
-            final LauncherAppWidgetProviderInfo appWidgetInfo;
-            String removalReason = "";
-            if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)) {
-                // If the provider is not ready, bind as a pending widget.
-                appWidgetInfo = null;
-                removalReason = "the provider isn't ready.";
-            } else if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) {
-                // The widget id is not valid. Try to find the widget based on the provider info.
-                appWidgetInfo = mAppWidgetManager.findProvider(item.providerName, item.user);
-                if (appWidgetInfo == null) {
-                    if (WidgetsModel.GO_DISABLE_WIDGETS) {
-                        removalReason = "widgets are disabled on go device.";
-                    } else {
-                        removalReason =
-                                "WidgetManagerHelper cannot find a provider from provider info.";
-                    }
+            InflationResult inflationResult = mWidgetInflater.inflateAppWidget(item);
+            if (inflationResult.getType() == WidgetInflater.TYPE_DELETE) {
+                getModelWriter().deleteItemFromDatabase(item, inflationResult.getReason());
+                if (restoreEventLogger != null) {
+                    restoreEventLogger.logSingleFavoritesItemRestoreFailed(
+                            ITEM_TYPE_APPWIDGET, RESTORE_ERROR_BIND_FAILURE);
                 }
-            } else {
-                appWidgetInfo = mAppWidgetManager.getLauncherAppWidgetInfo(item.appWidgetId,
-                        item.getTargetComponent());
-                if (appWidgetInfo == null) {
-                    if (item.appWidgetId <= CUSTOM_WIDGET_ID) {
-                        removalReason =
-                                "CustomWidgetManager cannot find provider from that widget id.";
-                    } else {
-                        removalReason = "AppWidgetManager cannot find provider for that widget id."
-                                + " It could be because AppWidgetService is not available, or the"
-                                + " appWidgetId has not been bound to a the provider yet, or you"
-                                + " don't have access to that appWidgetId.";
-                    }
-                }
+                return null;
             }
 
-            // If the provider is ready, but the width is not yet restored, try to restore it.
-            if (!item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)
-                    && (item.restoreStatus != LauncherAppWidgetInfo.RESTORE_COMPLETED)) {
-                if (appWidgetInfo == null) {
-                    getModelWriter().deleteItemFromDatabase(item,
-                            "Removing restored widget: id=" + item.appWidgetId
-                            + " belongs to component " + item.providerName + " user " + item.user
-                            + ", as the provider is null and " + removalReason);
-                    if (restoreEventLogger != null) {
-                        restoreEventLogger.logSingleFavoritesItemRestoreFailed(
-                                ITEM_TYPE_APPWIDGET, RESTORE_ERROR_BIND_FAILURE);
-                    }
-                    return null;
-                }
-
-                // If we do not have a valid id, try to bind an id.
-                if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) {
-                    if (!item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_ALLOCATED)) {
-                        // Id has not been allocated yet. Allocate a new id.
-                        item.appWidgetId = mAppWidgetHolder.allocateAppWidgetId();
-                        item.restoreStatus |= LauncherAppWidgetInfo.FLAG_ID_ALLOCATED;
-
-                        // Also try to bind the widget. If the bind fails, the user will be shown
-                        // a click to setup UI, which will ask for the bind permission.
-                        PendingAddWidgetInfo pendingInfo =
-                                new PendingAddWidgetInfo(appWidgetInfo, item.sourceContainer);
-                        pendingInfo.spanX = item.spanX;
-                        pendingInfo.spanY = item.spanY;
-                        pendingInfo.minSpanX = item.minSpanX;
-                        pendingInfo.minSpanY = item.minSpanY;
-                        Bundle options = pendingInfo.getDefaultSizeOptions(this);
-
-                        boolean isDirectConfig =
-                                item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG);
-                        if (isDirectConfig && item.bindOptions != null) {
-                            Bundle newOptions = item.bindOptions.getExtras();
-                            if (options != null) {
-                                newOptions.putAll(options);
-                            }
-                            options = newOptions;
-                        }
-                        boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed(
-                                item.appWidgetId, appWidgetInfo, options);
-
-                        // We tried to bind once. If we were not able to bind, we would need to
-                        // go through the permission dialog, which means we cannot skip the config
-                        // activity.
-                        item.bindOptions = null;
-                        item.restoreStatus &= ~LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG;
-
-                        // Bind succeeded
-                        if (success) {
-                            // If the widget has a configure activity, it is still needs to set it
-                            // up, otherwise the widget is ready to go.
-                            item.restoreStatus = (appWidgetInfo.configure == null) || isDirectConfig
-                                    ? LauncherAppWidgetInfo.RESTORE_COMPLETED
-                                    : LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
-                        }
-
-                        getModelWriter().updateItemInDatabase(item);
-                    }
-                } else if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_UI_NOT_READY)
-                        && (appWidgetInfo.configure == null)) {
-                    // The widget was marked as UI not ready, but there is no configure activity to
-                    // update the UI.
-                    item.restoreStatus = LauncherAppWidgetInfo.RESTORE_COMPLETED;
-                    getModelWriter().updateItemInDatabase(item);
-                }
-                else if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_UI_NOT_READY)
-                        && appWidgetInfo.configure != null) {
-                    if (mAppWidgetManager.isAppWidgetRestored(item.appWidgetId)) {
-                        item.restoreStatus = LauncherAppWidgetInfo.RESTORE_COMPLETED;
-                        getModelWriter().updateItemInDatabase(item);
-                    }
-                }
+            if (inflationResult.isUpdate()) {
+                getModelWriter().updateItemInDatabase(item);
             }
-
-            if (item.restoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED) {
-                // Verify that we own the widget
-                if (appWidgetInfo == null) {
-                    FileLog.e(TAG, "Removing invalid widget: id=" + item.appWidgetId);
-                    getModelWriter().deleteWidgetInfo(item, getAppWidgetHolder(), removalReason);
-                    if (restoreEventLogger != null) {
-                        restoreEventLogger.logSingleFavoritesItemRestoreFailed(
-                                ITEM_TYPE_APPWIDGET, RESTORE_ERROR_BIND_FAILURE);
-                    }
-                    return null;
-                }
-
-                item.minSpanX = appWidgetInfo.minSpanX;
-                item.minSpanY = appWidgetInfo.minSpanY;
-                view = mAppWidgetHolder.createView(this, item.appWidgetId, appWidgetInfo);
-            } else if (!item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)
-                    && appWidgetInfo != null) {
-                mAppWidgetHolder.addPendingView(item.appWidgetId,
-                        new PendingAppWidgetHostView(this, item, mIconCache, false));
-                view = mAppWidgetHolder.createView(this, item.appWidgetId, appWidgetInfo);
-            } else {
-                view = new PendingAppWidgetHostView(this, item, mIconCache, false);
-            }
+            AppWidgetHostView view = inflationResult.getType() == WidgetInflater.TYPE_PENDING
+                    ? new PendingAppWidgetHostView(this, item, inflationResult.getWidgetInfo())
+                    : mAppWidgetHolder.createView(
+                            item.appWidgetId, inflationResult.getWidgetInfo());
             prepareAppWidget(view, item);
+            return view;
         } finally {
             TraceHelper.INSTANCE.endSection();
         }
-
-        return view;
     }
 
     /**
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 5ae2d71..e015021 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -44,6 +44,7 @@
 import com.android.launcher3.icons.IconProvider;
 import com.android.launcher3.icons.LauncherIconProvider;
 import com.android.launcher3.icons.LauncherIcons;
+import com.android.launcher3.model.ModelLauncherCallbacks;
 import com.android.launcher3.notification.NotificationListener;
 import com.android.launcher3.pm.InstallSessionHelper;
 import com.android.launcher3.pm.InstallSessionTracker;
@@ -56,6 +57,7 @@
 import com.android.launcher3.util.SettingsCache;
 import com.android.launcher3.util.SimpleBroadcastReceiver;
 import com.android.launcher3.util.Themes;
+import com.android.launcher3.util.TraceHelper;
 import com.android.launcher3.widget.custom.CustomWidgetManager;
 
 public class LauncherAppState implements SafeCloseable {
@@ -71,6 +73,8 @@
     private final LauncherIconProvider mIconProvider;
     private final IconCache mIconCache;
     private final InvariantDeviceProfile mInvariantDeviceProfile;
+    private boolean mIsSafeModeEnabled;
+
     private final RunnableList mOnTerminateCallback = new RunnableList();
 
     public static LauncherAppState getInstance(final Context context) {
@@ -90,15 +94,18 @@
         Log.v(Launcher.TAG, "LauncherAppState initiated");
         Preconditions.assertUIThread();
 
+        mIsSafeModeEnabled = TraceHelper.allowIpcs("isSafeMode",
+                () -> context.getPackageManager().isSafeMode());
         mInvariantDeviceProfile.addOnChangeListener(modelPropertiesChanged -> {
             if (modelPropertiesChanged) {
                 refreshAndReloadLauncher();
             }
         });
 
-        mContext.getSystemService(LauncherApps.class).registerCallback(mModel);
+        ModelLauncherCallbacks callbacks = mModel.newModelCallbacks();
+        mContext.getSystemService(LauncherApps.class).registerCallback(callbacks);
         mOnTerminateCallback.add(() ->
-                mContext.getSystemService(LauncherApps.class).unregisterCallback(mModel));
+                mContext.getSystemService(LauncherApps.class).unregisterCallback(callbacks));
 
         SimpleBroadcastReceiver modelChangeReceiver =
                 new SimpleBroadcastReceiver(mModel::onBroadcastIntent);
@@ -224,6 +231,10 @@
         return mInvariantDeviceProfile;
     }
 
+    public boolean isSafeModeEnabled() {
+        return mIsSafeModeEnabled;
+    }
+
     /**
      * Shorthand for {@link #getInvariantDeviceProfile()}
      */
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index c81db63..d124746 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -20,6 +20,7 @@
 
 import static com.android.launcher3.LauncherAppState.ACTION_FORCE_ROLOAD;
 import static com.android.launcher3.config.FeatureFlags.IS_STUDIO_BUILD;
+import static com.android.launcher3.model.PackageUpdatedTask.OP_UPDATE;
 import static com.android.launcher3.pm.UserCache.ACTION_PROFILE_AVAILABLE;
 import static com.android.launcher3.pm.UserCache.ACTION_PROFILE_UNAVAILABLE;
 import static com.android.launcher3.testing.shared.TestProtocol.sDebugTracing;
@@ -28,7 +29,6 @@
 
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.LauncherApps;
 import android.content.pm.PackageInstaller;
 import android.content.pm.ShortcutInfo;
 import android.os.UserHandle;
@@ -43,7 +43,6 @@
 import com.android.launcher3.celllayout.CellPosMapper;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.logging.FileLog;
 import com.android.launcher3.model.AddWorkspaceItemsTask;
 import com.android.launcher3.model.AllAppsList;
 import com.android.launcher3.model.BaseModelUpdateTask;
@@ -55,8 +54,8 @@
 import com.android.launcher3.model.LoaderTask;
 import com.android.launcher3.model.ModelDbController;
 import com.android.launcher3.model.ModelDelegate;
+import com.android.launcher3.model.ModelLauncherCallbacks;
 import com.android.launcher3.model.ModelWriter;
-import com.android.launcher3.model.PackageIncrementalDownloadUpdatedTask;
 import com.android.launcher3.model.PackageInstallStateChangedTask;
 import com.android.launcher3.model.PackageUpdatedTask;
 import com.android.launcher3.model.ReloadStringCacheTask;
@@ -89,7 +88,7 @@
  * LauncherModel object held in a static. Also provide APIs for updating the database state
  * for the Launcher.
  */
-public class LauncherModel extends LauncherApps.Callback implements InstallSessionTracker.Callback {
+public class LauncherModel implements InstallSessionTracker.Callback {
     private static final boolean DEBUG_RECEIVER = false;
 
     static final String TAG = "Launcher.Model";
@@ -168,6 +167,10 @@
         return mModelDbController;
     }
 
+    public ModelLauncherCallbacks newModelCallbacks() {
+        return new ModelLauncherCallbacks(this::enqueueModelUpdateTask);
+    }
+
     /**
      * Adds the provided items to the workspace.
      */
@@ -186,77 +189,6 @@
                 owner);
     }
 
-    @Override
-    public void onPackageChanged(
-            @NonNull final String packageName, @NonNull final UserHandle user) {
-        int op = PackageUpdatedTask.OP_UPDATE;
-        enqueueModelUpdateTask(new PackageUpdatedTask(op, user, packageName));
-    }
-
-    @Override
-    public void onPackageRemoved(
-            @NonNull final String packageName, @NonNull final UserHandle user) {
-        onPackagesRemoved(user, packageName);
-    }
-
-    public void onPackagesRemoved(
-            @NonNull final UserHandle user, @NonNull final String... packages) {
-        int op = PackageUpdatedTask.OP_REMOVE;
-        FileLog.d(TAG, "package removed received " + TextUtils.join(",", packages));
-        enqueueModelUpdateTask(new PackageUpdatedTask(op, user, packages));
-    }
-
-    @Override
-    public void onPackageAdded(@NonNull final String packageName, @NonNull final UserHandle user) {
-        int op = PackageUpdatedTask.OP_ADD;
-        enqueueModelUpdateTask(new PackageUpdatedTask(op, user, packageName));
-    }
-
-    @Override
-    public void onPackagesAvailable(@NonNull final String[] packageNames,
-            @NonNull final UserHandle user, final boolean replacing) {
-        enqueueModelUpdateTask(
-                new PackageUpdatedTask(PackageUpdatedTask.OP_UPDATE, user, packageNames));
-    }
-
-    @Override
-    public void onPackagesUnavailable(@NonNull final String[] packageNames,
-            @NonNull final UserHandle user, final boolean replacing) {
-        if (!replacing) {
-            enqueueModelUpdateTask(new PackageUpdatedTask(
-                    PackageUpdatedTask.OP_UNAVAILABLE, user, packageNames));
-        }
-    }
-
-    @Override
-    public void onPackagesSuspended(
-            @NonNull final String[] packageNames, @NonNull final UserHandle user) {
-        enqueueModelUpdateTask(new PackageUpdatedTask(
-                PackageUpdatedTask.OP_SUSPEND, user, packageNames));
-    }
-
-    @Override
-    public void onPackagesUnsuspended(
-            @NonNull final String[] packageNames, @NonNull final UserHandle user) {
-        enqueueModelUpdateTask(new PackageUpdatedTask(
-                PackageUpdatedTask.OP_UNSUSPEND, user, packageNames));
-    }
-
-    @Override
-    public void onPackageLoadingProgressChanged(@NonNull final String packageName,
-            @NonNull final UserHandle user, final float progress) {
-        if (Utilities.ATLEAST_S) {
-            enqueueModelUpdateTask(new PackageIncrementalDownloadUpdatedTask(
-                    packageName, user, progress));
-        }
-    }
-
-    @Override
-    public void onShortcutsChanged(@NonNull final String packageName,
-            @NonNull final List<ShortcutInfo> shortcuts, @NonNull final UserHandle user) {
-        enqueueModelUpdateTask(new ShortcutsChangedTask(packageName, shortcuts, user, true));
-    }
-
     /**
      * Called when the icon for an app changes, outside of package event
      */
@@ -265,7 +197,7 @@
             @NonNull final UserHandle user) {
         // Update the icon for the calendar package
         Context context = mApp.getContext();
-        onPackageChanged(packageName, user);
+        enqueueModelUpdateTask(new PackageUpdatedTask(OP_UPDATE, user, packageName));
 
         List<ShortcutInfo> pinnedShortcuts = new ShortcutRequest(context, user)
                 .forPackage(packageName).query(ShortcutRequest.PINNED);
diff --git a/src/com/android/launcher3/LauncherPrefs.kt b/src/com/android/launcher3/LauncherPrefs.kt
index 51ba5c6..067d150 100644
--- a/src/com/android/launcher3/LauncherPrefs.kt
+++ b/src/com/android/launcher3/LauncherPrefs.kt
@@ -303,15 +303,11 @@
         const val SHOULD_SHOW_SMARTSPACE_KEY = "SHOULD_SHOW_SMARTSPACE_KEY"
         @JvmField
         val ICON_STATE =
-            nonRestorableItem(
-                    "pref_icon_shape_path",
-                "",
-                EncryptionType.MOVE_TO_DEVICE_PROTECTED
-            )
+            nonRestorableItem("pref_icon_shape_path", "", EncryptionType.MOVE_TO_DEVICE_PROTECTED)
         @JvmField
         val ALL_APPS_OVERVIEW_THRESHOLD =
             nonRestorableItem(
-                    "pref_all_apps_overview_threshold",
+                "pref_all_apps_overview_threshold",
                 180,
                 EncryptionType.MOVE_TO_DEVICE_PROTECTED
             )
@@ -319,58 +315,54 @@
         val LONG_PRESS_NAV_HANDLE_SLOP_PERCENTAGE =
             nonRestorableItem(
                 "pref_long_press_nav_handle_slop_percentage",
-                        LPNH_SLOP_PERCENTAGE.get(),
-                        EncryptionType.MOVE_TO_DEVICE_PROTECTED
+                LPNH_SLOP_PERCENTAGE.get(),
+                EncryptionType.MOVE_TO_DEVICE_PROTECTED
             )
         @JvmField
         val LONG_PRESS_NAV_HANDLE_TIMEOUT_MS =
-                nonRestorableItem(
-                        "pref_long_press_nav_handle_timeout_ms",
-                        LPNH_TIMEOUT_MS.get(),
-                        EncryptionType.MOVE_TO_DEVICE_PROTECTED
-                )
+            nonRestorableItem(
+                "pref_long_press_nav_handle_timeout_ms",
+                LPNH_TIMEOUT_MS.get(),
+                EncryptionType.MOVE_TO_DEVICE_PROTECTED
+            )
         @JvmField
         val LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_START_SCALE_PERCENT =
-                nonRestorableItem(
-                        "pref_long_press_nav_handle_haptic_hint_start_scale_percent",
-                        LPNH_HAPTIC_HINT_START_SCALE_PERCENT.get(),
-                        EncryptionType.MOVE_TO_DEVICE_PROTECTED
-                )
+            nonRestorableItem(
+                "pref_long_press_nav_handle_haptic_hint_start_scale_percent",
+                LPNH_HAPTIC_HINT_START_SCALE_PERCENT.get(),
+                EncryptionType.MOVE_TO_DEVICE_PROTECTED
+            )
         @JvmField
         val LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_END_SCALE_PERCENT =
-                nonRestorableItem(
-                        "pref_long_press_nav_handle_haptic_hint_end_scale_percent",
-                        LPNH_HAPTIC_HINT_END_SCALE_PERCENT.get(),
-                        EncryptionType.MOVE_TO_DEVICE_PROTECTED
-                )
+            nonRestorableItem(
+                "pref_long_press_nav_handle_haptic_hint_end_scale_percent",
+                LPNH_HAPTIC_HINT_END_SCALE_PERCENT.get(),
+                EncryptionType.MOVE_TO_DEVICE_PROTECTED
+            )
         @JvmField
         val LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_SCALE_EXPONENT =
-                nonRestorableItem(
-                        "pref_long_press_nav_handle_haptic_hint_scale_exponent",
-                        LPNH_HAPTIC_HINT_SCALE_EXPONENT.get(),
-                        EncryptionType.MOVE_TO_DEVICE_PROTECTED
-                )
+            nonRestorableItem(
+                "pref_long_press_nav_handle_haptic_hint_scale_exponent",
+                LPNH_HAPTIC_HINT_SCALE_EXPONENT.get(),
+                EncryptionType.MOVE_TO_DEVICE_PROTECTED
+            )
         @JvmField
         val LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_ITERATIONS =
-                nonRestorableItem(
-                        "pref_long_press_nav_handle_haptic_hint_iterations",
-                        LPNH_HAPTIC_HINT_ITERATIONS.get(),
-                        EncryptionType.MOVE_TO_DEVICE_PROTECTED
-                )
+            nonRestorableItem(
+                "pref_long_press_nav_handle_haptic_hint_iterations",
+                LPNH_HAPTIC_HINT_ITERATIONS.get(),
+                EncryptionType.MOVE_TO_DEVICE_PROTECTED
+            )
         @JvmField
         val LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_DELAY =
-                nonRestorableItem(
-                        "pref_long_press_nav_handle_haptic_hint_delay",
-                        LPNH_HAPTIC_HINT_DELAY.get(),
-                        EncryptionType.MOVE_TO_DEVICE_PROTECTED
-                )
+            nonRestorableItem(
+                "pref_long_press_nav_handle_haptic_hint_delay",
+                LPNH_HAPTIC_HINT_DELAY.get(),
+                EncryptionType.MOVE_TO_DEVICE_PROTECTED
+            )
         @JvmField
         val PRIVATE_SPACE_APPS =
-                nonRestorableItem(
-                        "pref_private_space_apps",
-                        0,
-                        EncryptionType.MOVE_TO_DEVICE_PROTECTED
-                )
+            nonRestorableItem("pref_private_space_apps", 0, EncryptionType.MOVE_TO_DEVICE_PROTECTED)
         @JvmField
         val THEMED_ICONS =
             backedUpItem(Themes.KEY_THEMED_ICONS, false, EncryptionType.MOVE_TO_DEVICE_PROTECTED)
@@ -420,7 +412,7 @@
             )
         @JvmField
         val IS_FIRST_LOAD_AFTER_RESTORE =
-            backedUpItem(
+            nonRestorableItem(
                 FIRST_LOAD_AFTER_RESTORE_KEY,
                 false,
                 EncryptionType.MOVE_TO_DEVICE_PROTECTED
diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
index 4ad4c71..ad764e3 100644
--- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
@@ -1315,6 +1315,10 @@
                 : mViewPager == null ? AdapterHolder.MAIN : mViewPager.getNextPage();
     }
 
+    public PrivateProfileManager getPrivateProfileManager() {
+        return mPrivateProfileManager;
+    }
+
     /**
      * Adds an update listener to animator that adds springs to the animation.
      */
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 03ac9df..e2c5795 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -342,7 +342,7 @@
             });
         }
 
-        if(FeatureFlags.ENABLE_PREMIUM_HAPTICS_ALL_APPS.get() && config.userControlled
+        if (FeatureFlags.ENABLE_PREMIUM_HAPTICS_ALL_APPS.get() && config.isUserControlled()
                 && Utilities.ATLEAST_S) {
             if (toState == ALL_APPS) {
                 builder.addOnFrameListener(
@@ -367,7 +367,7 @@
 
         // need to decide depending on the release velocity
         Interpolator verticalProgressInterpolator = config.getInterpolator(ANIM_VERTICAL_PROGRESS,
-                config.userControlled ? LINEAR : DECELERATE_1_7);
+                config.isUserControlled() ? LINEAR : DECELERATE_1_7);
         Animator anim = createSpringAnimation(mProgress, targetProgress);
         anim.setInterpolator(verticalProgressInterpolator);
         builder.add(anim);
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index 1782791..35c07c3 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -22,6 +22,7 @@
 import android.content.Context;
 
 import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
 import androidx.recyclerview.widget.DiffUtil;
 
 import com.android.launcher3.Flags;
@@ -321,6 +322,8 @@
                     break;
                 case PrivateProfileManager.STATE_ENABLED:
                     // Add PS Apps only in Enabled State.
+                    mPrivateProviderManager.addPrivateSpaceInstallAppButton(mAdapterItems);
+                    position++;
                     addAppsWithSections(mPrivateApps, position);
                     if (mActivityContext.getAppsView() != null) {
                         mActivityContext.getAppsView().getActiveRecyclerView()
@@ -338,26 +341,14 @@
             hasPrivateApps = appList.stream().
                     allMatch(mPrivateProviderManager.getItemInfoMatcher());
         }
-        int privateAppCount = 0;
-        int numberOfColumns = mActivityContext.getDeviceProfile().numShownAllAppsColumns;
-        int numberOfAppRows = (int) Math.ceil((double) appList.size() / numberOfColumns);
-        for (AppInfo info : appList) {
+        for (int i = 0; i < appList.size(); i++) {
+            AppInfo info = appList.get(i);
             // Apply decorator to private apps.
             if (hasPrivateApps) {
-                int roundRegion = ROUND_NOTHING;
-                if ((privateAppCount / numberOfColumns) == numberOfAppRows - 1) {
-                    if ((privateAppCount % numberOfColumns) == 0) {
-                        // App is the first column
-                        roundRegion = ROUND_BOTTOM_LEFT;
-                    } else if ((privateAppCount % numberOfColumns) == numberOfColumns-1) {
-                        roundRegion = ROUND_BOTTOM_RIGHT;
-                    }
-                }
                 mAdapterItems.add(AdapterItem.asAppWithDecorationInfo(info,
                         new SectionDecorationInfo(mActivityContext.getApplicationContext(),
-                                roundRegion,
+                                getRoundRegions(i, appList.size()),
                                 true /* decorateTogether */)));
-                privateAppCount += 1;
             } else {
                 mAdapterItems.add(AdapterItem.asApp(info));
             }
@@ -372,6 +363,43 @@
         }
     }
 
+    /**
+     * Determines the corner regions that should be rounded for a specific app icon based on its
+     * position in a grid. Apps that should only be cared about rounding are the apps in the last
+     * row. In the last row on the first column, the app should only be rounded on the bottom left.
+     * Apps in the middle would not be rounded and the last app on the last row will ALWAYS have a
+     * {@link SectionDecorationInfo#ROUND_BOTTOM_RIGHT}.
+     *
+     * @param appIndex The index of the app icon within the app list.
+     * @param appListSize The total number of apps within the app list.
+     * @return  An integer representing the corner regions to be rounded, using bitwise flags:
+     *          - {@link SectionDecorationInfo#ROUND_NOTHING}: No corners should be rounded.
+     *          - {@link SectionDecorationInfo#ROUND_TOP_LEFT}: Round the top-left corner.
+     *          - {@link SectionDecorationInfo#ROUND_TOP_RIGHT}: Round the top-right corner.
+     *          - {@link SectionDecorationInfo#ROUND_BOTTOM_LEFT}: Round the bottom-left corner.
+     *          - {@link SectionDecorationInfo#ROUND_BOTTOM_RIGHT}: Round the bottom-right corner.
+     */
+    @VisibleForTesting
+    int getRoundRegions(int appIndex, int appListSize) {
+        int numberOfAppRows = (int) Math.ceil((double) appListSize / mNumAppsPerRowAllApps);
+        int roundRegion = ROUND_NOTHING;
+        // App is in the last row.
+        if ((appIndex / mNumAppsPerRowAllApps) == numberOfAppRows - 1) {
+            if ((appIndex % mNumAppsPerRowAllApps) == 0) {
+                // App is the first column.
+                roundRegion = ROUND_BOTTOM_LEFT;
+            } else if ((appIndex % mNumAppsPerRowAllApps) == mNumAppsPerRowAllApps-1) {
+                // App is in the last column.
+                roundRegion = ROUND_BOTTOM_RIGHT;
+            }
+            // Ensure the last private app is rounded on the bottom right.
+            if (appIndex == appListSize - 1) {
+                roundRegion |= ROUND_BOTTOM_RIGHT;
+            }
+        }
+        return roundRegion;
+    }
+
     private static class MyDiffCallback extends DiffUtil.Callback {
 
         private final List<AdapterItem> mOldList;
diff --git a/src/com/android/launcher3/allapps/PrivateProfileManager.java b/src/com/android/launcher3/allapps/PrivateProfileManager.java
index 693681b..c99b69f 100644
--- a/src/com/android/launcher3/allapps/PrivateProfileManager.java
+++ b/src/com/android/launcher3/allapps/PrivateProfileManager.java
@@ -17,11 +17,14 @@
 package com.android.launcher3.allapps;
 
 import static com.android.launcher3.allapps.ActivityAllAppsContainerView.AdapterHolder.MAIN;
+import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_ICON;
 import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_PRIVATE_SPACE_HEADER;
+import static com.android.launcher3.allapps.SectionDecorationInfo.ROUND_NOTHING;
 import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 import static com.android.launcher3.util.SettingsCache.PRIVATE_SPACE_HIDE_WHEN_LOCKED_URI;
 
+import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
@@ -30,13 +33,21 @@
 
 import androidx.annotation.VisibleForTesting;
 
+import com.android.launcher3.BuildConfig;
 import com.android.launcher3.Flags;
+import com.android.launcher3.R;
+import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.logging.StatsLogManager;
+import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.pm.UserCache;
+import com.android.launcher3.uioverrides.ApiWrapper;
 import com.android.launcher3.util.Preconditions;
 import com.android.launcher3.util.SettingsCache;
+import com.android.launcher3.util.UserIconInfo;
 
 import java.util.ArrayList;
+import java.util.List;
 import java.util.function.Predicate;
 
 /**
@@ -71,6 +82,39 @@
         return adapterItems.size();
     }
 
+    /** Adds Private Space install app button to the layout. */
+    public void addPrivateSpaceInstallAppButton(List<BaseAllAppsAdapter.AdapterItem> adapterItems) {
+        Context context = mAllApps.getContext();
+        // Prepare intent
+        UserCache userCache = UserCache.getInstance(context);
+        UserHandle userHandle = userCache.getUserProfiles().stream()
+                .filter(user -> userCache.getUserInfo(user).type == UserIconInfo.TYPE_PRIVATE)
+                .findFirst()
+                .orElse(null);
+        Intent intent = ApiWrapper.getAppMarketActivityIntent(context,
+                BuildConfig.APPLICATION_ID, userHandle);
+
+        // Prepare bitmapInfo
+        Intent.ShortcutIconResource shortcut = Intent.ShortcutIconResource.fromContext(
+                context, com.android.launcher3.R.drawable.private_space_install_app_icon);
+        BitmapInfo bitmapInfo = LauncherIcons.obtain(context).createIconBitmap(shortcut);
+
+        AppInfo itemInfo = new AppInfo();
+        itemInfo.title = context.getResources().getString(R.string.ps_add_button_label);
+        itemInfo.intent = intent;
+        itemInfo.bitmap = bitmapInfo;
+        itemInfo.contentDescription = context.getResources().getString(
+                com.android.launcher3.R.string.ps_add_button_content_description);
+
+        BaseAllAppsAdapter.AdapterItem item = new BaseAllAppsAdapter.AdapterItem(VIEW_TYPE_ICON);
+        item.itemInfo = itemInfo;
+        item.decorationInfo = new SectionDecorationInfo(context, ROUND_NOTHING,
+                /* decorateTogether */ true);
+
+        adapterItems.add(item);
+        mAllApps.mAH.get(MAIN).mAdapter.notifyItemInserted(adapterItems.size() - 1);
+    }
+
     /** Disables quiet mode for Private Space User Profile. */
     public void unlockPrivateProfile() {
         enableQuietMode(false);
diff --git a/src/com/android/launcher3/allapps/SectionDecorationInfo.java b/src/com/android/launcher3/allapps/SectionDecorationInfo.java
index 1fed2b6..c438d19 100644
--- a/src/com/android/launcher3/allapps/SectionDecorationInfo.java
+++ b/src/com/android/launcher3/allapps/SectionDecorationInfo.java
@@ -22,11 +22,11 @@
 
 public class SectionDecorationInfo {
 
-    public static final int ROUND_NOTHING = 1 << 1;
-    public static final int ROUND_TOP_LEFT = 1 << 2;
-    public static final int ROUND_TOP_RIGHT = 1 << 3;
-    public static final int ROUND_BOTTOM_LEFT = 1 << 4;
-    public static final int ROUND_BOTTOM_RIGHT = 1 << 5;
+    public static final int ROUND_NOTHING = 0;
+    public static final int ROUND_TOP_LEFT = 1 << 1;
+    public static final int ROUND_TOP_RIGHT = 1 << 2;
+    public static final int ROUND_BOTTOM_LEFT = 1 << 3;
+    public static final int ROUND_BOTTOM_RIGHT = 1 << 4;
     public static final int DECORATOR_ALPHA = 255;
 
     protected boolean mShouldDecorateItemsTogether;
diff --git a/src/com/android/launcher3/allapps/UserProfileManager.java b/src/com/android/launcher3/allapps/UserProfileManager.java
index 0261010..6bef725 100644
--- a/src/com/android/launcher3/allapps/UserProfileManager.java
+++ b/src/com/android/launcher3/allapps/UserProfileManager.java
@@ -94,6 +94,19 @@
         return mCurrentState;
     }
 
+    /** Returns if user profile is enabled. */
+    public boolean isEnabled() {
+        return mCurrentState == STATE_ENABLED;
+    }
+
+    /** Returns the UserHandle corresponding to the profile type, null in case no matches found. */
+    public UserHandle getProfileUser() {
+        return mUserCache.getUserProfiles().stream()
+                .filter(getUserMatcher())
+                .findAny()
+                .orElse(null);
+    }
+
     /** Logs Event to StatsLogManager. */
     protected void logEvents(StatsLogManager.EventEnum event) {
         mStatsLogManager.logger().log(event);
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 61e853e..3ccd3e1 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -167,8 +167,6 @@
             "Enable the ability to generate monochromatic icons, if it is not provided by the app");
 
     // TODO(Block 8): Clean up flags
-    public static final BooleanFlag ENABLE_LAUNCHER_BR_METRICS = getDebugFlag(305984208,
-            "ENABLE_LAUNCHER_BR_METRICS", TEAMFOOD, "Enable metrics for Launcher restore");
 
     // TODO(Block 9): Clean up flags
     public static final BooleanFlag MULTI_SELECT_EDIT_MODE = getDebugFlag(270709220,
@@ -225,7 +223,7 @@
                 // Task bar pinning and task bar nav bar unification are both dependent on
                 // ENABLE_TASKBAR_NO_RECREATION. We want to turn ENABLE_TASKBAR_NO_RECREATION on
                 // when either of the dependent features is turned on.
-                || ENABLE_TASKBAR_PINNING.get() || ENABLE_TASKBAR_NAVBAR_UNIFICATION;
+                || enableTaskbarPinning() || ENABLE_TASKBAR_NAVBAR_UNIFICATION;
     }
 
     // TODO(Block 16): Clean up flags
diff --git a/src/com/android/launcher3/folder/PreviewItemManager.java b/src/com/android/launcher3/folder/PreviewItemManager.java
index b39e968e..871643e 100644
--- a/src/com/android/launcher3/folder/PreviewItemManager.java
+++ b/src/com/android/launcher3/folder/PreviewItemManager.java
@@ -37,6 +37,7 @@
 import android.view.View;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
 
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.Utilities;
@@ -71,7 +72,8 @@
 
     private final Context mContext;
     private final FolderIcon mIcon;
-    private final int mIconSize;
+    @VisibleForTesting
+    public final int mIconSize;
 
     // These variables are all associated with the drawing of the preview; they are stored
     // as member variables for shared usage and to avoid computation on each frame
@@ -428,7 +430,8 @@
         p.anim = anim;
     }
 
-    private void setDrawable(PreviewItemDrawingParams p, WorkspaceItemInfo item) {
+    @VisibleForTesting
+    public void setDrawable(PreviewItemDrawingParams p, WorkspaceItemInfo item) {
         if (item.hasPromiseIconUi() || (item.runtimeStatusFlags
                     & ItemInfoWithIcon.FLAG_SHOW_DOWNLOAD_PROGRESS_MASK) != 0) {
             PreloadIconDrawable drawable = newPendingIcon(mContext, item);
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index 6651fa0..0dc0d02 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -701,6 +701,9 @@
         @UiEvent(doc = "User tapped private space settings button")
         LAUNCHER_PRIVATE_SPACE_SETTINGS_TAP(1550),
 
+        @UiEvent(doc = "User tapped on install to private space system shortcut.")
+        LAUNCHER_PRIVATE_SPACE_INSTALL_SYSTEM_SHORTCUT_TAP(1565),
+
         // ADD MORE
         ;
 
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 7b9f6fa..795450a 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -17,18 +17,11 @@
 package com.android.launcher3.model;
 
 import static com.android.launcher3.BuildConfig.WIDGET_ON_FIRST_SCREEN;
-import static com.android.launcher3.Flags.enableSupportForArchiving;
-import static com.android.launcher3.Flags.enableLauncherBrMetrics;
+import static com.android.launcher3.Flags.enableLauncherBrMetricsFixed;
 import static com.android.launcher3.LauncherPrefs.IS_FIRST_LOAD_AFTER_RESTORE;
 import static com.android.launcher3.LauncherPrefs.SHOULD_SHOW_SMARTSPACE;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR;
 import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME;
-import static com.android.launcher3.backuprestore.LauncherRestoreEventLogger.RESTORE_ERROR_APP_NOT_INSTALLED;
-import static com.android.launcher3.backuprestore.LauncherRestoreEventLogger.RESTORE_ERROR_INVALID_LOCATION;
-import static com.android.launcher3.backuprestore.LauncherRestoreEventLogger.RESTORE_ERROR_MISSING_INFO;
-import static com.android.launcher3.backuprestore.LauncherRestoreEventLogger.RESTORE_ERROR_PROFILE_DELETED;
-import static com.android.launcher3.backuprestore.LauncherRestoreEventLogger.RESTORE_ERROR_SHORTCUT_NOT_FOUND;
-import static com.android.launcher3.backuprestore.LauncherRestoreEventLogger.RESTORE_ERROR_WIDGETS_DISABLED;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_SMARTSPACE_REMOVAL;
 import static com.android.launcher3.config.FeatureFlags.SMARTSPACE_AS_A_WIDGET;
 import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_HAS_SHORTCUT_PERMISSION;
@@ -37,16 +30,10 @@
 import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED;
 import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_WORK_PROFILE_QUIET_MODE_ENABLED;
 import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems;
-import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER;
-import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_SAFEMODE;
-import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED;
 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
 import static com.android.launcher3.util.PackageManagerHelper.hasShortcutsPermission;
-import static com.android.launcher3.util.PackageManagerHelper.isSystemApp;
 
-import android.annotation.SuppressLint;
 import android.appwidget.AppWidgetProviderInfo;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -56,12 +43,10 @@
 import android.content.pm.PackageInstaller.SessionInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ShortcutInfo;
-import android.graphics.Point;
 import android.os.Bundle;
 import android.os.Trace;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.LongSparseArray;
@@ -69,10 +54,9 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
+import androidx.annotation.WorkerThread;
 
-import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Flags;
-import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherModel;
 import com.android.launcher3.LauncherPrefs;
@@ -95,13 +79,11 @@
 import com.android.launcher3.model.data.FolderInfo;
 import com.android.launcher3.model.data.IconRequestInfo;
 import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.pm.InstallSessionHelper;
 import com.android.launcher3.pm.PackageInstallInfo;
 import com.android.launcher3.pm.UserCache;
-import com.android.launcher3.qsb.QsbContainerView;
 import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.shortcuts.ShortcutRequest;
 import com.android.launcher3.shortcuts.ShortcutRequest.QueryResult;
@@ -113,9 +95,7 @@
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.util.TraceHelper;
-import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.widget.WidgetManagerHelper;
-import com.android.launcher3.widget.custom.CustomWidgetManager;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -123,6 +103,7 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.CancellationException;
 
@@ -185,7 +166,7 @@
         mLauncherBinder = launcherBinder;
         mLauncherApps = mApp.getContext().getSystemService(LauncherApps.class);
         mUserManager = mApp.getContext().getSystemService(UserManager.class);
-        mUserCache = UserCache.getInstance(mApp.getContext());
+        mUserCache = UserCache.INSTANCE.get(mApp.getContext());
         mSessionHelper = InstallSessionHelper.INSTANCE.get(mApp.getContext());
         mIconCache = mApp.getIconCache();
         mUserManagerState = userManagerState;
@@ -234,7 +215,7 @@
         mIsRestoreFromBackup =
                 (Boolean) LauncherPrefs.get(mApp.getContext()).get(IS_FIRST_LOAD_AFTER_RESTORE);
         LauncherRestoreEventLogger restoreEventLogger = null;
-        if (enableLauncherBrMetrics()) {
+        if (enableLauncherBrMetricsFixed()) {
             restoreEventLogger = LauncherRestoreEventLogger.Companion
                     .newInstance(mApp.getContext());
         }
@@ -247,7 +228,7 @@
             // sanitizeData should not be invoked if the workspace is loaded from a db different
             // from the main db as defined in the invariant device profile.
             // (e.g. both grid preview and minimal device mode uses a different db)
-            if (mApp.getInvariantDeviceProfile().dbFile.equals(mDbName)) {
+            if (Objects.equals(mApp.getInvariantDeviceProfile().dbFile, mDbName)) {
                 verifyNotStopped();
                 sanitizeFolders(mItemsDeleted);
                 sanitizeWidgetsShortcutsAndPackages();
@@ -452,43 +433,19 @@
             mDbName = extras == null ? null : extras.getString(ModelDbController.EXTRA_DB_NAME);
             try {
                 final LongSparseArray<Boolean> unlockedUsers = new LongSparseArray<>();
-
-                mUserManagerState.init(mUserCache, mUserManager);
-
-                for (UserHandle user : mUserCache.getUserProfiles()) {
-                    long serialNo = mUserCache.getSerialNumberForUser(user);
-                    boolean userUnlocked = mUserManager.isUserUnlocked(user);
-
-                    // We can only query for shortcuts when the user is unlocked.
-                    if (userUnlocked) {
-                        QueryResult pinnedShortcuts = new ShortcutRequest(context, user)
-                                .query(ShortcutRequest.PINNED);
-                        if (pinnedShortcuts.wasSuccess()) {
-                            for (ShortcutInfo shortcut : pinnedShortcuts) {
-                                mShortcutKeyToPinnedShortcuts.put(ShortcutKey.fromInfo(shortcut),
-                                        shortcut);
-                            }
-                            if (pinnedShortcuts.isEmpty()) {
-                                FileLog.d(TAG, "No pinned shortcuts found for user " + user);
-                            }
-                        } else {
-                            // Shortcut manager can fail due to some race condition when the
-                            // lock state changes too frequently. For the purpose of the loading
-                            // shortcuts, consider the user is still locked.
-                            FileLog.d(TAG, "Shortcut request failed for user "
-                                    + user + ", user may still be locked.");
-                            userUnlocked = false;
-                        }
-                    }
-                    unlockedUsers.put(serialNo, userUnlocked);
-                }
+                queryPinnedShortcutsForUnlockedUsers(context, unlockedUsers);
 
                 List<IconRequestInfo<WorkspaceItemInfo>> iconRequestInfos = new ArrayList<>();
 
+                WorkspaceItemProcessor itemProcessor = new WorkspaceItemProcessor(c, memoryLogger,
+                        restoreEventLogger, mUserManagerState, mLauncherApps, mPendingPackages,
+                        mShortcutKeyToPinnedShortcuts, mApp, mIconCache, mBgDataModel,
+                        mWidgetProvidersMap, mIsRestoreFromBackup, installingPkgs, isSdCardReady,
+                        tempPackageKey, widgetHelper, pmHelper, iconRequestInfos, unlockedUsers,
+                        isSafeMode, allDeepShortcuts);
+
                 while (!mStopped && c.moveToNext()) {
-                    processWorkspaceItem(c, memoryLogger, restoreEventLogger, installingPkgs,
-                            isSdCardReady, tempPackageKey, widgetHelper, pmHelper,
-                            iconRequestInfos, unlockedUsers, isSafeMode, allDeepShortcuts);
+                    itemProcessor.processItem();
                 }
                 tryLoadWorkspaceIconsInBulk(iconRequestInfos);
             } finally {
@@ -513,502 +470,85 @@
             // Remove dead items
             mItemsDeleted = c.commitDeleted();
 
-            // Sort the folder items, update ranks, and make sure all preview items are high res.
-            List<FolderGridOrganizer> verifiers =
-                    mApp.getInvariantDeviceProfile().supportedProfiles.stream().map(
-                            FolderGridOrganizer::new).toList();
-            for (FolderInfo folder : mBgDataModel.folders) {
-                Collections.sort(folder.contents, Folder.ITEM_POS_COMPARATOR);
-                verifiers.forEach(verifier -> verifier.setFolderInfo(folder));
-                int size = folder.contents.size();
-
-                // Update ranks here to ensure there are no gaps caused by removed folder items.
-                // Ranks are the source of truth for folder items, so cellX and cellY can be
-                // ignored for now. Database will be updated once user manually modifies folder.
-                for (int rank = 0; rank < size; ++rank) {
-                    WorkspaceItemInfo info = folder.contents.get(rank);
-                    // rank is used differently in app pairs, so don't reset
-                    if (folder.itemType != ITEM_TYPE_APP_PAIR) {
-                        info.rank = rank;
-                    }
-
-                    if (info.usingLowResIcon() && info.itemType == Favorites.ITEM_TYPE_APPLICATION
-                            && verifiers.stream().anyMatch(
-                                verifier -> verifier.isItemInPreview(info.rank))) {
-                        mIconCache.getTitleAndIcon(info, false);
-                    }
-                }
-            }
+            processFolderItems();
 
             c.commitRestoredItems();
         }
     }
 
-    private void processWorkspaceItem(LoaderCursor c,
-            LoaderMemoryLogger memoryLogger,
-            @Nullable LauncherRestoreEventLogger restoreEventLogger,
-            HashMap<PackageUserKey, SessionInfo> installingPkgs,
-            boolean isSdCardReady,
-            PackageUserKey tempPackageKey,
-            WidgetManagerHelper widgetHelper,
-            PackageManagerHelper pmHelper,
-            List<IconRequestInfo<WorkspaceItemInfo>> iconRequestInfos,
-            LongSparseArray<Boolean> unlockedUsers,
-            boolean isSafeMode,
-            List<ShortcutInfo> allDeepShortcuts) {
+    /**
+     * Initialized the UserManagerState, and determines which users are unlocked. Additionally, if
+     * the user is unlocked, it queries LauncherAppsService for pinned shortcuts and stores the
+     * result in a class variable to be used in other methods while processing workspace items.
+     *
+     * @param context used to query LauncherAppsService
+     * @param unlockedUsers this param is changed, and the updated value is used outside this method
+     */
+    @WorkerThread
+    private void queryPinnedShortcutsForUnlockedUsers(Context context,
+            LongSparseArray<Boolean> unlockedUsers) {
+        mUserManagerState.init(mUserCache, mUserManager);
 
-        try {
-            if (c.user == null) {
-                // User has been deleted, remove the item.
-                c.markDeleted("User of this item has been deleted");
-                if (mIsRestoreFromBackup && restoreEventLogger != null) {
-                    restoreEventLogger.logSingleFavoritesItemRestoreFailed(
-                            c.itemType, RESTORE_ERROR_PROFILE_DELETED);
+        for (UserHandle user : mUserCache.getUserProfiles()) {
+            long serialNo = mUserCache.getSerialNumberForUser(user);
+            boolean userUnlocked = mUserManager.isUserUnlocked(user);
+
+            // We can only query for shortcuts when the user is unlocked.
+            if (userUnlocked) {
+                QueryResult pinnedShortcuts = new ShortcutRequest(context, user)
+                        .query(ShortcutRequest.PINNED);
+                if (pinnedShortcuts.wasSuccess()) {
+                    for (ShortcutInfo shortcut : pinnedShortcuts) {
+                        mShortcutKeyToPinnedShortcuts.put(ShortcutKey.fromInfo(shortcut),
+                                shortcut);
+                    }
+                    if (pinnedShortcuts.isEmpty()) {
+                        FileLog.d(TAG, "No pinned shortcuts found for user " + user);
+                    }
+                } else {
+                    // Shortcut manager can fail due to some race condition when the
+                    // lock state changes too frequently. For the purpose of the loading
+                    // shortcuts, consider the user is still locked.
+                    FileLog.d(TAG, "Shortcut request failed for user "
+                            + user + ", user may still be locked.");
+                    userUnlocked = false;
                 }
-                return;
             }
+            unlockedUsers.put(serialNo, userUnlocked);
+        }
 
-            boolean allowMissingTarget = false;
-            switch (c.itemType) {
-                case Favorites.ITEM_TYPE_APPLICATION:
-                case Favorites.ITEM_TYPE_DEEP_SHORTCUT:
-                    Intent intent = c.parseIntent();
-                    if (intent == null) {
-                        c.markDeleted("Invalid or null intent");
-                        if (mIsRestoreFromBackup && restoreEventLogger != null) {
-                            restoreEventLogger.logSingleFavoritesItemRestoreFailed(
-                                    c.itemType, RESTORE_ERROR_MISSING_INFO);
-                        }
-                        return;
-                    }
+    }
 
-                    int disabledState = mUserManagerState.isUserQuiet(c.serialNumber)
-                            ? WorkspaceItemInfo.FLAG_DISABLED_QUIET_USER : 0;
-                    ComponentName cn = intent.getComponent();
-                    String targetPkg = cn == null ? intent.getPackage() : cn.getPackageName();
+    /**
+     * After all items have been processed and added to the BgDataModel, this method can correctly
+     * rank items inside folders and load the correct miniature preview icons to be shown when the
+     * folder is collapsed.
+     */
+    @WorkerThread
+    private void processFolderItems() {
+        // Sort the folder items, update ranks, and make sure all preview items are high res.
+        List<FolderGridOrganizer> verifiers = mApp.getInvariantDeviceProfile().supportedProfiles
+                .stream().map(FolderGridOrganizer::new).toList();
+        for (FolderInfo folder : mBgDataModel.folders) {
+            Collections.sort(folder.contents, Folder.ITEM_POS_COMPARATOR);
+            verifiers.forEach(verifier -> verifier.setFolderInfo(folder));
+            int size = folder.contents.size();
 
-                    if (TextUtils.isEmpty(targetPkg)) {
-                        c.markDeleted("Shortcuts can't have null package");
-                        if (mIsRestoreFromBackup && restoreEventLogger != null) {
-                            restoreEventLogger.logSingleFavoritesItemRestoreFailed(
-                                    c.itemType, RESTORE_ERROR_MISSING_INFO);
-                        }
-                        return;
-                    }
+            // Update ranks here to ensure there are no gaps caused by removed folder items.
+            // Ranks are the source of truth for folder items, so cellX and cellY can be
+            // ignored for now. Database will be updated once user manually modifies folder.
+            for (int rank = 0; rank < size; ++rank) {
+                WorkspaceItemInfo info = folder.contents.get(rank);
+                // rank is used differently in app pairs, so don't reset
+                if (folder.itemType != ITEM_TYPE_APP_PAIR) {
+                    info.rank = rank;
+                }
 
-                    // If there is no target package, it's an implicit intent
-                    // (legacy shortcut) which is always valid
-                    boolean validTarget = TextUtils.isEmpty(targetPkg)
-                            || mLauncherApps.isPackageEnabled(targetPkg, c.user);
-
-                    // If it's a deep shortcut, we'll use pinned shortcuts to restore it
-                    if (cn != null && validTarget && c.itemType
-                            != Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
-                        // If the apk is present and the shortcut points to a specific component.
-
-                        // If the component is already present
-                        if (mLauncherApps.isActivityEnabled(cn, c.user)) {
-                            // no special handling necessary for this item
-                            c.markRestored();
-                            if (mIsRestoreFromBackup && restoreEventLogger != null) {
-                                restoreEventLogger.logSingleFavoritesItemRestored(c.itemType);
-                            }
-                        } else {
-                            // Gracefully try to find a fallback activity.
-                            intent = pmHelper.getAppLaunchIntent(targetPkg, c.user);
-                            if (intent != null) {
-                                c.restoreFlag = 0;
-                                c.updater().put(
-                                        Favorites.INTENT,
-                                        intent.toUri(0)).commit();
-                                cn = intent.getComponent();
-                            } else {
-                                c.markDeleted("Intent null, unable to find a launch target");
-                                if (mIsRestoreFromBackup && restoreEventLogger != null) {
-                                    restoreEventLogger.logSingleFavoritesItemRestoreFailed(
-                                            c.itemType, RESTORE_ERROR_MISSING_INFO);
-                                }
-                                return;
-                            }
-                        }
-                    }
-                    // else if cn == null => can't infer much, leave it
-                    // else if !validPkg => could be restored icon or missing sd-card
-
-                    if (!TextUtils.isEmpty(targetPkg) && !validTarget) {
-                        // Points to a valid app (superset of cn != null) but the apk
-                        // is not available.
-
-                        if (c.restoreFlag != 0) {
-                            // Package is not yet available but might be
-                            // installed later.
-                            FileLog.d(TAG, "package not yet restored: " + targetPkg);
-                            tempPackageKey.update(targetPkg, c.user);
-                            if (c.hasRestoreFlag(WorkspaceItemInfo.FLAG_RESTORE_STARTED)) {
-                                // Restore has started once.
-                            } else if (installingPkgs.containsKey(tempPackageKey)) {
-                                // App restore has started. Update the flag
-                                c.restoreFlag |= WorkspaceItemInfo.FLAG_RESTORE_STARTED;
-                                FileLog.d(TAG, "restore started for installing app: " + targetPkg);
-                                c.updater().put(Favorites.RESTORED, c.restoreFlag).commit();
-                            } else {
-                                c.markDeleted("removing app that is not restored and not "
-                                        + "installing. package: " + targetPkg);
-                                if (mIsRestoreFromBackup && restoreEventLogger != null) {
-                                    restoreEventLogger.logSingleFavoritesItemRestoreFailed(
-                                            c.itemType, RESTORE_ERROR_APP_NOT_INSTALLED);
-                                }
-                                return;
-                            }
-                        } else if (pmHelper.isAppOnSdcard(targetPkg, c.user)) {
-                            // Package is present but not available.
-                            disabledState |= WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE;
-                            // Add the icon on the workspace anyway.
-                            allowMissingTarget = true;
-                        } else if (!isSdCardReady) {
-                            // SdCard is not ready yet. Package might get available,
-                            // once it is ready.
-                            Log.d(TAG, "Missing package, will check later: " + targetPkg);
-                            mPendingPackages.add(new PackageUserKey(targetPkg, c.user));
-                            // Add the icon on the workspace anyway.
-                            allowMissingTarget = true;
-                        } else {
-                            // Do not wait for external media load anymore.
-                            c.markDeleted("Invalid package removed: " + targetPkg);
-                            if (mIsRestoreFromBackup && restoreEventLogger != null) {
-                                restoreEventLogger.logSingleFavoritesItemRestoreFailed(
-                                        c.itemType, RESTORE_ERROR_APP_NOT_INSTALLED);
-                            }
-                            return;
-                        }
-                    }
-
-                    if ((c.restoreFlag & WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI) != 0) {
-                        validTarget = false;
-                    }
-
-                    if (validTarget) {
-                        // The shortcut points to a valid target (either no target
-                        // or something which is ready to be used)
-                        c.markRestored();
-                    }
-
-                    boolean useLowResIcon = !c.isOnWorkspaceOrHotseat();
-
-                    WorkspaceItemInfo info;
-                    if (c.restoreFlag != 0) {
-                        // Already verified above that user is same as default user
-                        info = c.getRestoredItemInfo(intent);
-                    } else if (c.itemType == Favorites.ITEM_TYPE_APPLICATION) {
-                        info = c.getAppShortcutInfo(
-                                intent, allowMissingTarget, useLowResIcon, false);
-                    } else if (c.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
-                        ShortcutKey key = ShortcutKey.fromIntent(intent, c.user);
-                        if (unlockedUsers.get(c.serialNumber)) {
-                            ShortcutInfo pinnedShortcut = mShortcutKeyToPinnedShortcuts.get(key);
-                            if (pinnedShortcut == null) {
-                                // The shortcut is no longer valid.
-                                c.markDeleted("Pinned shortcut not found from request."
-                                        + " package=" + key.getPackageName() + ", user=" + c.user);
-                                if (mIsRestoreFromBackup && restoreEventLogger != null) {
-                                    restoreEventLogger.logSingleFavoritesItemRestoreFailed(
-                                            c.itemType, RESTORE_ERROR_SHORTCUT_NOT_FOUND);
-                                }
-                                return;
-                            }
-                            info = new WorkspaceItemInfo(pinnedShortcut, mApp.getContext());
-                            // If the pinned deep shortcut is no longer published,
-                            // use the last saved icon instead of the default.
-                            mIconCache.getShortcutIcon(info, pinnedShortcut, c::loadIcon);
-
-                            if (pmHelper.isAppSuspended(
-                                    pinnedShortcut.getPackage(), info.user)) {
-                                info.runtimeStatusFlags |= FLAG_DISABLED_SUSPENDED;
-                            }
-                            intent = info.getIntent();
-                            allDeepShortcuts.add(pinnedShortcut);
-                        } else {
-                            // Create a shortcut info in disabled mode for now.
-                            info = c.loadSimpleWorkspaceItem();
-                            info.runtimeStatusFlags |= FLAG_DISABLED_LOCKED_USER;
-                        }
-                        if (mIsRestoreFromBackup && restoreEventLogger != null) {
-                            restoreEventLogger.logSingleFavoritesItemRestored(c.itemType);
-                        }
-                    } else { // item type == ITEM_TYPE_SHORTCUT
-                        info = c.loadSimpleWorkspaceItem();
-
-                        // Shortcuts are only available on the primary profile
-                        if (!TextUtils.isEmpty(targetPkg)
-                                && pmHelper.isAppSuspended(targetPkg, c.user)) {
-                            disabledState |= FLAG_DISABLED_SUSPENDED;
-                        }
-                        info.options = c.getOptions();
-
-                        // App shortcuts that used to be automatically added to Launcher
-                        // didn't always have the correct intent flags set, so do that here
-                        if (intent.getAction() != null
-                                && intent.getCategories() != null
-                                && intent.getAction().equals(Intent.ACTION_MAIN)
-                                && intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) {
-                            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
-                                    | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
-                        }
-                    }
-
-                    if (info != null) {
-                        if (info.itemType != Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
-                            // Skip deep shortcuts; their title and icons have already been
-                            // loaded above.
-                            iconRequestInfos.add(c.createIconRequestInfo(info, useLowResIcon));
-                        }
-
-                        c.applyCommonProperties(info);
-
-                        info.intent = intent;
-                        info.rank = c.getRank();
-                        info.spanX = 1;
-                        info.spanY = 1;
-                        info.runtimeStatusFlags |= disabledState;
-                        if (isSafeMode && !isSystemApp(mApp.getContext(), intent)) {
-                            info.runtimeStatusFlags |= FLAG_DISABLED_SAFEMODE;
-                        }
-                        LauncherActivityInfo activityInfo = c.getLauncherActivityInfo();
-                        if (activityInfo != null) {
-                            info.setProgressLevel(
-                                    PackageManagerHelper.getLoadingProgress(activityInfo),
-                                    PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING);
-                        }
-
-                        if ((c.restoreFlag != 0
-                                || (enableSupportForArchiving()
-                                && activityInfo != null
-                                && activityInfo.getApplicationInfo().isArchived))
-                                && !TextUtils.isEmpty(targetPkg)) {
-                            tempPackageKey.update(targetPkg, c.user);
-                            SessionInfo si = installingPkgs.get(tempPackageKey);
-                            if (si == null) {
-                                info.runtimeStatusFlags
-                                        &= ~ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE;
-                            } else if (activityInfo == null
-                                    // For archived apps, include progress info in case there is
-                                    // a pending install session post restart of device.
-                                    || (enableSupportForArchiving()
-                                    && activityInfo.getApplicationInfo().isArchived)) {
-                                int installProgress = (int) (si.getProgress() * 100);
-
-                                info.setProgressLevel(installProgress,
-                                        PackageInstallInfo.STATUS_INSTALLING);
-                            }
-                        }
-
-                        c.checkAndAddItem(info, mBgDataModel, memoryLogger);
-                    } else {
-                        throw new RuntimeException("Unexpected null WorkspaceItemInfo");
-                    }
-                    break;
-
-                case Favorites.ITEM_TYPE_FOLDER:
-                case Favorites.ITEM_TYPE_APP_PAIR:
-                    FolderInfo folderInfo = mBgDataModel.findOrMakeFolder(c.id);
-                    c.applyCommonProperties(folderInfo);
-
-                    folderInfo.itemType = c.itemType;
-                    // Do not trim the folder label, as is was set by the user.
-                    folderInfo.title = c.getString(c.mTitleIndex);
-                    folderInfo.spanX = 1;
-                    folderInfo.spanY = 1;
-                    folderInfo.options = c.getOptions();
-
-                    // no special handling required for restored folders
-                    c.markRestored();
-                    if (mIsRestoreFromBackup && restoreEventLogger != null) {
-                        restoreEventLogger.logSingleFavoritesItemRestored(c.itemType);
-                    }
-                    c.checkAndAddItem(folderInfo, mBgDataModel, memoryLogger);
-                    break;
-
-                case Favorites.ITEM_TYPE_APPWIDGET:
-                    if (WidgetsModel.GO_DISABLE_WIDGETS) {
-                        c.markDeleted("Only legacy shortcuts can have null package");
-                        if (mIsRestoreFromBackup && restoreEventLogger != null) {
-                            restoreEventLogger.logSingleFavoritesItemRestoreFailed(
-                                    c.itemType, RESTORE_ERROR_WIDGETS_DISABLED);
-                        }
-                        return;
-                    }
-                    // Follow through
-                case Favorites.ITEM_TYPE_CUSTOM_APPWIDGET:
-                    // Read all Launcher-specific widget details
-                    boolean customWidget = c.itemType
-                            == Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
-
-                    int appWidgetId = c.getAppWidgetId();
-                    String savedProvider = c.getAppWidgetProvider();
-                    final ComponentName component;
-
-                    if ((c.getOptions() & LauncherAppWidgetInfo.OPTION_SEARCH_WIDGET) != 0) {
-                        component  = QsbContainerView.getSearchComponentName(mApp.getContext());
-                        if (component == null) {
-                            c.markDeleted("Discarding SearchWidget without packagename ");
-                            if (mIsRestoreFromBackup && restoreEventLogger != null) {
-                                restoreEventLogger.logSingleFavoritesItemRestoreFailed(
-                                        c.itemType, RESTORE_ERROR_MISSING_INFO);
-                            }
-                            return;
-                        }
-                    } else {
-                        component = ComponentName.unflattenFromString(savedProvider);
-                    }
-                    final boolean isIdValid =
-                            !c.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID);
-                    final boolean wasProviderReady =
-                            !c.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY);
-
-                    ComponentKey providerKey = new ComponentKey(component, c.user);
-                    if (!mWidgetProvidersMap.containsKey(providerKey)) {
-                        if (customWidget) {
-                            mWidgetProvidersMap.put(providerKey, CustomWidgetManager.INSTANCE
-                                    .get(mApp.getContext()).getWidgetProvider(component));
-                        } else {
-                            mWidgetProvidersMap.put(providerKey,
-                                    widgetHelper.findProvider(component, c.user));
-                        }
-                    }
-                    final AppWidgetProviderInfo provider = mWidgetProvidersMap.get(providerKey);
-
-                    final boolean isProviderReady = isValidProvider(provider);
-                    if (!isSafeMode && !customWidget && wasProviderReady && !isProviderReady) {
-                        c.markDeleted("Deleting widget that isn't installed anymore: " + provider);
-                        if (mIsRestoreFromBackup && restoreEventLogger != null) {
-                            restoreEventLogger.logSingleFavoritesItemRestoreFailed(
-                                    c.itemType, RESTORE_ERROR_APP_NOT_INSTALLED);
-                        }
-                    } else {
-                        LauncherAppWidgetInfo appWidgetInfo;
-                        if (isProviderReady) {
-                            appWidgetInfo =
-                                    new LauncherAppWidgetInfo(appWidgetId, provider.provider);
-
-                            // The provider is available. So the widget is either
-                            // available or not available. We do not need to track
-                            // any future restore updates.
-                            int status = c.restoreFlag
-                                    & ~LauncherAppWidgetInfo.FLAG_RESTORE_STARTED
-                                    & ~LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY;
-                            if (!wasProviderReady) {
-                                // If provider was not previously ready, update status and UI flag.
-
-                                // Id would be valid only if the widget restore broadcast received.
-                                if (isIdValid) {
-                                    status |= LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
-                                }
-                            }
-                            appWidgetInfo.restoreStatus = status;
-                        } else {
-                            Log.v(TAG, "Widget restore pending id=" + c.id
-                                    + " appWidgetId=" + appWidgetId
-                                    + " status=" + c.restoreFlag);
-                            appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId, component);
-                            appWidgetInfo.restoreStatus = c.restoreFlag;
-
-                            tempPackageKey.update(component.getPackageName(), c.user);
-                            SessionInfo si = installingPkgs.get(tempPackageKey);
-                            Integer installProgress = si == null
-                                    ? null
-                                    : (int) (si.getProgress() * 100);
-
-                            if (c.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_RESTORE_STARTED)) {
-                                // Restore has started once.
-                            } else if (installProgress != null) {
-                                // App restore has started. Update the flag
-                                appWidgetInfo.restoreStatus
-                                        |= LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
-                            } else if (!isSafeMode) {
-                                c.markDeleted("Unrestored widget removed: " + component);
-                                if (mIsRestoreFromBackup && restoreEventLogger != null) {
-                                    restoreEventLogger.logSingleFavoritesItemRestoreFailed(
-                                            c.itemType, RESTORE_ERROR_APP_NOT_INSTALLED);
-                                }
-                                return;
-                            }
-
-                            appWidgetInfo.installProgress =
-                                    installProgress == null ? 0 : installProgress;
-                        }
-                        if (appWidgetInfo.hasRestoreFlag(
-                                LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG)) {
-                            appWidgetInfo.bindOptions = c.parseIntent();
-                        }
-
-                        c.applyCommonProperties(appWidgetInfo);
-                        appWidgetInfo.spanX = c.getSpanX();
-                        appWidgetInfo.spanY = c.getSpanY();
-                        appWidgetInfo.options = c.getOptions();
-                        appWidgetInfo.user = c.user;
-                        appWidgetInfo.sourceContainer = c.getAppWidgetSource();
-
-                        if (appWidgetInfo.spanX <= 0 || appWidgetInfo.spanY <= 0) {
-                            c.markDeleted("Widget has invalid size: "
-                                    + appWidgetInfo.spanX + "x" + appWidgetInfo.spanY);
-                            if (mIsRestoreFromBackup && restoreEventLogger != null) {
-                                restoreEventLogger.logSingleFavoritesItemRestoreFailed(
-                                        c.itemType, RESTORE_ERROR_INVALID_LOCATION);
-                            }
-                            return;
-                        }
-                        LauncherAppWidgetProviderInfo widgetProviderInfo =
-                                widgetHelper.getLauncherAppWidgetInfo(appWidgetId,
-                                        appWidgetInfo.getTargetComponent());
-                        if (widgetProviderInfo != null
-                                && (appWidgetInfo.spanX < widgetProviderInfo.minSpanX
-                                || appWidgetInfo.spanY < widgetProviderInfo.minSpanY)) {
-                            FileLog.d(TAG, "Widget " + widgetProviderInfo.getComponent()
-                                    + " minSizes not meet: span=" + appWidgetInfo.spanX
-                                    + "x" + appWidgetInfo.spanY + " minSpan="
-                                    + widgetProviderInfo.minSpanX + "x"
-                                    + widgetProviderInfo.minSpanY);
-                            logWidgetInfo(mApp.getInvariantDeviceProfile(), widgetProviderInfo);
-                        }
-                        if (!c.isOnWorkspaceOrHotseat()) {
-                            c.markDeleted("Widget found where container != CONTAINER_DESKTOP"
-                                    + "nor CONTAINER_HOTSEAT - ignoring!");
-                            if (mIsRestoreFromBackup && restoreEventLogger != null) {
-                                restoreEventLogger.logSingleFavoritesItemRestoreFailed(
-                                        c.itemType, RESTORE_ERROR_INVALID_LOCATION);
-                            }
-                            return;
-                        }
-
-                        if (!customWidget) {
-                            String providerName = appWidgetInfo.providerName.flattenToString();
-                            if (!providerName.equals(savedProvider)
-                                    || (appWidgetInfo.restoreStatus != c.restoreFlag)) {
-                                c.updater()
-                                        .put(Favorites.APPWIDGET_PROVIDER,
-                                                providerName)
-                                        .put(Favorites.RESTORED,
-                                                appWidgetInfo.restoreStatus)
-                                        .commit();
-                            }
-                        }
-
-                        if (appWidgetInfo.restoreStatus
-                                != LauncherAppWidgetInfo.RESTORE_COMPLETED) {
-                            appWidgetInfo.pendingItemInfo = WidgetsModel.newPendingItemInfo(
-                                    mApp.getContext(),
-                                    appWidgetInfo.providerName,
-                                    appWidgetInfo.user);
-                            mIconCache.getTitleAndIconForApp(
-                                    appWidgetInfo.pendingItemInfo, false);
-                        }
-
-                        c.checkAndAddItem(appWidgetInfo, mBgDataModel);
-                    }
-                    break;
+                if (info.usingLowResIcon() && info.itemType == Favorites.ITEM_TYPE_APPLICATION
+                        && verifiers.stream().anyMatch(it -> it.isItemInPreview(info.rank))) {
+                    mIconCache.getTitleAndIcon(info, false);
+                }
             }
-        } catch (Exception e) {
-            Log.e(TAG, "Desktop items loading interrupted", e);
         }
     }
 
@@ -1206,52 +746,6 @@
                 && (provider.provider.getPackageName() != null);
     }
 
-    @SuppressLint("NewApi") // Already added API check.
-    private static void logWidgetInfo(InvariantDeviceProfile idp,
-            LauncherAppWidgetProviderInfo widgetProviderInfo) {
-        Point cellSize = new Point();
-        for (DeviceProfile deviceProfile : idp.supportedProfiles) {
-            deviceProfile.getCellSize(cellSize);
-            FileLog.d(TAG, "DeviceProfile available width: " + deviceProfile.availableWidthPx
-                    + ", available height: " + deviceProfile.availableHeightPx
-                    + ", cellLayoutBorderSpacePx Horizontal: "
-                    + deviceProfile.cellLayoutBorderSpacePx.x
-                    + ", cellLayoutBorderSpacePx Vertical: "
-                    + deviceProfile.cellLayoutBorderSpacePx.y
-                    + ", cellSize: " + cellSize);
-        }
-
-        StringBuilder widgetDimension = new StringBuilder();
-        widgetDimension.append("Widget dimensions:\n")
-                .append("minResizeWidth: ")
-                .append(widgetProviderInfo.minResizeWidth)
-                .append("\n")
-                .append("minResizeHeight: ")
-                .append(widgetProviderInfo.minResizeHeight)
-                .append("\n")
-                .append("defaultWidth: ")
-                .append(widgetProviderInfo.minWidth)
-                .append("\n")
-                .append("defaultHeight: ")
-                .append(widgetProviderInfo.minHeight)
-                .append("\n");
-        if (Utilities.ATLEAST_S) {
-            widgetDimension.append("targetCellWidth: ")
-                    .append(widgetProviderInfo.targetCellWidth)
-                    .append("\n")
-                    .append("targetCellHeight: ")
-                    .append(widgetProviderInfo.targetCellHeight)
-                    .append("\n")
-                    .append("maxResizeWidth: ")
-                    .append(widgetProviderInfo.maxResizeWidth)
-                    .append("\n")
-                    .append("maxResizeHeight: ")
-                    .append(widgetProviderInfo.maxResizeHeight)
-                    .append("\n");
-        }
-        FileLog.d(TAG, widgetDimension.toString());
-    }
-
     private static void logASplit(String label) {
         if (DEBUG) {
             Log.d(TAG, label);
diff --git a/src/com/android/launcher3/model/ModelLauncherCallbacks.kt b/src/com/android/launcher3/model/ModelLauncherCallbacks.kt
new file mode 100644
index 0000000..b12b2bc
--- /dev/null
+++ b/src/com/android/launcher3/model/ModelLauncherCallbacks.kt
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2024 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.model
+
+import android.content.pm.LauncherApps
+import android.content.pm.ShortcutInfo
+import android.os.UserHandle
+import android.text.TextUtils
+import com.android.launcher3.LauncherModel.ModelUpdateTask
+import com.android.launcher3.logging.FileLog
+import com.android.launcher3.model.PackageUpdatedTask.OP_ADD
+import com.android.launcher3.model.PackageUpdatedTask.OP_REMOVE
+import com.android.launcher3.model.PackageUpdatedTask.OP_SUSPEND
+import com.android.launcher3.model.PackageUpdatedTask.OP_UNAVAILABLE
+import com.android.launcher3.model.PackageUpdatedTask.OP_UNSUSPEND
+import com.android.launcher3.model.PackageUpdatedTask.OP_UPDATE
+import java.util.function.Consumer
+
+/**
+ * Implementation of {@link LauncherApps#Callbacks} which converts various events to corresponding
+ * model tasks
+ */
+class ModelLauncherCallbacks(private var taskExecutor: Consumer<ModelUpdateTask>) :
+    LauncherApps.Callback() {
+
+    override fun onPackageAdded(packageName: String, user: UserHandle) {
+        taskExecutor.accept(PackageUpdatedTask(OP_ADD, user, packageName))
+    }
+
+    override fun onPackageChanged(packageName: String, user: UserHandle) {
+        taskExecutor.accept(PackageUpdatedTask(OP_UPDATE, user, packageName))
+    }
+
+    override fun onPackageLoadingProgressChanged(
+        packageName: String,
+        user: UserHandle,
+        progress: Float
+    ) {
+        taskExecutor.accept(PackageIncrementalDownloadUpdatedTask(packageName, user, progress))
+    }
+
+    override fun onPackageRemoved(packageName: String, user: UserHandle) {
+        FileLog.d(TAG, "package removed received $packageName")
+        taskExecutor.accept(PackageUpdatedTask(OP_REMOVE, user, packageName))
+    }
+
+    override fun onPackagesAvailable(
+        vararg packageNames: String,
+        user: UserHandle,
+        replacing: Boolean
+    ) {
+        taskExecutor.accept(PackageUpdatedTask(OP_UPDATE, user, *packageNames))
+    }
+
+    override fun onPackagesSuspended(vararg packageNames: String, user: UserHandle) {
+        taskExecutor.accept(PackageUpdatedTask(OP_SUSPEND, user, *packageNames))
+    }
+
+    override fun onPackagesUnavailable(
+        packageNames: Array<String>,
+        user: UserHandle,
+        replacing: Boolean
+    ) {
+        if (!replacing) {
+            taskExecutor.accept(PackageUpdatedTask(OP_UNAVAILABLE, user, *packageNames))
+        }
+    }
+
+    override fun onPackagesUnsuspended(vararg packageNames: String, user: UserHandle) {
+        taskExecutor.accept(PackageUpdatedTask(OP_UNSUSPEND, user, *packageNames))
+    }
+
+    override fun onShortcutsChanged(
+        packageName: String,
+        shortcuts: MutableList<ShortcutInfo>,
+        user: UserHandle
+    ) {
+        taskExecutor.accept(ShortcutsChangedTask(packageName, shortcuts, user, true))
+    }
+
+    fun onPackagesRemoved(user: UserHandle, packages: List<String>) {
+        FileLog.d(TAG, "package removed received " + TextUtils.join(",", packages))
+        taskExecutor.accept(PackageUpdatedTask(OP_REMOVE, user, *packages.toTypedArray()))
+    }
+
+    companion object {
+        private const val TAG = "LauncherAppsCallbackImpl"
+    }
+}
diff --git a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
index 76a87ed..2457a42 100644
--- a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
+++ b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
@@ -52,7 +52,8 @@
                 ApplicationInfo ai = app.getContext()
                         .getPackageManager().getApplicationInfo(mInstallInfo.packageName, 0);
                 if (InstantAppResolver.newInstance(app.getContext()).isInstantApp(ai)) {
-                    app.getModel().onPackageAdded(ai.packageName, mInstallInfo.user);
+                    app.getModel().newModelCallbacks()
+                            .onPackageAdded(ai.packageName, mInstallInfo.user);
                 }
             } catch (PackageManager.NameNotFoundException e) {
                 // Ignore
diff --git a/src/com/android/launcher3/model/SdCardAvailableReceiver.java b/src/com/android/launcher3/model/SdCardAvailableReceiver.java
index 3798575..8cfa3aa 100644
--- a/src/com/android/launcher3/model/SdCardAvailableReceiver.java
+++ b/src/com/android/launcher3/model/SdCardAvailableReceiver.java
@@ -67,11 +67,10 @@
                 }
             }
             if (!packagesRemoved.isEmpty()) {
-                mModel.onPackagesRemoved(user,
-                        packagesRemoved.toArray(new String[packagesRemoved.size()]));
+                mModel.newModelCallbacks().onPackagesRemoved(user, packagesRemoved);
             }
             if (!packagesUnavailable.isEmpty()) {
-                mModel.onPackagesUnavailable(
+                mModel.newModelCallbacks().onPackagesUnavailable(
                         packagesUnavailable.toArray(new String[packagesUnavailable.size()]),
                         user, false);
             }
diff --git a/src/com/android/launcher3/model/WorkspaceItemProcessor.kt b/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
new file mode 100644
index 0000000..5389d38
--- /dev/null
+++ b/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
@@ -0,0 +1,672 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.model
+
+import android.annotation.SuppressLint
+import android.appwidget.AppWidgetProviderInfo
+import android.content.ComponentName
+import android.content.Intent
+import android.content.pm.LauncherApps
+import android.content.pm.PackageInstaller
+import android.content.pm.ShortcutInfo
+import android.graphics.Point
+import android.text.TextUtils
+import android.util.Log
+import android.util.LongSparseArray
+import androidx.annotation.VisibleForTesting
+import com.android.launcher3.Flags
+import com.android.launcher3.InvariantDeviceProfile
+import com.android.launcher3.LauncherAppState
+import com.android.launcher3.LauncherSettings.Favorites
+import com.android.launcher3.Utilities
+import com.android.launcher3.backuprestore.LauncherRestoreEventLogger
+import com.android.launcher3.icons.IconCache
+import com.android.launcher3.logging.FileLog
+import com.android.launcher3.model.data.IconRequestInfo
+import com.android.launcher3.model.data.ItemInfoWithIcon
+import com.android.launcher3.model.data.LauncherAppWidgetInfo
+import com.android.launcher3.model.data.WorkspaceItemInfo
+import com.android.launcher3.pm.PackageInstallInfo
+import com.android.launcher3.qsb.QsbContainerView
+import com.android.launcher3.shortcuts.ShortcutKey
+import com.android.launcher3.util.ComponentKey
+import com.android.launcher3.util.PackageManagerHelper
+import com.android.launcher3.util.PackageUserKey
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo
+import com.android.launcher3.widget.WidgetManagerHelper
+import com.android.launcher3.widget.custom.CustomWidgetManager
+
+/**
+ * This items is used by LoaderTask to process items that have been loaded from the Launcher's DB.
+ * This data, stored in the Favorites table, needs to be processed in order to be shown on the Home
+ * Page.
+ *
+ * This class processes each of those items: App Shortcuts, Widgets, Folders, etc., one at a time.
+ */
+class WorkspaceItemProcessor(
+    private val c: LoaderCursor,
+    private val memoryLogger: LoaderMemoryLogger?,
+    private val restoreEventLogger: LauncherRestoreEventLogger?,
+    private val userManagerState: UserManagerState,
+    private val launcherApps: LauncherApps,
+    private val pendingPackages: MutableSet<PackageUserKey>,
+    private val shortcutKeyToPinnedShortcuts: Map<ShortcutKey, ShortcutInfo>,
+    private val app: LauncherAppState,
+    private val iconCache: IconCache,
+    private val bgDataModel: BgDataModel,
+    private val widgetProvidersMap: MutableMap<ComponentKey, AppWidgetProviderInfo?>,
+    private val isRestoreFromBackup: Boolean,
+    private val installingPkgs: HashMap<PackageUserKey, PackageInstaller.SessionInfo>,
+    private val isSdCardReady: Boolean,
+    private val tempPackageKey: PackageUserKey,
+    private val widgetHelper: WidgetManagerHelper,
+    private val pmHelper: PackageManagerHelper,
+    private val iconRequestInfos: MutableList<IconRequestInfo<WorkspaceItemInfo>>,
+    private val unlockedUsers: LongSparseArray<Boolean>,
+    private val isSafeMode: Boolean,
+    private val allDeepShortcuts: MutableList<ShortcutInfo>
+) {
+    /**
+     * This is the entry point for processing 1 workspace item. This method is like the midfielder
+     * that delegates the actual processing to either processAppShortcut, processFolder, or
+     * processWidget depending on what type of item is being processed.
+     *
+     * All the parameters are expected to be shared between many repeated calls of this method, one
+     * for each workspace item.
+     */
+    fun processItem() {
+        try {
+            if (c.user == null) {
+                // User has been deleted, remove the item.
+                c.markDeleted("User has been deleted")
+                if (isRestoreFromBackup) {
+                    restoreEventLogger?.logSingleFavoritesItemRestoreFailed(
+                        c.itemType,
+                        LauncherRestoreEventLogger.RESTORE_ERROR_PROFILE_DELETED
+                    )
+                }
+                return
+            }
+            when (c.itemType) {
+                Favorites.ITEM_TYPE_APPLICATION,
+                Favorites.ITEM_TYPE_DEEP_SHORTCUT -> processAppOrDeepShortcut()
+                Favorites.ITEM_TYPE_FOLDER,
+                Favorites.ITEM_TYPE_APP_PAIR -> processFolderOrAppPair()
+                Favorites.ITEM_TYPE_APPWIDGET,
+                Favorites.ITEM_TYPE_CUSTOM_APPWIDGET -> processWidget()
+            }
+        } catch (e: Exception) {
+            Log.e(TAG, "Desktop items loading interrupted", e)
+        }
+    }
+
+    /**
+     * This method verifies that an app shortcut should be shown on the home screen, updates the
+     * database accordingly, formats the data in such a way that it is ready to be added to the data
+     * model, and then adds it to the launcher’s data model.
+     *
+     * In this method, verification means that an an app shortcut database entry is required to:
+     * Have a Launch Intent. This is how the app component symbolized by the shortcut is launched.
+     * Have a Package Name. Not be in a funky “Restoring, but never actually restored” state. Not
+     * have null or missing ShortcutInfos or ItemInfos in other data models.
+     *
+     * If any of the above are found to be true, the database entry is deleted, and not shown on the
+     * user’s home screen. When an app is verified, it is marked as restored, meaning that the app
+     * is viable to show on the home screen.
+     *
+     * In order to accommodate different types and versions of App Shortcuts, different properties
+     * and flags are set on the ItemInfo objects that are added to the data model. For example,
+     * icons that are not a part of the workspace or hotseat are marked as using low resolution icon
+     * bitmaps. Currently suspended app icons are marked as such. Installing packages are also
+     * marked as such. Lastly, after applying common properties to the ItemInfo, it is added to the
+     * data model to be bound to the launcher’s data model.
+     */
+    @SuppressLint("NewApi")
+    @VisibleForTesting
+    fun processAppOrDeepShortcut() {
+        var allowMissingTarget = false
+        var intent = c.parseIntent()
+        if (intent == null) {
+            c.markDeleted("Invalid or null intent")
+            if (isRestoreFromBackup) {
+                restoreEventLogger?.logSingleFavoritesItemRestoreFailed(
+                    c.itemType,
+                    LauncherRestoreEventLogger.RESTORE_ERROR_MISSING_INFO
+                )
+            }
+            return
+        }
+        var disabledState =
+            if (userManagerState.isUserQuiet(c.serialNumber))
+                WorkspaceItemInfo.FLAG_DISABLED_QUIET_USER
+            else 0
+        var cn = intent.component
+        val targetPkg = if (cn == null) intent.getPackage() else cn.packageName
+        if (TextUtils.isEmpty(targetPkg)) {
+            c.markDeleted("Shortcuts can't have null package")
+            if (isRestoreFromBackup) {
+                restoreEventLogger?.logSingleFavoritesItemRestoreFailed(
+                    c.itemType,
+                    LauncherRestoreEventLogger.RESTORE_ERROR_MISSING_INFO
+                )
+            }
+            return
+        }
+
+        // If there is no target package, it's an implicit intent
+        // (legacy shortcut) which is always valid
+        var validTarget =
+            (TextUtils.isEmpty(targetPkg) || launcherApps.isPackageEnabled(targetPkg, c.user))
+
+        // If it's a deep shortcut, we'll use pinned shortcuts to restore it
+        if (cn != null && validTarget && (c.itemType != Favorites.ITEM_TYPE_DEEP_SHORTCUT)) {
+            // If the apk is present and the shortcut points to a specific component.
+
+            // If the component is already present
+            if (launcherApps.isActivityEnabled(cn, c.user)) {
+                // no special handling necessary for this item
+                c.markRestored()
+                if (isRestoreFromBackup) {
+                    restoreEventLogger?.logSingleFavoritesItemRestored(c.itemType)
+                }
+            } else {
+                // Gracefully try to find a fallback activity.
+                intent = pmHelper.getAppLaunchIntent(targetPkg, c.user)
+                if (intent != null) {
+                    c.restoreFlag = 0
+                    c.updater().put(Favorites.INTENT, intent.toUri(0)).commit()
+                } else {
+                    c.markDeleted("Intent null, unable to find a launch target")
+                    if (isRestoreFromBackup) {
+                        restoreEventLogger?.logSingleFavoritesItemRestoreFailed(
+                            c.itemType,
+                            LauncherRestoreEventLogger.RESTORE_ERROR_MISSING_INFO
+                        )
+                    }
+                    return
+                }
+            }
+        }
+        // else if cn == null => can't infer much, leave it
+        // else if !validPkg => could be restored icon or missing sd-card
+        when {
+            !TextUtils.isEmpty(targetPkg) && !validTarget -> {
+                // Points to a valid app (superset of cn != null) but the apk
+                // is not available.
+                when {
+                    c.restoreFlag != 0 -> {
+                        // Package is not yet available but might be
+                        // installed later.
+                        FileLog.d(TAG, "package not yet restored: $targetPkg")
+                        tempPackageKey.update(targetPkg, c.user)
+                        when {
+                            c.hasRestoreFlag(WorkspaceItemInfo.FLAG_RESTORE_STARTED) -> {
+                                // Restore has started once.
+                            }
+                            installingPkgs.containsKey(tempPackageKey) -> {
+                                // App restore has started. Update the flag
+                                c.restoreFlag =
+                                    c.restoreFlag or WorkspaceItemInfo.FLAG_RESTORE_STARTED
+                                FileLog.d(TAG, "restore started for installing app: $targetPkg")
+                                c.updater().put(Favorites.RESTORED, c.restoreFlag).commit()
+                            }
+                            else -> {
+                                c.markDeleted(
+                                    "removing app that is not restored and not installing. package: $targetPkg"
+                                )
+                                if (isRestoreFromBackup) {
+                                    restoreEventLogger?.logSingleFavoritesItemRestoreFailed(
+                                        c.itemType,
+                                        LauncherRestoreEventLogger.RESTORE_ERROR_APP_NOT_INSTALLED
+                                    )
+                                }
+                                return
+                            }
+                        }
+                    }
+                    pmHelper.isAppOnSdcard(targetPkg!!, c.user) -> {
+                        // Package is present but not available.
+                        disabledState =
+                            disabledState or WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE
+                        // Add the icon on the workspace anyway.
+                        allowMissingTarget = true
+                    }
+                    !isSdCardReady -> {
+                        // SdCard is not ready yet. Package might get available,
+                        // once it is ready.
+                        Log.d(TAG, "Missing package, will check later: $targetPkg")
+                        pendingPackages.add(PackageUserKey(targetPkg, c.user))
+                        // Add the icon on the workspace anyway.
+                        allowMissingTarget = true
+                    }
+                    else -> {
+                        // Do not wait for external media load anymore.
+                        c.markDeleted("Invalid package removed: $targetPkg")
+                        if (isRestoreFromBackup) {
+                            restoreEventLogger?.logSingleFavoritesItemRestoreFailed(
+                                c.itemType,
+                                LauncherRestoreEventLogger.RESTORE_ERROR_APP_NOT_INSTALLED
+                            )
+                        }
+                        return
+                    }
+                }
+            }
+        }
+        if (c.restoreFlag and WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI != 0) {
+            validTarget = false
+        }
+        if (validTarget) {
+            // The shortcut points to a valid target (either no target
+            // or something which is ready to be used)
+            c.markRestored()
+        }
+        val useLowResIcon = !c.isOnWorkspaceOrHotseat
+        val info: WorkspaceItemInfo?
+        when {
+            c.restoreFlag != 0 -> {
+                // Already verified above that user is same as default user
+                info = c.getRestoredItemInfo(intent)
+            }
+            c.itemType == Favorites.ITEM_TYPE_APPLICATION ->
+                info = c.getAppShortcutInfo(intent, allowMissingTarget, useLowResIcon, false)
+            c.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT -> {
+                val key = ShortcutKey.fromIntent(intent, c.user)
+                if (unlockedUsers[c.serialNumber]) {
+                    val pinnedShortcut = shortcutKeyToPinnedShortcuts[key]
+                    if (pinnedShortcut == null) {
+                        // The shortcut is no longer valid.
+                        c.markDeleted(
+                            "Pinned shortcut not found from request. package=${key.packageName}, user=${c.user}"
+                        )
+                        if (isRestoreFromBackup) {
+                            restoreEventLogger?.logSingleFavoritesItemRestoreFailed(
+                                c.itemType,
+                                LauncherRestoreEventLogger.RESTORE_ERROR_SHORTCUT_NOT_FOUND
+                            )
+                        }
+                        return
+                    }
+                    info = WorkspaceItemInfo(pinnedShortcut, app.context)
+                    // If the pinned deep shortcut is no longer published,
+                    // use the last saved icon instead of the default.
+                    iconCache.getShortcutIcon(info, pinnedShortcut, c::loadIcon)
+                    if (pmHelper.isAppSuspended(pinnedShortcut.getPackage(), info.user)) {
+                        info.runtimeStatusFlags =
+                            info.runtimeStatusFlags or ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED
+                    }
+                    intent = info.getIntent()
+                    allDeepShortcuts.add(pinnedShortcut)
+                } else {
+                    // Create a shortcut info in disabled mode for now.
+                    info = c.loadSimpleWorkspaceItem()
+                    info.runtimeStatusFlags =
+                        info.runtimeStatusFlags or ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER
+                }
+                if (isRestoreFromBackup) {
+                    restoreEventLogger?.logSingleFavoritesItemRestored(c.itemType)
+                }
+            }
+            else -> { // item type == ITEM_TYPE_SHORTCUT
+                info = c.loadSimpleWorkspaceItem()
+
+                // Shortcuts are only available on the primary profile
+                if (!TextUtils.isEmpty(targetPkg) && pmHelper.isAppSuspended(targetPkg!!, c.user)) {
+                    disabledState = disabledState or ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED
+                }
+                info.options = c.options
+
+                // App shortcuts that used to be automatically added to Launcher
+                // didn't always have the correct intent flags set, so do that here
+                if (
+                    intent.action != null &&
+                        intent.categories != null &&
+                        intent.action == Intent.ACTION_MAIN &&
+                        intent.categories.contains(Intent.CATEGORY_LAUNCHER)
+                ) {
+                    intent.addFlags(
+                        Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
+                    )
+                }
+            }
+        }
+        if (info != null) {
+            if (info.itemType != Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+                // Skip deep shortcuts; their title and icons have already been
+                // loaded above.
+                iconRequestInfos.add(c.createIconRequestInfo(info, useLowResIcon))
+            }
+            c.applyCommonProperties(info)
+            info.intent = intent
+            info.rank = c.rank
+            info.spanX = 1
+            info.spanY = 1
+            info.runtimeStatusFlags = info.runtimeStatusFlags or disabledState
+            if (isSafeMode && !PackageManagerHelper.isSystemApp(app.context, intent)) {
+                info.runtimeStatusFlags =
+                    info.runtimeStatusFlags or ItemInfoWithIcon.FLAG_DISABLED_SAFEMODE
+            }
+            val activityInfo = c.launcherActivityInfo
+            if (activityInfo != null) {
+                info.setProgressLevel(
+                    PackageManagerHelper.getLoadingProgress(activityInfo),
+                    PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING
+                )
+            }
+            if (
+                (c.restoreFlag != 0 ||
+                    Flags.enableSupportForArchiving() &&
+                        activityInfo != null &&
+                        activityInfo.applicationInfo.isArchived) && !TextUtils.isEmpty(targetPkg)
+            ) {
+                tempPackageKey.update(targetPkg, c.user)
+                val si = installingPkgs[tempPackageKey]
+                if (si == null) {
+                    info.runtimeStatusFlags =
+                        info.runtimeStatusFlags and
+                            ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE.inv()
+                } else if (
+                    activityInfo ==
+                        null // For archived apps, include progress info in case there is
+                    // a pending install session post restart of device.
+                    ||
+                        (Flags.enableSupportForArchiving() &&
+                            activityInfo.applicationInfo.isArchived)
+                ) {
+                    val installProgress = (si.getProgress() * 100).toInt()
+                    info.setProgressLevel(installProgress, PackageInstallInfo.STATUS_INSTALLING)
+                }
+            }
+            c.checkAndAddItem(info, bgDataModel, memoryLogger)
+        } else {
+            throw RuntimeException("Unexpected null WorkspaceItemInfo")
+        }
+    }
+
+    /**
+     * Loads the folder information from the database and formats it into a FolderInfo. Some of the
+     * processing for folder content items is done in LoaderTask after all the items in the
+     * workspace have been loaded. The loaded FolderInfos are stored in the BgDataModel.
+     */
+    @VisibleForTesting
+    fun processFolderOrAppPair() {
+        val folderInfo =
+            bgDataModel.findOrMakeFolder(c.id).apply {
+                c.applyCommonProperties(this)
+                itemType = c.itemType
+                // Do not trim the folder label, as is was set by the user.
+                title = c.getString(c.mTitleIndex)
+                spanX = 1
+                spanY = 1
+                options = c.options
+            }
+
+        // no special handling required for restored folders
+        c.markRestored()
+        if (isRestoreFromBackup) {
+            restoreEventLogger?.logSingleFavoritesItemRestored(c.itemType)
+        }
+        c.checkAndAddItem(folderInfo, bgDataModel, memoryLogger)
+    }
+
+    /**
+     * This method, similar to processAppShortcut above, verifies that a widget should be shown on
+     * the home screen, updates the database accordingly, formats the data in such a way that it is
+     * ready to be added to the data model, and then adds it to the launcher’s data model.
+     *
+     * It verifies that: Widgets are not disabled due to the Launcher variety being of the `Go`
+     * type. Search Widgets have a package name. The app behind the widget is still installed on the
+     * device. The app behind the widget is not in a funky “Restoring, but never actually restored”
+     * state. The widget has a valid size. The widget is in the workspace or the hotseat. If any of
+     * the above are found to be true, the database entry is deleted, and the widget is not shown on
+     * the user’s home screen. When a widget is verified, it is marked as restored, meaning that the
+     * widget is viable to show on the home screen.
+     *
+     * Common properties are applied to the Widget’s Info object, and other information as well
+     * depending on the type of widget. Custom widgets are treated differently than non-custom
+     * widgets, installing / restoring widgets are treated differently, etc.
+     */
+    @VisibleForTesting
+    fun processWidget() {
+        if (WidgetsModel.GO_DISABLE_WIDGETS && c.itemType == Favorites.ITEM_TYPE_APPWIDGET) {
+            c.markDeleted("Only legacy shortcuts can have null package")
+            if (isRestoreFromBackup) {
+                restoreEventLogger?.logSingleFavoritesItemRestoreFailed(
+                    c.itemType,
+                    LauncherRestoreEventLogger.RESTORE_ERROR_WIDGETS_DISABLED
+                )
+            }
+            return
+        }
+
+        // Read all Launcher-specific widget details
+        val customWidget = (c.itemType == Favorites.ITEM_TYPE_CUSTOM_APPWIDGET)
+        val appWidgetId = c.appWidgetId
+        val savedProvider = c.appWidgetProvider
+        val component: ComponentName?
+        if (c.options and LauncherAppWidgetInfo.OPTION_SEARCH_WIDGET != 0) {
+            component = QsbContainerView.getSearchComponentName(app.context)
+            if (component == null) {
+                c.markDeleted("Discarding SearchWidget without package name")
+                if (isRestoreFromBackup) {
+                    restoreEventLogger?.logSingleFavoritesItemRestoreFailed(
+                        c.itemType,
+                        LauncherRestoreEventLogger.RESTORE_ERROR_MISSING_INFO
+                    )
+                }
+                return
+            }
+        } else {
+            component = ComponentName.unflattenFromString(savedProvider)
+        }
+        val isIdValid = !c.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)
+        val wasProviderReady = !c.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)
+        val providerKey = ComponentKey(component, c.user)
+        if (!widgetProvidersMap.containsKey(providerKey)) {
+            if (customWidget) {
+                widgetProvidersMap[providerKey] =
+                    CustomWidgetManager.INSTANCE[app.context].getWidgetProvider(component)
+            } else {
+                widgetProvidersMap[providerKey] = widgetHelper.findProvider(component, c.user)
+            }
+        }
+        val provider = widgetProvidersMap[providerKey]
+        val isProviderReady = LoaderTask.isValidProvider(provider)
+        if (!isSafeMode && !customWidget && wasProviderReady && !isProviderReady) {
+            c.markDeleted("Deleting widget that isn't installed anymore: $provider")
+            if (isRestoreFromBackup) {
+                restoreEventLogger?.logSingleFavoritesItemRestoreFailed(
+                    c.itemType,
+                    LauncherRestoreEventLogger.RESTORE_ERROR_APP_NOT_INSTALLED
+                )
+            }
+        } else {
+            val appWidgetInfo: LauncherAppWidgetInfo
+            if (isProviderReady) {
+                appWidgetInfo = LauncherAppWidgetInfo(appWidgetId, provider!!.provider)
+
+                // The provider is available. So the widget is either
+                // available or not available. We do not need to track
+                // any future restore updates.
+                var status =
+                    (c.restoreFlag and
+                        LauncherAppWidgetInfo.FLAG_RESTORE_STARTED.inv() and
+                        LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY.inv())
+                if (!wasProviderReady) {
+                    // If provider was not previously ready, update status and UI flag.
+
+                    // Id would be valid only if the widget restore broadcast received.
+                    if (isIdValid) {
+                        status = status or LauncherAppWidgetInfo.FLAG_UI_NOT_READY
+                    }
+                }
+                appWidgetInfo.restoreStatus = status
+            } else {
+                Log.v(
+                    TAG,
+                    "Widget restore pending id=${c.id} appWidgetId=$appWidgetId status=${c.restoreFlag}"
+                )
+                appWidgetInfo = LauncherAppWidgetInfo(appWidgetId, component)
+                appWidgetInfo.restoreStatus = c.restoreFlag
+                tempPackageKey.update(component!!.packageName, c.user)
+                val si = installingPkgs[tempPackageKey]
+                val installProgress = if (si == null) null else (si.getProgress() * 100).toInt()
+                when {
+                    c.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_RESTORE_STARTED) -> {
+                        // Restore has started once.
+                    }
+                    installProgress != null -> {
+                        // App restore has started. Update the flag
+                        appWidgetInfo.restoreStatus =
+                            appWidgetInfo.restoreStatus or
+                                LauncherAppWidgetInfo.FLAG_RESTORE_STARTED
+                    }
+                    !isSafeMode -> {
+                        c.markDeleted("Unrestored widget removed: $component")
+                        if (isRestoreFromBackup) {
+                            restoreEventLogger?.logSingleFavoritesItemRestoreFailed(
+                                c.itemType,
+                                LauncherRestoreEventLogger.RESTORE_ERROR_APP_NOT_INSTALLED
+                            )
+                        }
+                        return
+                    }
+                }
+                appWidgetInfo.installProgress = installProgress ?: 0
+            }
+            if (appWidgetInfo.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG)) {
+                appWidgetInfo.bindOptions = c.parseIntent()
+            }
+            c.applyCommonProperties(appWidgetInfo)
+            appWidgetInfo.spanX = c.spanX
+            appWidgetInfo.spanY = c.spanY
+            appWidgetInfo.options = c.options
+            appWidgetInfo.user = c.user
+            appWidgetInfo.sourceContainer = c.appWidgetSource
+            if (appWidgetInfo.spanX <= 0 || appWidgetInfo.spanY <= 0) {
+                c.markDeleted(
+                    "Widget has invalid size: ${appWidgetInfo.spanX}x${appWidgetInfo.spanY}"
+                )
+                if (isRestoreFromBackup) {
+                    restoreEventLogger?.logSingleFavoritesItemRestoreFailed(
+                        c.itemType,
+                        LauncherRestoreEventLogger.RESTORE_ERROR_INVALID_LOCATION
+                    )
+                }
+                return
+            }
+            val widgetProviderInfo =
+                widgetHelper.getLauncherAppWidgetInfo(appWidgetId, appWidgetInfo.targetComponent)
+            if (
+                widgetProviderInfo != null &&
+                    (appWidgetInfo.spanX < widgetProviderInfo.minSpanX ||
+                        appWidgetInfo.spanY < widgetProviderInfo.minSpanY)
+            ) {
+                FileLog.d(
+                    TAG,
+                    "Widget ${widgetProviderInfo.component} minSizes not meet:" +
+                        " span=${appWidgetInfo.spanX}x${appWidgetInfo.spanY}" +
+                        " minSpan=${widgetProviderInfo.minSpanX}x${widgetProviderInfo.minSpanY}"
+                )
+                logWidgetInfo(app.invariantDeviceProfile, widgetProviderInfo)
+            }
+            if (!c.isOnWorkspaceOrHotseat) {
+                c.markDeleted(
+                    "Widget found where container != CONTAINER_DESKTOP" +
+                        " nor CONTAINER_HOTSEAT - ignoring!"
+                )
+                if (isRestoreFromBackup) {
+                    restoreEventLogger?.logSingleFavoritesItemRestoreFailed(
+                        c.itemType,
+                        LauncherRestoreEventLogger.RESTORE_ERROR_INVALID_LOCATION
+                    )
+                }
+                return
+            }
+            if (!customWidget) {
+                val providerName = appWidgetInfo.providerName.flattenToString()
+                if (providerName != savedProvider || appWidgetInfo.restoreStatus != c.restoreFlag) {
+                    c.updater()
+                        .put(Favorites.APPWIDGET_PROVIDER, providerName)
+                        .put(Favorites.RESTORED, appWidgetInfo.restoreStatus)
+                        .commit()
+                }
+            }
+            if (appWidgetInfo.restoreStatus != LauncherAppWidgetInfo.RESTORE_COMPLETED) {
+                appWidgetInfo.pendingItemInfo =
+                    WidgetsModel.newPendingItemInfo(
+                        app.context,
+                        appWidgetInfo.providerName,
+                        appWidgetInfo.user
+                    )
+                iconCache.getTitleAndIconForApp(appWidgetInfo.pendingItemInfo, false)
+            }
+            c.checkAndAddItem(appWidgetInfo, bgDataModel)
+        }
+    }
+
+    companion object {
+        private val TAG = WorkspaceItemProcessor::class.java.simpleName
+        private fun logWidgetInfo(
+            idp: InvariantDeviceProfile,
+            widgetProviderInfo: LauncherAppWidgetProviderInfo
+        ) {
+            val cellSize = Point()
+            for (deviceProfile in idp.supportedProfiles) {
+                deviceProfile.getCellSize(cellSize)
+                FileLog.d(
+                    TAG,
+                    "DeviceProfile available width: ${deviceProfile.availableWidthPx}," +
+                        " available height: ${deviceProfile.availableHeightPx}," +
+                        " cellLayoutBorderSpacePx Horizontal: ${deviceProfile.cellLayoutBorderSpacePx.x}," +
+                        " cellLayoutBorderSpacePx Vertical: ${deviceProfile.cellLayoutBorderSpacePx.y}," +
+                        " cellSize: $cellSize"
+                )
+            }
+            val widgetDimension = StringBuilder()
+            widgetDimension
+                .append("Widget dimensions:\n")
+                .append("minResizeWidth: ")
+                .append(widgetProviderInfo.minResizeWidth)
+                .append("\n")
+                .append("minResizeHeight: ")
+                .append(widgetProviderInfo.minResizeHeight)
+                .append("\n")
+                .append("defaultWidth: ")
+                .append(widgetProviderInfo.minWidth)
+                .append("\n")
+                .append("defaultHeight: ")
+                .append(widgetProviderInfo.minHeight)
+                .append("\n")
+            if (Utilities.ATLEAST_S) {
+                widgetDimension
+                    .append("targetCellWidth: ")
+                    .append(widgetProviderInfo.targetCellWidth)
+                    .append("\n")
+                    .append("targetCellHeight: ")
+                    .append(widgetProviderInfo.targetCellHeight)
+                    .append("\n")
+                    .append("maxResizeWidth: ")
+                    .append(widgetProviderInfo.maxResizeWidth)
+                    .append("\n")
+                    .append("maxResizeHeight: ")
+                    .append(widgetProviderInfo.maxResizeHeight)
+                    .append("\n")
+            }
+            FileLog.d(TAG, widgetDimension.toString())
+        }
+    }
+}
diff --git a/src/com/android/launcher3/model/data/AppInfo.java b/src/com/android/launcher3/model/data/AppInfo.java
index 872ce4b..ea8a7a1 100644
--- a/src/com/android/launcher3/model/data/AppInfo.java
+++ b/src/com/android/launcher3/model/data/AppInfo.java
@@ -55,7 +55,8 @@
      */
     public Intent intent;
 
-    @NonNull
+    // componentName for the Private Space Install App button can be null
+    @Nullable
     public ComponentName componentName;
 
     // Section name used for indexing.
diff --git a/src/com/android/launcher3/pm/UserCache.java b/src/com/android/launcher3/pm/UserCache.java
index 4661fd4..8708d5a 100644
--- a/src/com/android/launcher3/pm/UserCache.java
+++ b/src/com/android/launcher3/pm/UserCache.java
@@ -28,6 +28,7 @@
 
 import androidx.annotation.AnyThread;
 import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
 import androidx.annotation.WorkerThread;
 
 import com.android.launcher3.util.MainThreadInitializedObject;
@@ -154,6 +155,11 @@
                 .orElse(Process.myUserHandle());
     }
 
+    @VisibleForTesting
+    public void putToCache(UserHandle userHandle, UserIconInfo info) {
+        mUserToSerialMap.put(userHandle, info);
+    }
+
     /**
      * @see UserManager#getUserProfiles()
      */
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index f39f806..8463361 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -1,5 +1,6 @@
 package com.android.launcher3.popup;
 
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_INSTALL_SYSTEM_SHORTCUT_TAP;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_APP_INFO_TAP;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_WIDGETS_TAP;
 
@@ -8,6 +9,7 @@
 import android.content.Intent;
 import android.graphics.Rect;
 import android.os.Process;
+import android.os.UserHandle;
 import android.view.View;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.ImageView;
@@ -20,10 +22,12 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.allapps.PrivateProfileManager;
 import com.android.launcher3.model.WidgetItem;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.uioverrides.ApiWrapper;
+import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.InstantAppResolver;
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.PackageUserKey;
@@ -212,6 +216,69 @@
         }
     }
 
+    public static final Factory<Launcher> PRIVATE_PROFILE_INSTALL =
+            (launcher, itemInfo, originalView) -> {
+                if (itemInfo.getTargetComponent() == null
+                        || !(itemInfo instanceof com.android.launcher3.model.data.AppInfo)
+                        || !itemInfo.getContainerInfo().hasAllAppsContainer()
+                        || !Process.myUserHandle().equals(itemInfo.user)) {
+                    return null;
+                }
+
+                PrivateProfileManager privateProfileManager =
+                        launcher.getAppsView().getPrivateProfileManager();
+                if (privateProfileManager == null || !privateProfileManager.isEnabled()) {
+                    return null;
+                }
+
+                UserHandle privateProfileUser = privateProfileManager.getProfileUser();
+                if (privateProfileUser == null) {
+                    return null;
+                }
+                // Do not show shortcut if an app is already installed to the space
+                ComponentKey targetKey =
+                        new ComponentKey(itemInfo.getTargetComponent(), privateProfileUser);
+                if (launcher.getAppsView().getAppsStore().getApp(targetKey) != null) {
+                    return null;
+                }
+
+                // TODO(b/302666597): do not install app if it's in deny list (e.g. settings)
+
+                return new InstallToPrivateProfile(
+                        launcher, itemInfo, originalView, privateProfileUser);
+            };
+
+    static class InstallToPrivateProfile extends SystemShortcut<Launcher> {
+        UserHandle mSpaceUser;
+
+        InstallToPrivateProfile(
+                Launcher target, ItemInfo itemInfo, View originalView, UserHandle spaceUser) {
+            // TODO(b/302666597): update icon once available
+            super(
+                    R.drawable.ic_install_to_private,
+                    R.string.install_private_system_shortcut_label,
+                    target,
+                    itemInfo,
+                    originalView);
+            mSpaceUser = spaceUser;
+        }
+
+        @Override
+        public void onClick(View view) {
+            Intent intent =
+                    ApiWrapper.getAppMarketActivityIntent(
+                            view.getContext(),
+                            mItemInfo.getTargetComponent().getPackageName(),
+                            mSpaceUser);
+            mTarget.startActivitySafely(view, intent, mItemInfo);
+            AbstractFloatingView.closeAllOpenViews(mTarget);
+            mTarget.getStatsLogManager()
+                    .logger()
+                    .withItemInfo(mItemInfo)
+                    .log(LAUNCHER_PRIVATE_SPACE_INSTALL_SYSTEM_SHORTCUT_TAP);
+        }
+    }
+
     public static final Factory<BaseDraggingActivity> INSTALL =
             (activity, itemInfo, originalView) -> {
                 boolean supportsWebUI = (itemInfo instanceof WorkspaceItemInfo)
diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java
index a969c8f..9750d25 100644
--- a/src/com/android/launcher3/provider/RestoreDbTask.java
+++ b/src/com/android/launcher3/provider/RestoreDbTask.java
@@ -18,7 +18,7 @@
 
 import static android.os.Process.myUserHandle;
 
-import static com.android.launcher3.Flags.enableLauncherBrMetrics;
+import static com.android.launcher3.Flags.enableLauncherBrMetricsFixed;
 import static com.android.launcher3.InvariantDeviceProfile.TYPE_MULTI_DISPLAY;
 import static com.android.launcher3.LauncherPrefs.APP_WIDGET_IDS;
 import static com.android.launcher3.LauncherPrefs.IS_FIRST_LOAD_AFTER_RESTORE;
@@ -30,7 +30,6 @@
 import static com.android.launcher3.backuprestore.LauncherRestoreEventLogger.RESTORE_ERROR_PROFILE_NOT_RESTORED;
 import static com.android.launcher3.backuprestore.LauncherRestoreEventLogger.RESTORE_ERROR_WIDGETS_DISABLED;
 import static com.android.launcher3.backuprestore.LauncherRestoreEventLogger.RESTORE_ERROR_WIDGET_REMOVED;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_LAUNCHER_BR_METRICS;
 import static com.android.launcher3.provider.LauncherDbUtils.dropTable;
 import static com.android.launcher3.widget.LauncherWidgetHolder.APPWIDGET_HOST_ID;
 
@@ -199,7 +198,7 @@
         Arrays.fill(args, "?");
         final String where = "profileId NOT IN (" + TextUtils.join(", ", Arrays.asList(args)) + ")";
         logFavoritesTable(db, "items to delete from unrestored profiles:", where, profileIds);
-        if (enableLauncherBrMetrics() || ENABLE_LAUNCHER_BR_METRICS.get()) {
+        if (enableLauncherBrMetricsFixed()) {
             reportUnrestoredProfiles(db, where, profileIds, restoreEventLogger);
         }
         int itemsDeletedCount = db.delete(Favorites.TABLE_NAME, where, profileIds);
@@ -361,7 +360,7 @@
         DeviceGridState deviceGridState = new DeviceGridState(context);
         FileLog.d(TAG, "restore initiated from backup: DeviceGridState=" + deviceGridState);
         LauncherPrefs.get(context).putSync(RESTORE_DEVICE.to(deviceGridState.getDeviceType()));
-        if (enableLauncherBrMetrics() || ENABLE_LAUNCHER_BR_METRICS.get()) {
+        if (enableLauncherBrMetricsFixed()) {
             LauncherPrefs.get(context).putSync(IS_FIRST_LOAD_AFTER_RESTORE.to(true));
         }
     }
diff --git a/src/com/android/launcher3/responsive/ResponsiveSpec.kt b/src/com/android/launcher3/responsive/ResponsiveSpec.kt
index b0e1b27..65e0b32 100644
--- a/src/com/android/launcher3/responsive/ResponsiveSpec.kt
+++ b/src/com/android/launcher3/responsive/ResponsiveSpec.kt
@@ -59,7 +59,7 @@
     val cellSize: SizeSpec,
 ) : IResponsiveSpec {
     init {
-        check(isValid()) { "Invalid ResponsiveSpec found." }
+        check(isValid()) { "Invalid ResponsiveSpec found. $this" }
     }
 
     constructor(
@@ -106,7 +106,7 @@
         }
 
         if (!isValidRemainderSpace()) {
-            logError("The total Remainder Space used must be lower or equal to 100%.")
+            logError("The total Remainder Space used must be equal to 0 or 1.")
             return false
         }
 
@@ -131,11 +131,12 @@
     }
 
     private fun isValidRemainderSpace(): Boolean {
-        // TODO(b/313621277): This validation must be update do accept only 0 or 1 instead of <= 1f.
-        return startPadding.ofRemainderSpace +
-            endPadding.ofRemainderSpace +
-            gutter.ofRemainderSpace +
-            cellSize.ofRemainderSpace <= 1f
+        val remainderSpaceUsed =
+            startPadding.ofRemainderSpace +
+                endPadding.ofRemainderSpace +
+                gutter.ofRemainderSpace +
+                cellSize.ofRemainderSpace
+        return remainderSpaceUsed == 0f || remainderSpaceUsed == 1f
     }
 
     private fun isValidAvailableSpace(): Boolean {
@@ -254,8 +255,8 @@
 
         startPaddingPx = spec.startPadding.getRemainderSpaceValue(remainderSpace, startPaddingPx)
         endPaddingPx = spec.endPadding.getRemainderSpaceValue(remainderSpace, endPaddingPx)
-        gutterPx = spec.gutter.getRemainderSpaceValue(remainderSpace, gutterPx)
-        cellSizePx = spec.cellSize.getRemainderSpaceValue(remainderSpace, cellSizePx)
+        gutterPx = spec.gutter.getRemainderSpaceValue(remainderSpace, gutterPx, gutters)
+        cellSizePx = spec.cellSize.getRemainderSpaceValue(remainderSpace, cellSizePx, cells)
     }
 
     override fun hashCode(): Int {
diff --git a/src/com/android/launcher3/responsive/SizeSpec.kt b/src/com/android/launcher3/responsive/SizeSpec.kt
index d146898..41dcd5e 100644
--- a/src/com/android/launcher3/responsive/SizeSpec.kt
+++ b/src/com/android/launcher3/responsive/SizeSpec.kt
@@ -57,11 +57,16 @@
     /**
      * Calculates the [SizeSpec] value when remainder space value is defined. If no remainderSpace
      * is 0, returns a default value.
+     *
+     * @param remainderSpace The remainder space to be used for the calculation
+     * @param defaultValue The default value to be returned when no ofRemainderSpace is defined
+     * @param factor A number to divide the remainder space. The default value is 1. This property
+     *   is used to split equally the remainder space by the number of cells and gutters.
      */
-    fun getRemainderSpaceValue(remainderSpace: Int, defaultValue: Int): Int {
+    fun getRemainderSpaceValue(remainderSpace: Int, defaultValue: Int, factor: Int = 1): Int {
         val remainderSpaceValue =
             if (ofRemainderSpace > 0) {
-                (ofRemainderSpace * remainderSpace).roundToInt()
+                (ofRemainderSpace * remainderSpace / factor).roundToInt()
             } else {
                 defaultValue
             }
diff --git a/src/com/android/launcher3/statemanager/StateManager.java b/src/com/android/launcher3/statemanager/StateManager.java
index 360ff7e..e8b3066 100644
--- a/src/com/android/launcher3/statemanager/StateManager.java
+++ b/src/com/android/launcher3/statemanager/StateManager.java
@@ -19,6 +19,7 @@
 import static android.animation.ValueAnimator.areAnimatorsEnabled;
 
 import static com.android.launcher3.anim.AnimatorPlaybackController.callListenerCommandRecursively;
+import static com.android.launcher3.states.StateAnimationConfig.HANDLE_STATE_APPLY;
 import static com.android.launcher3.states.StateAnimationConfig.SKIP_ALL_ANIMATIONS;
 
 import android.animation.Animator;
@@ -36,10 +37,13 @@
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.states.StateAnimationConfig;
 import com.android.launcher3.states.StateAnimationConfig.AnimationFlags;
+import com.android.launcher3.states.StateAnimationConfig.AnimationPropertyFlags;
 import com.android.launcher3.testing.shared.TestProtocol;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.stream.Collectors;
 
 /**
  * Class to manage transitions between different states for a StatefulActivity based on different
@@ -189,7 +193,7 @@
 
     public void reapplyState(boolean cancelCurrentAnimation) {
         boolean wasInAnimation = mConfig.currentAnimation != null;
-        if (cancelCurrentAnimation) {
+        if (cancelCurrentAnimation && (mConfig.animProps & HANDLE_STATE_APPLY) == 0) {
             // Animation canceling can trigger a cleanup routine, causing problems when we are in a
             // launcher state that relies on member variable data. So if we are in one of those
             // states, accelerate the current animation to its end point rather than canceling it
@@ -227,7 +231,15 @@
 
     private void goToState(
             STATE_TYPE state, boolean animated, long delay, AnimatorListener listener) {
-        Log.d(TestProtocol.OVERVIEW_OVER_HOME, "go to state " + state);
+        String stackTrace = Log.getStackTraceString(new Exception("tracing state transition"));
+        String truncatedTrace =
+                Arrays.stream(stackTrace.split("\\n"))
+                    .limit(5)
+                    .skip(1) // Removes the line "java.lang.Exception: tracing state transition"
+                    .filter(traceLine -> !traceLine.contains("StateManager.goToState"))
+                    .collect(Collectors.joining("\n"));
+        Log.d(TestProtocol.OVERVIEW_OVER_HOME,
+                "go to state " + state + " partial trace:\n" + truncatedTrace);
 
         animated &= areAnimatorsEnabled();
         if (mActivity.isInState(state)) {
@@ -237,7 +249,7 @@
                     listener.onAnimationEnd(null);
                 }
                 return;
-            } else if ((!mConfig.userControlled && animated && mConfig.targetState == state)
+            } else if ((!mConfig.isUserControlled() && animated && mConfig.targetState == state)
                     || mState.shouldPreserveDataStateOnReapply()) {
                 // We are running the same animation as requested, and/or target state should not be
                 // reset -- allow the current animation to complete instead of canceling it.
@@ -343,7 +355,7 @@
 
     public AnimatorPlaybackController createAnimationToNewWorkspace(STATE_TYPE state,
             StateAnimationConfig config) {
-        config.userControlled = true;
+        config.animProps |= StateAnimationConfig.USER_CONTROLLED;
         cancelAnimation();
         config.copyTo(mConfig);
         mConfig.playbackController = createAnimationToNewWorkspaceInternal(state)
@@ -418,7 +430,7 @@
     }
 
     public void moveToRestState(boolean isAnimated) {
-        if (mConfig.currentAnimation != null && mConfig.userControlled) {
+        if (mConfig.currentAnimation != null && mConfig.isUserControlled()) {
             // The user is doing something. Lets not mess it up
             return;
         }
@@ -450,10 +462,18 @@
         }
     }
 
+    /**
+     * Sets the provided controller as the current user controlled state animation
+     */
     public void setCurrentUserControlledAnimation(AnimatorPlaybackController controller) {
+        setCurrentAnimation(controller, StateAnimationConfig.USER_CONTROLLED);
+    }
+
+    public void setCurrentAnimation(AnimatorPlaybackController controller,
+            @AnimationPropertyFlags int animationProps) {
         clearCurrentAnimation();
         setCurrentAnimation(controller.getTarget());
-        mConfig.userControlled = true;
+        mConfig.animProps = animationProps;
         mConfig.playbackController = controller;
     }
 
diff --git a/src/com/android/launcher3/statemanager/StatefulActivity.java b/src/com/android/launcher3/statemanager/StatefulActivity.java
index 520f33c..30ba703 100644
--- a/src/com/android/launcher3/statemanager/StatefulActivity.java
+++ b/src/com/android/launcher3/statemanager/StatefulActivity.java
@@ -126,16 +126,8 @@
 
     @Override
     public void reapplyUi() {
-        reapplyUi(true /* cancelCurrentAnimation */);
-    }
-
-    /**
-     * Re-applies if any state transition is not running, optionally cancelling
-     * the transition if requested.
-     */
-    public void reapplyUi(boolean cancelCurrentAnimation) {
         getRootView().dispatchInsets();
-        getStateManager().reapplyState(cancelCurrentAnimation);
+        getStateManager().reapplyState(true /* cancelCurrentAnimation */);
     }
 
     @Override
diff --git a/src/com/android/launcher3/states/StateAnimationConfig.java b/src/com/android/launcher3/states/StateAnimationConfig.java
index 0d9e010..0ca5afd 100644
--- a/src/com/android/launcher3/states/StateAnimationConfig.java
+++ b/src/com/android/launcher3/states/StateAnimationConfig.java
@@ -40,8 +40,19 @@
     public static final int SKIP_DEPTH_CONTROLLER = 1 << 2;
     public static final int SKIP_SCRIM = 1 << 3;
 
+    @IntDef(flag = true, value = {
+            USER_CONTROLLED,
+            HANDLE_STATE_APPLY
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AnimationPropertyFlags {}
+    // Indicates that the animation is controlled by the user
+    public static final int USER_CONTROLLED = 1 << 0;
+    // Indicates that he animation can survive state UI resets due to inset or config changes
+    public static final int HANDLE_STATE_APPLY = 1 << 1;
+
     public long duration;
-    public boolean userControlled;
+    public @AnimationPropertyFlags int animProps = 0;
     public @AnimationFlags int animFlags = 0;
 
 
@@ -105,12 +116,16 @@
     public void copyTo(StateAnimationConfig target) {
         target.duration = duration;
         target.animFlags = animFlags;
-        target.userControlled = userControlled;
+        target.animProps = animProps;
         for (int i = 0; i < ANIM_TYPES_COUNT; i++) {
             target.mInterpolators[i] = mInterpolators[i];
         }
     }
 
+    public boolean isUserControlled() {
+        return (animProps & USER_CONTROLLED) != 0;
+    }
+
     /**
      * Returns the interpolator set for animId or fallback if nothing is set
      *
diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java
index 7b192dc..ccff095 100644
--- a/src/com/android/launcher3/testing/TestInformationHandler.java
+++ b/src/com/android/launcher3/testing/TestInformationHandler.java
@@ -153,11 +153,13 @@
                 }, this::getCurrentActivity);
             }
 
-            case TestProtocol.REQUEST_IME_INSETS: {
+            case TestProtocol.REQUEST_SYSTEM_GESTURE_REGION: {
                 return getUIProperty(Bundle::putParcelable, activity -> {
                     WindowInsetsCompat insets = WindowInsetsCompat.toWindowInsetsCompat(
                             activity.getWindow().getDecorView().getRootWindowInsets());
-                    return insets.getInsets(WindowInsetsCompat.Type.ime()).toPlatformInsets();
+                    return insets.getInsets(WindowInsetsCompat.Type.ime()
+                            | WindowInsetsCompat.Type.systemGestures())
+                            .toPlatformInsets();
                 }, this::getCurrentActivity);
             }
 
diff --git a/src/com/android/launcher3/touch/AllAppsSwipeController.java b/src/com/android/launcher3/touch/AllAppsSwipeController.java
index 8b9bc19..fe4a83b 100644
--- a/src/com/android/launcher3/touch/AllAppsSwipeController.java
+++ b/src/com/android/launcher3/touch/AllAppsSwipeController.java
@@ -192,7 +192,7 @@
     protected StateAnimationConfig getConfigForStates(LauncherState fromState,
             LauncherState toState) {
         StateAnimationConfig config = super.getConfigForStates(fromState, toState);
-        config.userControlled = true;
+        config.animProps |= StateAnimationConfig.USER_CONTROLLED;
         if (fromState == NORMAL && toState == ALL_APPS) {
             applyNormalToAllAppsAnimConfig(mLauncher, config);
         } else if (fromState == ALL_APPS && toState == NORMAL) {
@@ -209,13 +209,13 @@
             config.setInterpolator(ANIM_SCRIM_FADE,
                     Interpolators.reverse(ALL_APPS_SCRIM_RESPONDER));
             config.setInterpolator(ANIM_ALL_APPS_FADE, FINAL_FRAME);
-            if (!config.userControlled) {
+            if (!config.isUserControlled()) {
                 config.setInterpolator(ANIM_VERTICAL_PROGRESS, EMPHASIZED);
             }
             config.setInterpolator(ANIM_WORKSPACE_SCALE, DECELERATED_EASE);
             config.setInterpolator(ANIM_DEPTH, DECELERATED_EASE);
         } else {
-            if (config.userControlled) {
+            if (config.isUserControlled()) {
                 config.setInterpolator(ANIM_DEPTH, Interpolators.reverse(BLUR_MANUAL));
                 config.setInterpolator(ANIM_WORKSPACE_FADE,
                         Interpolators.reverse(WORKSPACE_FADE_MANUAL));
@@ -250,29 +250,32 @@
         if (launcher.getDeviceProfile().isTablet) {
             config.setInterpolator(ANIM_ALL_APPS_FADE, INSTANT);
             config.setInterpolator(ANIM_SCRIM_FADE, ALL_APPS_SCRIM_RESPONDER);
-            if (!config.userControlled) {
+            if (!config.isUserControlled()) {
                 config.setInterpolator(ANIM_VERTICAL_PROGRESS, EMPHASIZED);
             }
             config.setInterpolator(ANIM_WORKSPACE_SCALE, DECELERATED_EASE);
             config.setInterpolator(ANIM_DEPTH, DECELERATED_EASE);
         } else {
-            config.setInterpolator(ANIM_DEPTH, config.userControlled ? BLUR_MANUAL : BLUR_ATOMIC);
+            config.setInterpolator(ANIM_DEPTH,
+                    config.isUserControlled() ? BLUR_MANUAL : BLUR_ATOMIC);
             config.setInterpolator(ANIM_WORKSPACE_FADE,
-                    config.userControlled ? WORKSPACE_FADE_MANUAL : WORKSPACE_FADE_ATOMIC);
+                    config.isUserControlled() ? WORKSPACE_FADE_MANUAL : WORKSPACE_FADE_ATOMIC);
             config.setInterpolator(ANIM_WORKSPACE_SCALE,
-                    config.userControlled ? WORKSPACE_SCALE_MANUAL : WORKSPACE_SCALE_ATOMIC);
+                    config.isUserControlled() ? WORKSPACE_SCALE_MANUAL : WORKSPACE_SCALE_ATOMIC);
             config.setInterpolator(ANIM_HOTSEAT_FADE,
-                    config.userControlled ? HOTSEAT_FADE_MANUAL : HOTSEAT_FADE_ATOMIC);
+                    config.isUserControlled() ? HOTSEAT_FADE_MANUAL : HOTSEAT_FADE_ATOMIC);
             config.setInterpolator(ANIM_HOTSEAT_SCALE,
-                    config.userControlled ? HOTSEAT_SCALE_MANUAL : HOTSEAT_SCALE_ATOMIC);
+                    config.isUserControlled() ? HOTSEAT_SCALE_MANUAL : HOTSEAT_SCALE_ATOMIC);
             config.setInterpolator(ANIM_HOTSEAT_TRANSLATE,
-                    config.userControlled ? HOTSEAT_TRANSLATE_MANUAL : HOTSEAT_TRANSLATE_ATOMIC);
+                    config.isUserControlled()
+                            ? HOTSEAT_TRANSLATE_MANUAL
+                            : HOTSEAT_TRANSLATE_ATOMIC);
             config.setInterpolator(ANIM_SCRIM_FADE,
-                    config.userControlled ? SCRIM_FADE_MANUAL : SCRIM_FADE_ATOMIC);
+                    config.isUserControlled() ? SCRIM_FADE_MANUAL : SCRIM_FADE_ATOMIC);
             config.setInterpolator(ANIM_ALL_APPS_FADE,
-                    config.userControlled ? ALL_APPS_FADE_MANUAL : ALL_APPS_FADE_ATOMIC);
+                    config.isUserControlled() ? ALL_APPS_FADE_MANUAL : ALL_APPS_FADE_ATOMIC);
             config.setInterpolator(ANIM_VERTICAL_PROGRESS,
-                    config.userControlled
+                    config.isUserControlled()
                             ? ALL_APPS_VERTICAL_PROGRESS_MANUAL
                             : ALL_APPS_VERTICAL_PROGRESS_ATOMIC);
         }
@@ -285,7 +288,7 @@
      */
     public static void applyOverviewToAllAppsAnimConfig(
             DeviceProfile deviceProfile, StateAnimationConfig config, float threshold) {
-        config.userControlled = true;
+        config.animProps |= StateAnimationConfig.USER_CONTROLLED;
         config.animFlags = SKIP_OVERVIEW;
         if (deviceProfile.isTablet) {
             config.setInterpolator(ANIM_ALL_APPS_FADE, INSTANT);
diff --git a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
index dcbf7d1..7077ad9 100644
--- a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
@@ -285,7 +285,7 @@
         primaryIconView.setTranslationX(0);
         secondaryIconView.setTranslationX(0);
         if (enableOverviewIconMenu()) {
-            if (primaryIconView.getLayoutDirection() == LAYOUT_DIRECTION_RTL) {
+            if (isRtl) {
                 primaryIconView.setTranslationY(groupedTaskViewHeight - primarySnapshotHeight);
                 secondaryIconView.setTranslationY(0);
             } else {
@@ -295,14 +295,25 @@
         } else if (splitConfig.initiatedFromSeascape) {
             // if the split was initiated from seascape,
             // the task on the right (secondary) is slightly larger
-            primaryIconView.setTranslationY(-bottomToMidpointOffset - insetOffset
-                    + taskIconHeight);
-            secondaryIconView.setTranslationY(-bottomToMidpointOffset - insetOffset);
+            if (isRtl) {
+                primaryIconView.setTranslationY(bottomToMidpointOffset - insetOffset
+                        + taskIconHeight);
+                secondaryIconView.setTranslationY(bottomToMidpointOffset - insetOffset);
+            } else {
+                primaryIconView.setTranslationY(-bottomToMidpointOffset - insetOffset
+                        + taskIconHeight);
+                secondaryIconView.setTranslationY(-bottomToMidpointOffset - insetOffset);
+            }
         } else {
             // if not,
             // the task on the left (primary) is slightly larger
-            primaryIconView.setTranslationY(-bottomToMidpointOffset + taskIconHeight);
-            secondaryIconView.setTranslationY(-bottomToMidpointOffset);
+            if (isRtl) {
+                primaryIconView.setTranslationY(bottomToMidpointOffset + taskIconHeight);
+                secondaryIconView.setTranslationY(bottomToMidpointOffset);
+            } else {
+                primaryIconView.setTranslationY(-bottomToMidpointOffset + taskIconHeight);
+                secondaryIconView.setTranslationY(-bottomToMidpointOffset);
+            }
         }
 
         primaryIconView.setLayoutParams(primaryIconParams);
diff --git a/src/com/android/launcher3/util/CannedAnimationCoordinator.kt b/src/com/android/launcher3/util/CannedAnimationCoordinator.kt
index 18f8339..85f81f5 100644
--- a/src/com/android/launcher3/util/CannedAnimationCoordinator.kt
+++ b/src/com/android/launcher3/util/CannedAnimationCoordinator.kt
@@ -26,6 +26,8 @@
 import com.android.launcher3.anim.AnimatorPlaybackController
 import com.android.launcher3.anim.PendingAnimation
 import com.android.launcher3.statemanager.StatefulActivity
+import com.android.launcher3.states.StateAnimationConfig.HANDLE_STATE_APPLY
+import com.android.launcher3.states.StateAnimationConfig.USER_CONTROLLED
 import java.util.function.Consumer
 
 private const val TAG = "CannedAnimCoordinator"
@@ -107,8 +109,15 @@
         }
         // Link this to the state manager so that it auto-cancels when state changes
         recreatePending = false
+        // Animator coordinator takes care of reapplying the animation due to state reset. Set the
+        // flags accordingly
         animationController =
-            controller.apply { activity.stateManager.setCurrentUserControlledAnimation(this) }
+            controller.apply {
+                activity.stateManager.setCurrentAnimation(
+                    this,
+                    USER_CONTROLLED or HANDLE_STATE_APPLY
+                )
+            }
         recreateAnimation(provider)
     }
 
diff --git a/src/com/android/launcher3/util/CellContentDimensions.kt b/src/com/android/launcher3/util/CellContentDimensions.kt
index ae3bf5c..5059c2f 100644
--- a/src/com/android/launcher3/util/CellContentDimensions.kt
+++ b/src/com/android/launcher3/util/CellContentDimensions.kt
@@ -48,7 +48,10 @@
             cellContentHeight = getCellContentHeight()
 
             // Step 3. Decrease label size
-            if (cellContentHeight > cellHeightPx) {
+            if (
+                cellContentHeight > cellHeightPx &&
+                    iconTextSizePx > iconSizeSteps.minimumIconLabelSize
+            ) {
                 iconTextSizePx =
                     max(
                         iconSizeSteps.minimumIconLabelSize,
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHost.java b/src/com/android/launcher3/widget/LauncherAppWidgetHost.java
index 739e204..b1c477c 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetHost.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetHost.java
@@ -21,13 +21,22 @@
 import android.appwidget.AppWidgetHost;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.Context;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.RemoteViews;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.util.Executors;
+import com.android.launcher3.util.SafeCloseable;
+import com.android.launcher3.widget.LauncherWidgetHolder.ProviderChangedListener;
 
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.WeakHashMap;
 import java.util.function.IntConsumer;
 
 /**
@@ -37,8 +46,7 @@
  */
 class LauncherAppWidgetHost extends AppWidgetHost {
     @NonNull
-    private final ArrayList<LauncherWidgetHolder.ProviderChangedListener>
-            mProviderChangeListeners = new ArrayList<>();
+    private final List<ProviderChangedListener> mProviderChangeListeners;
 
     @NonNull
     private final Context mContext;
@@ -46,33 +54,13 @@
     @Nullable
     private final IntConsumer mAppWidgetRemovedCallback;
 
-    @NonNull
-    private final LauncherWidgetHolder mHolder;
-
     public LauncherAppWidgetHost(@NonNull Context context,
-            @Nullable IntConsumer appWidgetRemovedCallback, @NonNull LauncherWidgetHolder holder) {
+            @Nullable IntConsumer appWidgetRemovedCallback,
+            List<ProviderChangedListener> providerChangeListeners) {
         super(context, APPWIDGET_HOST_ID);
         mContext = context;
         mAppWidgetRemovedCallback = appWidgetRemovedCallback;
-        mHolder = holder;
-    }
-
-    /**
-     * Add a listener that is triggered when the providers of the widgets are changed
-     * @param listener The listener that notifies when the providers changed
-     */
-    public void addProviderChangeListener(
-            @NonNull LauncherWidgetHolder.ProviderChangedListener listener) {
-        mProviderChangeListeners.add(listener);
-    }
-
-    /**
-     * Remove the specified listener from the host
-     * @param listener The listener that is to be removed from the host
-     */
-    public void removeProviderChangeListener(
-            LauncherWidgetHolder.ProviderChangedListener listener) {
-        mProviderChangeListeners.remove(listener);
+        mProviderChangeListeners = providerChangeListeners;
     }
 
     @Override
@@ -89,7 +77,7 @@
     @NonNull
     public LauncherAppWidgetHostView onCreateView(Context context, int appWidgetId,
             AppWidgetProviderInfo appWidget) {
-        return mHolder.onCreateView(context, appWidgetId);
+        return new ListenableHostView(context);
     }
 
     /**
@@ -115,7 +103,10 @@
         if (mAppWidgetRemovedCallback == null) {
             return;
         }
-        mAppWidgetRemovedCallback.accept(appWidgetId);
+        // Route the call via model thread, in case it comes while a loader-bind is in progress
+        Executors.MODEL_EXECUTOR.execute(
+                () -> Executors.MAIN_EXECUTOR.execute(
+                        () -> mAppWidgetRemovedCallback.accept(appWidgetId)));
     }
 
     /**
@@ -126,4 +117,36 @@
         super.clearViews();
     }
 
+    public static class ListenableHostView extends LauncherAppWidgetHostView {
+
+        private Set<Runnable> mUpdateListeners = Collections.EMPTY_SET;
+
+        ListenableHostView(Context context) {
+            super(context);
+        }
+
+        @Override
+        public void updateAppWidget(RemoteViews remoteViews) {
+            super.updateAppWidget(remoteViews);
+            mUpdateListeners.forEach(Runnable::run);
+        }
+
+        @Override
+        public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+            super.onInitializeAccessibilityNodeInfo(info);
+            info.setClassName(LauncherAppWidgetHostView.class.getName());
+        }
+
+        /**
+         * Adds a callback to be run everytime the provided app widget updates.
+         * @return a closable to remove this callback
+         */
+        public SafeCloseable addUpdateListener(Runnable callback) {
+            if (mUpdateListeners == Collections.EMPTY_SET) {
+                mUpdateListeners = Collections.newSetFromMap(new WeakHashMap<>());
+            }
+            mUpdateListeners.add(callback);
+            return () -> mUpdateListeners.remove(callback);
+        }
+    }
 }
diff --git a/src/com/android/launcher3/widget/LauncherWidgetHolder.java b/src/com/android/launcher3/widget/LauncherWidgetHolder.java
index fbd48cf..23127b3 100644
--- a/src/com/android/launcher3/widget/LauncherWidgetHolder.java
+++ b/src/com/android/launcher3/widget/LauncherWidgetHolder.java
@@ -42,8 +42,12 @@
 import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.util.ResourceBasedOverride;
+import com.android.launcher3.util.SafeCloseable;
+import com.android.launcher3.widget.LauncherAppWidgetHost.ListenableHostView;
 import com.android.launcher3.widget.custom.CustomWidgetManager;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.function.IntConsumer;
 
 /**
@@ -61,15 +65,14 @@
             FLAG_STATE_IS_NORMAL | FLAG_ACTIVITY_STARTED | FLAG_ACTIVITY_RESUMED;
 
     @NonNull
-    private final Context mContext;
+    protected final Context mContext;
 
     @NonNull
     private final AppWidgetHost mWidgetHost;
 
     @NonNull
     protected final SparseArray<LauncherAppWidgetHostView> mViews = new SparseArray<>();
-    @NonNull
-    private final SparseArray<PendingAppWidgetHostView> mPendingViews = new SparseArray<>();
+    protected final List<ProviderChangedListener> mProviderChangedListeners = new ArrayList<>();
 
     protected int mFlags = FLAG_STATE_IS_NORMAL;
 
@@ -86,7 +89,8 @@
 
     protected AppWidgetHost createHost(
             Context context, @Nullable IntConsumer appWidgetRemovedCallback) {
-        return new LauncherAppWidgetHost(context, appWidgetRemovedCallback, this);
+        return new LauncherAppWidgetHost(
+                context, appWidgetRemovedCallback, mProviderChangedListeners);
     }
 
     /**
@@ -158,28 +162,6 @@
     }
 
     /**
-     * Add the pending view to the host for complete configuration in further steps
-     * @param appWidgetId The ID of the specified app widget
-     * @param view The {@link PendingAppWidgetHostView} of the app widget
-     */
-    public void addPendingView(int appWidgetId, @NonNull PendingAppWidgetHostView view) {
-        mPendingViews.put(appWidgetId, view);
-    }
-
-    /**
-     * @param appWidgetId The app widget id of the specified widget
-     * @return The {@link PendingAppWidgetHostView} of the widget if it exists, null otherwise
-     */
-    @Nullable
-    protected PendingAppWidgetHostView getPendingView(int appWidgetId) {
-        return mPendingViews.get(appWidgetId);
-    }
-
-    protected void removePendingView(int appWidgetId) {
-        mPendingViews.remove(appWidgetId);
-    }
-
-    /**
      * Called when the launcher is destroyed
      */
     public void destroy() {
@@ -201,18 +183,18 @@
      * Add a listener that is triggered when the providers of the widgets are changed
      * @param listener The listener that notifies when the providers changed
      */
-    public void addProviderChangeListener(@NonNull ProviderChangedListener listener) {
-        LauncherAppWidgetHost tempHost = (LauncherAppWidgetHost) mWidgetHost;
-        tempHost.addProviderChangeListener(listener);
+    public void addProviderChangeListener(
+            @NonNull LauncherWidgetHolder.ProviderChangedListener listener) {
+        MAIN_EXECUTOR.execute(() -> mProviderChangedListeners.add(listener));
     }
 
     /**
      * Remove the specified listener from the host
      * @param listener The listener that is to be removed from the host
      */
-    public void removeProviderChangeListener(ProviderChangedListener listener) {
-        LauncherAppWidgetHost tempHost = (LauncherAppWidgetHost) mWidgetHost;
-        tempHost.removeProviderChangeListener(listener);
+    public void removeProviderChangeListener(
+            LauncherWidgetHolder.ProviderChangedListener listener) {
+        MAIN_EXECUTOR.execute(() -> mProviderChangedListeners.remove(listener));
     }
 
     /**
@@ -316,32 +298,51 @@
     }
 
     /**
+     * Adds a callback to be run everytime the provided app widget updates.
+     * @return a closable to remove this callback
+     */
+    public SafeCloseable addOnUpdateListener(
+            int appWidgetId, LauncherAppWidgetProviderInfo appWidget, Runnable callback) {
+        if (createView(appWidgetId, appWidget) instanceof ListenableHostView lhv) {
+            return lhv.addUpdateListener(callback);
+        }
+        return () -> { };
+    }
+
+    /**
      * Create a view for the specified app widget
-     * @param context The activity context for which the view is created
+     *
      * @param appWidgetId The ID of the widget
-     * @param appWidget The {@link LauncherAppWidgetProviderInfo} of the widget
+     * @param appWidget   The {@link LauncherAppWidgetProviderInfo} of the widget
      * @return A view for the widget
      */
     @NonNull
-    public AppWidgetHostView createView(@NonNull Context context, int appWidgetId,
-            @NonNull LauncherAppWidgetProviderInfo appWidget) {
-
+    public AppWidgetHostView createView(
+            int appWidgetId, @NonNull LauncherAppWidgetProviderInfo appWidget) {
         if (appWidget.isCustomWidget()) {
-            LauncherAppWidgetHostView lahv = new LauncherAppWidgetHostView(context);
+            LauncherAppWidgetHostView lahv = new LauncherAppWidgetHostView(mContext);
             lahv.setAppWidget(0, appWidget);
-            CustomWidgetManager.INSTANCE.get(context).onViewCreated(lahv);
+            CustomWidgetManager.INSTANCE.get(mContext).onViewCreated(lahv);
             return lahv;
-        } else if ((mFlags & FLAG_LISTENING) == 0) {
+        }
+
+        LauncherAppWidgetHostView view = createViewInternal(appWidgetId, appWidget);
+        mViews.put(appWidgetId, view);
+        return view;
+    }
+
+    @NonNull
+    protected LauncherAppWidgetHostView createViewInternal(
+            int appWidgetId, @NonNull LauncherAppWidgetProviderInfo appWidget) {
+        if ((mFlags & FLAG_LISTENING) == 0) {
             // Since the launcher hasn't started listening to widget updates, we can't simply call
-            // super.createView here because the later will make a binder call to retrieve
+            // host.createView here because the later will make a binder call to retrieve
             // RemoteViews from system process.
-            LauncherAppWidgetHostView view =
-                    new PendingAppWidgetHostView(context, appWidgetId, appWidget);
-            mViews.put(appWidgetId, view);
-            return view;
+            return new PendingAppWidgetHostView(mContext, appWidgetId, appWidget);
         } else {
             try {
-                return mWidgetHost.createView(context, appWidgetId, appWidget);
+                return (LauncherAppWidgetHostView) mWidgetHost.createView(
+                        mContext, appWidgetId, appWidget);
             } catch (Exception e) {
                 if (!Utilities.isBinderSizeError(e)) {
                     throw new RuntimeException(e);
@@ -352,7 +353,7 @@
                 // will update.
                 LauncherAppWidgetHostView view = mViews.get(appWidgetId);
                 if (view == null) {
-                    view = onCreateView(mContext, appWidgetId);
+                    view = new ListenableHostView(mContext);
                 }
                 view.setAppWidget(appWidgetId, appWidget);
                 view.switchToErrorView();
@@ -372,26 +373,6 @@
     }
 
     /**
-     * Called to return a proper view when creating a view
-     *
-     * @param context     The context for which the widget view is created
-     * @param appWidgetId The ID of the added widget
-     * @return A view for the specified app widget
-     */
-    @NonNull
-    public LauncherAppWidgetHostView onCreateView(Context context, int appWidgetId) {
-        final LauncherAppWidgetHostView view;
-        if (getPendingView(appWidgetId) != null) {
-            view = getPendingView(appWidgetId);
-            removePendingView(appWidgetId);
-        } else {
-            view = new LauncherAppWidgetHostView(context);
-        }
-        mViews.put(appWidgetId, view);
-        return view;
-    }
-
-    /**
      * Clears all the views from the host
      */
     public void clearViews() {
diff --git a/src/com/android/launcher3/widget/PendingAppWidgetHostView.java b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
index 2bd4c7e..25979c2 100644
--- a/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
@@ -18,6 +18,7 @@
 
 import static com.android.launcher3.graphics.PreloadIconDrawable.newPendingIcon;
 import static com.android.launcher3.icons.FastBitmapDrawable.getDisabledColorFilter;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 
 import android.content.Context;
 import android.graphics.Canvas;
@@ -38,16 +39,18 @@
 import android.view.View.OnClickListener;
 import android.widget.RemoteViews;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
 import com.android.launcher3.icons.FastBitmapDrawable;
-import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
 import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.model.data.PackageItemInfo;
+import com.android.launcher3.util.SafeCloseable;
 import com.android.launcher3.util.Themes;
 
 import java.util.List;
@@ -64,12 +67,16 @@
     private static final int DEFERRED_ALPHA = 0x77;
 
     private final Rect mRect = new Rect();
-    private OnClickListener mClickListener;
+
+    private final LauncherAppWidgetProviderInfo mAppwidget;
     private final LauncherAppWidgetInfo mInfo;
     private final int mStartState;
     private final boolean mDisabledForSafeMode;
     private final CharSequence mLabel;
 
+    private OnClickListener mClickListener;
+    private SafeCloseable mOnDetachCleanup;
+
     private int mDragFlags;
 
     private Drawable mCenterDrawable;
@@ -81,8 +88,8 @@
     private Layout mSetupTextLayout;
 
     public PendingAppWidgetHostView(Context context, LauncherAppWidgetInfo info,
-            IconCache cache, boolean disabledForSafeMode) {
-        this(context, info, disabledForSafeMode,
+            @Nullable LauncherAppWidgetProviderInfo appWidget) {
+        this(context, info, appWidget,
                 context.getResources().getText(R.string.gadget_complete_setup_text));
 
         super.updateAppWidget(null);
@@ -91,16 +98,17 @@
         if (info.pendingItemInfo == null) {
             info.pendingItemInfo = new PackageItemInfo(info.providerName.getPackageName(),
                     info.user);
-            cache.updateIconInBackground(this, info.pendingItemInfo);
+            LauncherAppState.getInstance(context).getIconCache()
+                    .updateIconInBackground(this, info.pendingItemInfo);
         } else {
             reapplyItemInfo(info.pendingItemInfo);
         }
     }
 
     public PendingAppWidgetHostView(
-            Context context, int appWidgetId, LauncherAppWidgetProviderInfo appWidget) {
-        this(context, new LauncherAppWidgetInfo(appWidgetId, appWidget.provider), false,
-                appWidget.label);
+            Context context, int appWidgetId, @NonNull LauncherAppWidgetProviderInfo appWidget) {
+        this(context, new LauncherAppWidgetInfo(appWidgetId, appWidget.provider),
+                appWidget, appWidget.label);
         getBackground().mutate().setAlpha(DEFERRED_ALPHA);
 
         mCenterDrawable = new ColorDrawable(Color.TRANSPARENT);
@@ -109,12 +117,13 @@
     }
 
     private PendingAppWidgetHostView(Context context, LauncherAppWidgetInfo info,
-            boolean disabledForSafeMode, CharSequence label) {
+            LauncherAppWidgetProviderInfo appwidget, CharSequence label) {
         super(new ContextThemeWrapper(context, R.style.WidgetContainerTheme));
 
+        mAppwidget = appwidget;
         mInfo = info;
         mStartState = info.restoreStatus;
-        mDisabledForSafeMode = disabledForSafeMode;
+        mDisabledForSafeMode = LauncherAppState.getInstance(context).isSafeModeEnabled();
         mLabel = label;
 
         mPaint = new TextPaint();
@@ -128,9 +137,40 @@
 
     @Override
     public void updateAppWidget(RemoteViews remoteViews) {
+        checkIfRestored();
+    }
+
+    private void checkIfRestored() {
         WidgetManagerHelper widgetManagerHelper = new WidgetManagerHelper(getContext());
         if (widgetManagerHelper.isAppWidgetRestored(mInfo.appWidgetId)) {
-            reInflate();
+            MAIN_EXECUTOR.getHandler().post(this::reInflate);
+        }
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+
+        if ((mAppwidget != null)
+                && !mInfo.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)
+                && mInfo.restoreStatus != LauncherAppWidgetInfo.RESTORE_COMPLETED) {
+            // If the widget is not completely restored, but has a valid ID, then listen of
+            // updates from provider app for potential restore complete.
+            if (mOnDetachCleanup != null) {
+                mOnDetachCleanup.close();
+            }
+            mOnDetachCleanup = mLauncher.getAppWidgetHolder()
+                    .addOnUpdateListener(mInfo.appWidgetId, mAppwidget, this::checkIfRestored);
+            checkIfRestored();
+        }
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        if (mOnDetachCleanup != null) {
+            mOnDetachCleanup.close();
+            mOnDetachCleanup = null;
         }
     }
 
diff --git a/src/com/android/launcher3/widget/WidgetHostViewLoader.java b/src/com/android/launcher3/widget/WidgetHostViewLoader.java
index b18cd47..1cc00ef 100644
--- a/src/com/android/launcher3/widget/WidgetHostViewLoader.java
+++ b/src/com/android/launcher3/widget/WidgetHostViewLoader.java
@@ -1,7 +1,6 @@
 package com.android.launcher3.widget;
 
 import android.appwidget.AppWidgetHostView;
-import android.content.Context;
 import android.os.Bundle;
 import android.os.Handler;
 import android.util.Log;
@@ -117,7 +116,7 @@
                     return;
                 }
                 AppWidgetHostView hostView = mLauncher.getAppWidgetHolder().createView(
-                        (Context) mLauncher, mWidgetLoadingId, pInfo);
+                        mWidgetLoadingId, pInfo);
                 mInfo.boundWidget = hostView;
 
                 // We used up the widget Id in binding the above view.
diff --git a/src/com/android/launcher3/widget/WidgetInflater.kt b/src/com/android/launcher3/widget/WidgetInflater.kt
new file mode 100644
index 0000000..00707f5
--- /dev/null
+++ b/src/com/android/launcher3/widget/WidgetInflater.kt
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.widget
+
+import android.content.Context
+import com.android.launcher3.Launcher
+import com.android.launcher3.LauncherAppState
+import com.android.launcher3.logging.FileLog
+import com.android.launcher3.model.WidgetsModel
+import com.android.launcher3.model.data.LauncherAppWidgetInfo
+import com.android.launcher3.qsb.QsbContainerView
+
+/** Utility class for handling widget inflation taking into account all the restore state updates */
+class WidgetInflater(private val context: Context) {
+
+    private val widgetHelper = WidgetManagerHelper(context)
+
+    fun inflateAppWidget(
+        item: LauncherAppWidgetInfo,
+    ): InflationResult {
+        if (item.hasOptionFlag(LauncherAppWidgetInfo.OPTION_SEARCH_WIDGET)) {
+            item.providerName = QsbContainerView.getSearchComponentName(context)
+            if (item.providerName == null) {
+                return InflationResult(
+                    TYPE_DELETE,
+                    reason = "search widget removed because search component cannot be found"
+                )
+            }
+        }
+        if (LauncherAppState.INSTANCE.get(context).isSafeModeEnabled) {
+            return InflationResult(TYPE_PENDING)
+        }
+        val appWidgetInfo: LauncherAppWidgetProviderInfo?
+        var removalReason = ""
+        if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)) {
+            // If the provider is not ready, bind as a pending widget.
+            appWidgetInfo = null
+            removalReason = "the provider isn't ready."
+        } else if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) {
+            // The widget id is not valid. Try to find the widget based on the provider info.
+            appWidgetInfo = widgetHelper.findProvider(item.providerName, item.user)
+            if (appWidgetInfo == null) {
+                if (WidgetsModel.GO_DISABLE_WIDGETS) {
+                    removalReason = "widgets are disabled on go device."
+                } else {
+                    removalReason = "WidgetManagerHelper cannot find a provider from provider info."
+                }
+            }
+        } else {
+            appWidgetInfo =
+                widgetHelper.getLauncherAppWidgetInfo(item.appWidgetId, item.targetComponent)
+            if (appWidgetInfo == null) {
+                if (item.appWidgetId <= LauncherAppWidgetInfo.CUSTOM_WIDGET_ID) {
+                    removalReason = "CustomWidgetManager cannot find provider from that widget id."
+                } else {
+                    removalReason =
+                        ("AppWidgetManager cannot find provider for that widget id." +
+                            " It could be because AppWidgetService is not available, or the" +
+                            " appWidgetId has not been bound to a the provider yet, or you" +
+                            " don't have access to that appWidgetId.")
+                }
+            }
+        }
+
+        var update = false
+
+        // If the provider is ready, but the width is not yet restored, try to restore it.
+        if (
+            !item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) &&
+                item.restoreStatus != LauncherAppWidgetInfo.RESTORE_COMPLETED
+        ) {
+            if (appWidgetInfo == null) {
+                return InflationResult(
+                    type = TYPE_DELETE,
+                    reason =
+                        "Removing restored widget: id=${item.appWidgetId} belongs to component" +
+                            " ${item.providerName} user ${item.user}" +
+                            ", as the provider is null and $removalReason"
+                )
+            }
+
+            // If we do not have a valid id, try to bind an id.
+            if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) {
+                if (!item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_ALLOCATED)) {
+                    // Id has not been allocated yet. Allocate a new id.
+                    LauncherWidgetHolder.newInstance(context).let {
+                        item.appWidgetId = it.allocateAppWidgetId()
+                        it.destroy()
+                    }
+                    item.restoreStatus =
+                        item.restoreStatus or LauncherAppWidgetInfo.FLAG_ID_ALLOCATED
+
+                    // Also try to bind the widget. If the bind fails, the user will be shown
+                    // a click to setup UI, which will ask for the bind permission.
+                    val pendingInfo = PendingAddWidgetInfo(appWidgetInfo, item.sourceContainer)
+                    pendingInfo.spanX = item.spanX
+                    pendingInfo.spanY = item.spanY
+                    pendingInfo.minSpanX = item.minSpanX
+                    pendingInfo.minSpanY = item.minSpanY
+                    var options = pendingInfo.getDefaultSizeOptions(context)
+                    val isDirectConfig =
+                        item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG)
+                    if (isDirectConfig && item.bindOptions != null) {
+                        val newOptions = item.bindOptions.extras
+                        if (options != null) {
+                            newOptions!!.putAll(options)
+                        }
+                        options = newOptions
+                    }
+                    val success =
+                        widgetHelper.bindAppWidgetIdIfAllowed(
+                            item.appWidgetId,
+                            appWidgetInfo,
+                            options
+                        )
+
+                    // We tried to bind once. If we were not able to bind, we would need to
+                    // go through the permission dialog, which means we cannot skip the config
+                    // activity.
+                    item.bindOptions = null
+                    item.restoreStatus =
+                        item.restoreStatus and LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG.inv()
+
+                    // Bind succeeded
+                    if (success) {
+                        // If the widget has a configure activity, it is still needs to set it
+                        // up, otherwise the widget is ready to go.
+                        item.restoreStatus =
+                            if ((appWidgetInfo.configure == null) || isDirectConfig)
+                                LauncherAppWidgetInfo.RESTORE_COMPLETED
+                            else LauncherAppWidgetInfo.FLAG_UI_NOT_READY
+                    }
+                    update = true
+                }
+            } else if (
+                (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_UI_NOT_READY) &&
+                    (appWidgetInfo.configure == null))
+            ) {
+                // The widget was marked as UI not ready, but there is no configure activity to
+                // update the UI.
+                item.restoreStatus = LauncherAppWidgetInfo.RESTORE_COMPLETED
+                update = true
+            } else if (
+                (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_UI_NOT_READY) &&
+                    appWidgetInfo.configure != null)
+            ) {
+                if (widgetHelper.isAppWidgetRestored(item.appWidgetId)) {
+                    item.restoreStatus = LauncherAppWidgetInfo.RESTORE_COMPLETED
+                    update = true
+                }
+            }
+        }
+
+        if (item.restoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED) {
+            // Verify that we own the widget
+            if (appWidgetInfo == null) {
+                FileLog.e(Launcher.TAG, "Removing invalid widget: id=" + item.appWidgetId)
+                return InflationResult(TYPE_DELETE, reason = removalReason)
+            }
+            item.minSpanX = appWidgetInfo.minSpanX
+            item.minSpanY = appWidgetInfo.minSpanY
+            return InflationResult(TYPE_REAL, isUpdate = update, widgetInfo = appWidgetInfo)
+        } else {
+            return InflationResult(TYPE_PENDING, isUpdate = update, widgetInfo = appWidgetInfo)
+        }
+    }
+
+    data class InflationResult(
+        val type: Int,
+        val reason: String? = null,
+        val isUpdate: Boolean = false,
+        val widgetInfo: LauncherAppWidgetProviderInfo? = null
+    )
+
+    companion object {
+        const val TYPE_DELETE = 0
+
+        const val TYPE_PENDING = 1
+
+        const val TYPE_REAL = 2
+    }
+}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java b/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
index c3ab08c..54c9324 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
@@ -18,6 +18,7 @@
 import static com.android.launcher3.Flags.enableUnfoldedTwoPanePicker;
 
 import android.content.Context;
+import android.content.res.Configuration;
 import android.graphics.Outline;
 import android.os.Process;
 import android.util.AttributeSet;
@@ -124,6 +125,9 @@
     }
 
     @Override
+    protected void onConfigurationChanged(Configuration newConfig) {}
+
+    @Override
     protected void onLayout(boolean changed, int l, int t, int r, int b) {
         super.onLayout(changed, l, t, r, b);
         if (changed && mDeviceProfile.isTwoPanels && enableUnfoldedTwoPanePicker()) {
diff --git a/tests/AndroidManifest-common.xml b/tests/AndroidManifest-common.xml
index bd9da0a..e5e3354 100644
--- a/tests/AndroidManifest-common.xml
+++ b/tests/AndroidManifest-common.xml
@@ -149,7 +149,8 @@
             android:name="com.android.launcher3.testcomponent.BaseTestingActivity"
             android:label="LauncherTestApp"
             android:exported="true"
-            android:taskAffinity="com.android.launcher3.testcomponent.Affinity1">
+            android:taskAffinity="com.android.launcher3.testcomponent.Affinity1"
+            android:theme="@style/Theme.TestActivities">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
                 <category android:name="android.intent.category.LAUNCHER"/>
@@ -343,6 +344,24 @@
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity-alias>
+        <activity-alias android:name="AAAActivity"
+            android:label="AAA"
+            android:exported="true"
+            android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity-alias>
+        <activity-alias android:name="ZZZActivity"
+            android:label="ZZZ"
+            android:exported="true"
+            android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity-alias>
 
         <!-- [b/197780098] Disable eager initialization of Jetpack libraries. -->
         <provider
diff --git a/tests/res/values/styles.xml b/tests/res/values/styles.xml
new file mode 100644
index 0000000..1e1a2cd
--- /dev/null
+++ b/tests/res/values/styles.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2024 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.
+  -->
+
+<resources>
+    <style name="Theme.TestActivities" parent="@android:style/Theme.DeviceDefault.DayNight">
+        <!-- Hardcoded ActionBar height to avoid changes while emulating -->
+        <!-- (56dp - default for handheld) -->
+        <item name="android:actionBarSize">168px</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/tests/res/xml/invalid_responsive_spec_1.xml b/tests/res/xml/invalid_responsive_spec_1.xml
index d1bcf65..7845d1e 100644
--- a/tests/res/xml/invalid_responsive_spec_1.xml
+++ b/tests/res/xml/invalid_responsive_spec_1.xml
@@ -27,7 +27,7 @@
     <workspaceSpec
         launcher:maxAvailableSize="9999dp"
         launcher:dimensionType="width">
-        <cellSize launcher:ofRemainderSpace="0.25" />
+        <cellSize launcher:ofRemainderSpace="1" />
         <endPadding launcher:fixedSize="22dp" />
         <gutter launcher:fixedSize="16dp" />
         <startPadding launcher:fixedSize="22dp" />
diff --git a/tests/res/xml/invalid_responsive_spec_2.xml b/tests/res/xml/invalid_responsive_spec_2.xml
index 49e1f93..ae30bb9 100644
--- a/tests/res/xml/invalid_responsive_spec_2.xml
+++ b/tests/res/xml/invalid_responsive_spec_2.xml
@@ -28,7 +28,7 @@
         <workspaceSpec
             launcher:maxAvailableSize="9999dp"
             launcher:dimensionType="width">
-            <cellSize launcher:ofRemainderSpace="0.25" />
+            <cellSize launcher:ofRemainderSpace="1" />
             <endPadding launcher:fixedSize="22dp" />
             <gutter launcher:fixedSize="16dp" />
             <startPadding launcher:fixedSize="22dp" />
@@ -38,7 +38,7 @@
     <workspaceSpec
         launcher:maxAvailableSize="9999dp"
         launcher:dimensionType="width">
-        <cellSize launcher:ofRemainderSpace="0.25" />
+        <cellSize launcher:ofRemainderSpace="1" />
         <endPadding launcher:fixedSize="22dp" />
         <gutter launcher:fixedSize="16dp" />
         <startPadding launcher:fixedSize="22dp" />
diff --git a/tests/res/xml/valid_responsive_spec_unsorted.xml b/tests/res/xml/valid_responsive_spec_unsorted.xml
index 9a463d5..8676f48 100644
--- a/tests/res/xml/valid_responsive_spec_unsorted.xml
+++ b/tests/res/xml/valid_responsive_spec_unsorted.xml
@@ -22,7 +22,7 @@
             <startPadding launcher:fixedSize="0dp" />
             <endPadding launcher:fixedSize="34dp" />
             <gutter launcher:fixedSize="12dp" />
-            <cellSize launcher:ofRemainderSpace="0.25" />
+            <cellSize launcher:ofRemainderSpace="1" />
         </workspaceSpec>
 
         <!-- Height spec -->
@@ -32,7 +32,7 @@
             <startPadding launcher:fixedSize="0dp" />
             <endPadding launcher:fixedSize="24dp" />
             <gutter launcher:fixedSize="12dp" />
-            <cellSize launcher:ofRemainderSpace="0.25" />
+            <cellSize launcher:ofRemainderSpace="1" />
         </workspaceSpec>
 
         <!-- Width spec -->
@@ -42,7 +42,7 @@
             <startPadding launcher:fixedSize="16dp" />
             <endPadding launcher:fixedSize="64dp" />
             <gutter launcher:fixedSize="12dp" />
-            <cellSize launcher:ofRemainderSpace="0.25" />
+            <cellSize launcher:ofRemainderSpace="1" />
         </workspaceSpec>
         <workspaceSpec
             launcher:dimensionType="width"
@@ -50,7 +50,7 @@
             <startPadding launcher:fixedSize="36dp" />
             <endPadding launcher:fixedSize="80dp" />
             <gutter launcher:fixedSize="12dp" />
-            <cellSize launcher:ofRemainderSpace="0.25" />
+            <cellSize launcher:ofRemainderSpace="1" />
         </workspaceSpec>
         <workspaceSpec
             launcher:dimensionType="width"
@@ -58,7 +58,7 @@
             <startPadding launcher:fixedSize="0dp" />
             <endPadding launcher:fixedSize="36dp" />
             <gutter launcher:fixedSize="12dp" />
-            <cellSize launcher:ofRemainderSpace="0.25" />
+            <cellSize launcher:ofRemainderSpace="1" />
         </workspaceSpec>
     </specs>
 
@@ -71,7 +71,7 @@
             <startPadding launcher:fixedSize="2dp" />
             <endPadding launcher:fixedSize="2dp" />
             <gutter launcher:fixedSize="8dp" />
-            <cellSize launcher:ofRemainderSpace="0.25" />
+            <cellSize launcher:ofRemainderSpace="1" />
         </workspaceSpec>
 
         <!-- Width spec -->
@@ -81,7 +81,7 @@
             <startPadding launcher:fixedSize="1dp" />
             <endPadding launcher:fixedSize="1dp" />
             <gutter launcher:fixedSize="8dp" />
-            <cellSize launcher:ofRemainderSpace="0.25" />
+            <cellSize launcher:ofRemainderSpace="1" />
         </workspaceSpec>
     </specs>
 
@@ -122,7 +122,7 @@
             <startPadding launcher:fixedSize="22dp" />
             <endPadding launcher:fixedSize="22dp" />
             <gutter launcher:fixedSize="16dp" />
-            <cellSize launcher:ofRemainderSpace="0.25" />
+            <cellSize launcher:ofRemainderSpace="1" />
         </workspaceSpec>
     </specs>
 </workspaceSpecs>
diff --git a/tests/res/xml/valid_workspace_file.xml b/tests/res/xml/valid_workspace_file.xml
index 9c44502..dc9963a 100644
--- a/tests/res/xml/valid_workspace_file.xml
+++ b/tests/res/xml/valid_workspace_file.xml
@@ -54,7 +54,7 @@
             <startPadding launcher:fixedSize="22dp" />
             <endPadding launcher:fixedSize="22dp" />
             <gutter launcher:fixedSize="16dp" />
-            <cellSize launcher:ofRemainderSpace="0.25" />
+            <cellSize launcher:ofRemainderSpace="1" />
         </workspaceSpec>
     </specs>
 
@@ -67,7 +67,7 @@
             <startPadding launcher:fixedSize="0dp" />
             <endPadding launcher:fixedSize="24dp" />
             <gutter launcher:fixedSize="12dp" />
-            <cellSize launcher:ofRemainderSpace="0.25" />
+            <cellSize launcher:ofRemainderSpace="1" />
         </workspaceSpec>
         <workspaceSpec
             launcher:dimensionType="height"
@@ -75,7 +75,7 @@
             <startPadding launcher:fixedSize="0dp" />
             <endPadding launcher:fixedSize="34dp" />
             <gutter launcher:fixedSize="12dp" />
-            <cellSize launcher:ofRemainderSpace="0.25" />
+            <cellSize launcher:ofRemainderSpace="1" />
         </workspaceSpec>
 
         <!-- Width spec -->
@@ -85,7 +85,7 @@
             <startPadding launcher:fixedSize="0dp" />
             <endPadding launcher:fixedSize="36dp" />
             <gutter launcher:fixedSize="12dp" />
-            <cellSize launcher:ofRemainderSpace="0.25" />
+            <cellSize launcher:ofRemainderSpace="1" />
         </workspaceSpec>
         <workspaceSpec
             launcher:dimensionType="width"
@@ -93,7 +93,7 @@
             <startPadding launcher:fixedSize="16dp" />
             <endPadding launcher:fixedSize="64dp" />
             <gutter launcher:fixedSize="12dp" />
-            <cellSize launcher:ofRemainderSpace="0.25" />
+            <cellSize launcher:ofRemainderSpace="1" />
         </workspaceSpec>
         <workspaceSpec
             launcher:dimensionType="width"
@@ -101,7 +101,7 @@
             <startPadding launcher:fixedSize="36dp" />
             <endPadding launcher:fixedSize="80dp" />
             <gutter launcher:fixedSize="12dp" />
-            <cellSize launcher:ofRemainderSpace="0.25" />
+            <cellSize launcher:ofRemainderSpace="1" />
         </workspaceSpec>
     </specs>
 </workspaceSpecs>
diff --git a/tests/res/xml/valid_workspace_unsorted_file.xml b/tests/res/xml/valid_workspace_unsorted_file.xml
index 6bf7c78..f783e9f 100644
--- a/tests/res/xml/valid_workspace_unsorted_file.xml
+++ b/tests/res/xml/valid_workspace_unsorted_file.xml
@@ -52,7 +52,7 @@
             <startPadding launcher:fixedSize="22dp" />
             <endPadding launcher:fixedSize="22dp" />
             <gutter launcher:fixedSize="16dp" />
-            <cellSize launcher:ofRemainderSpace="0.25" />
+            <cellSize launcher:ofRemainderSpace="1" />
         </workspaceSpec>
     </specs>
 </workspaceSpecs>
diff --git a/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java b/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java
index ccbe382..3e188e6 100644
--- a/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java
+++ b/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java
@@ -107,7 +107,7 @@
     public static final String REQUEST_WIDGETS_SCROLL_Y = "widgets-scroll-y";
     public static final String REQUEST_TARGET_INSETS = "target-insets";
     public static final String REQUEST_WINDOW_INSETS = "window-insets";
-    public static final String REQUEST_IME_INSETS = "ime-insets";
+    public static final String REQUEST_SYSTEM_GESTURE_REGION = "gesture-region";
     public static final String REQUEST_PID = "pid";
     public static final String REQUEST_FORCE_GC = "gc";
     public static final String REQUEST_RECENT_TASKS_LIST = "recent-tasks-list";
diff --git a/tests/src/com/android/launcher3/allapps/AlphabeticalAppsListTest.java b/tests/src/com/android/launcher3/allapps/AlphabeticalAppsListTest.java
index 259f519..3068785 100644
--- a/tests/src/com/android/launcher3/allapps/AlphabeticalAppsListTest.java
+++ b/tests/src/com/android/launcher3/allapps/AlphabeticalAppsListTest.java
@@ -19,6 +19,9 @@
 import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
 
 import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_PRIVATE_SPACE_HEADER;
+import static com.android.launcher3.allapps.SectionDecorationInfo.ROUND_BOTTOM_LEFT;
+import static com.android.launcher3.allapps.SectionDecorationInfo.ROUND_BOTTOM_RIGHT;
+import static com.android.launcher3.allapps.SectionDecorationInfo.ROUND_NOTHING;
 import static com.android.launcher3.allapps.UserProfileManager.STATE_DISABLED;
 import static com.android.launcher3.allapps.UserProfileManager.STATE_ENABLED;
 import static com.android.launcher3.allapps.UserProfileManager.STATE_TRANSITION;
@@ -61,6 +64,8 @@
     private static final int PRIVATE_SPACE_HEADER_ITEM_COUNT = 1;
     private static final int MAIN_USER_APP_COUNT = 2;
     private static final int PRIVATE_USER_APP_COUNT = 1;
+    private static final int NUM_APP_COLS = 4;
+    private static final int NUM_APP_ROWS = 3;
 
     private AlphabeticalAppsList<?> mAlphabeticalAppsList;
     @Mock
@@ -81,6 +86,7 @@
                 info != null && info.user.equals(PRIVATE_HANDLE));
         mAlphabeticalAppsList = new AlphabeticalAppsList<>(mContext, mAllAppsStore,
                 null, mPrivateProfileManager);
+        mAlphabeticalAppsList.setNumAppsPerRowAllApps(NUM_APP_COLS);
     }
 
     @Test
@@ -182,6 +188,94 @@
                 .toList().size());
     }
 
+    @Test
+    public void getRoundRegions_whenIndexIsMiddleOfLastRow_roundNothing() {
+        int index = 3;
+
+        int roundRegions = mAlphabeticalAppsList.getRoundRegions(index,
+                NUM_APP_COLS * NUM_APP_ROWS);
+
+        assertEquals(ROUND_NOTHING, roundRegions);
+    }
+
+    @Test
+    public void getRoundRegions_whenIndexIsInEndOfLastRow_roundBottomRight() {
+        int index = 11;
+
+        int roundRegions = mAlphabeticalAppsList.getRoundRegions(index,
+                NUM_APP_COLS * NUM_APP_ROWS);
+
+        assertEquals(ROUND_BOTTOM_RIGHT, roundRegions);
+    }
+
+    @Test
+    public void getRoundRegions_whenIndexIsInBeginningOfLastRow_roundBottomLeft() {
+        int index = 8;
+
+        int roundRegions = mAlphabeticalAppsList.getRoundRegions(index,
+                NUM_APP_COLS * NUM_APP_ROWS);
+
+        assertEquals(ROUND_BOTTOM_LEFT, roundRegions);
+    }
+
+    @Test
+    public void getRoundRegions_whenIndexIsInMiddleOfLastRow_roundNothing() {
+        int index = 9;
+
+        int roundRegions = mAlphabeticalAppsList.getRoundRegions(index,
+                NUM_APP_COLS * NUM_APP_ROWS);
+
+        assertEquals(ROUND_NOTHING, roundRegions);
+    }
+
+    @Test
+    public void getRoundRegions_whenIndexIsInMiddleRow_roundNothing() {
+        int index = 5;
+
+        int roundRegions = mAlphabeticalAppsList.getRoundRegions(index,
+                NUM_APP_COLS * NUM_APP_ROWS);
+
+        assertEquals(ROUND_NOTHING, roundRegions);
+    }
+
+    @Test
+    public void getRoundRegions_whenIndexIsInBeginningOfTopRow_roundNothing() {
+        int index = 0;
+
+        int roundRegions = mAlphabeticalAppsList.getRoundRegions(index,
+                NUM_APP_COLS * NUM_APP_ROWS);
+
+        assertEquals(ROUND_NOTHING, roundRegions);
+    }
+
+    @Test
+    public void getRoundRegions_whenIndexIsInLastOfTopRow_roundNothing() {
+        int index = 3;
+
+        int roundRegions = mAlphabeticalAppsList.getRoundRegions(index,
+                NUM_APP_COLS * NUM_APP_ROWS);
+
+        assertEquals(ROUND_NOTHING, roundRegions);
+    }
+
+    @Test
+    public void getRoundRegions_whenIndexIsInMiddleOfLastRowLastItem_roundBottomRight() {
+        int index = 9;
+
+        int roundRegions = mAlphabeticalAppsList.getRoundRegions(index, index+1);
+
+        assertEquals(ROUND_BOTTOM_RIGHT, roundRegions);
+    }
+
+    @Test
+    public void getRoundRegions_whenIndexIsInBeginningOfLastRowLastItem_roundBottomRight() {
+        int index = 8;
+
+        int roundRegions = mAlphabeticalAppsList.getRoundRegions(index, index+1);
+
+        assertEquals(ROUND_BOTTOM_RIGHT | ROUND_BOTTOM_LEFT, roundRegions);
+    }
+
     private int addPrivateSpaceHeader(List<BaseAllAppsAdapter.AdapterItem> adapterItemList) {
         adapterItemList.add(new BaseAllAppsAdapter.AdapterItem(VIEW_TYPE_PRIVATE_SPACE_HEADER));
         return adapterItemList.size();
diff --git a/tests/src/com/android/launcher3/allapps/TaplOpenCloseAllAppsTest.java b/tests/src/com/android/launcher3/allapps/TaplOpenCloseAllAppsTest.java
index 92ff355..da0beb1 100644
--- a/tests/src/com/android/launcher3/allapps/TaplOpenCloseAllAppsTest.java
+++ b/tests/src/com/android/launcher3/allapps/TaplOpenCloseAllAppsTest.java
@@ -130,6 +130,7 @@
      */
     @Test
     @PortraitLandscape
+    @PlatinumTest(focusArea = "launcher")
     public void testAllAppsFromHome() {
         // Test opening all apps
         assertNotNull("switchToAllApps() returned null",
@@ -181,10 +182,10 @@
             executeOnLauncher(launcher -> assertTrue("flingBackward() didn't scroll App Apps",
                     flingBackwardY < flingForwardY));
 
-            // Test scrolling down to YouTube.
-            assertNotNull("All apps: can't find YouTube", allApps.getAppIcon("YouTube"));
-            // Test scrolling up to Camera.
-            assertNotNull("All apps: can't find Camera", allApps.getAppIcon("Camera"));
+            // Test scrolling down to the end of the app list.
+            assertNotNull("All apps: can't find YouTube", allApps.getAppIcon("ZZZ"));
+            // Test scrolling up to the beginning oof the app list.
+            assertNotNull("All apps: can't find Camera", allApps.getAppIcon("AAA"));
             // Test failing to find a non-existing app.
             final AllApps allAppsFinal = allApps;
             expectFail("All apps: could find a non-existing app",
diff --git a/tests/src/com/android/launcher3/compat/TaplPromiseIconUiTest.java b/tests/src/com/android/launcher3/compat/TaplPromiseIconUiTest.java
index 2dc1cb2..bfa0d34 100644
--- a/tests/src/com/android/launcher3/compat/TaplPromiseIconUiTest.java
+++ b/tests/src/com/android/launcher3/compat/TaplPromiseIconUiTest.java
@@ -33,7 +33,6 @@
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.compatibility.common.util.SystemUtil;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.ui.AbstractLauncherUiTest;
@@ -141,10 +140,8 @@
     @RequiresFlagsEnabled(FLAG_ENABLE_SUPPORT_FOR_ARCHIVING)
     public void testPromiseIcon_addedArchivedApp() throws Throwable {
         installDummyAppAndWaitForUIUpdate();
-        assertThat(
-                SystemUtil.runShellCommand(
-                        String.format("pm archive %s", DUMMY_PACKAGE))).isEqualTo(
-                "Success\n");
+        assertThat(mDevice.executeShellCommand(String.format("pm archive %s", DUMMY_PACKAGE)))
+                .isEqualTo("Success\n");
 
         final ItemOperator findPromiseApp = (info, view) ->
                 info != null && TextUtils.equals(info.title, DUMMY_LABEL);
diff --git a/tests/src/com/android/launcher3/dragging/TaplDragTest.java b/tests/src/com/android/launcher3/dragging/TaplDragTest.java
index e040367..94a96aa 100644
--- a/tests/src/com/android/launcher3/dragging/TaplDragTest.java
+++ b/tests/src/com/android/launcher3/dragging/TaplDragTest.java
@@ -20,6 +20,8 @@
 import static com.android.launcher3.util.TestConstants.AppNames.MAPS_APP_NAME;
 import static com.android.launcher3.util.TestConstants.AppNames.STORE_APP_NAME;
 import static com.android.launcher3.ui.AbstractLauncherUiTest.initialize;
+import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL;
+import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
@@ -40,9 +42,9 @@
 import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
 import com.android.launcher3.util.TestUtil;
 import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
+import com.android.launcher3.util.rule.TestStabilityRule;
 
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 
 /**
@@ -71,7 +73,8 @@
     @Test
     @PortraitLandscape
     @ScreenRecord
-    @Ignore // b/233075289
+    // Staging; will be promoted to presubmit if stable
+    @TestStabilityRule.Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT)
     @PlatinumTest(focusArea = "launcher")
     public void testDragToFolder() {
         // TODO: add the use case to drag an icon to an existing folder. Currently it either fails
diff --git a/tests/src/com/android/launcher3/folder/PreviewItemManagerTest.kt b/tests/src/com/android/launcher3/folder/PreviewItemManagerTest.kt
new file mode 100644
index 0000000..4ec5b0e
--- /dev/null
+++ b/tests/src/com/android/launcher3/folder/PreviewItemManagerTest.kt
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.folder
+
+import android.R
+import android.content.Context
+import android.os.Process
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
+import com.android.launcher3.LauncherPrefs
+import com.android.launcher3.LauncherPrefs.Companion.get
+import com.android.launcher3.icons.BaseIconFactory
+import com.android.launcher3.icons.FastBitmapDrawable
+import com.android.launcher3.icons.UserBadgeDrawable
+import com.android.launcher3.model.data.WorkspaceItemInfo
+import com.android.launcher3.util.ActivityContextWrapper
+import com.android.launcher3.util.FlagOp
+import com.android.launcher3.util.LauncherLayoutBuilder
+import com.android.launcher3.util.LauncherModelHelper
+import com.android.launcher3.util.UserIconInfo
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** Tests for [PreviewItemManager] */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class PreviewItemManagerTest {
+
+    private lateinit var previewItemManager: PreviewItemManager
+    private lateinit var context: Context
+    private lateinit var folderItems: ArrayList<WorkspaceItemInfo>
+    private lateinit var modelHelper: LauncherModelHelper
+    private lateinit var folderIcon: FolderIcon
+
+    @Before
+    fun setup() {
+        getInstrumentation().runOnMainSync {
+            folderIcon =
+                FolderIcon(ActivityContextWrapper(ApplicationProvider.getApplicationContext()))
+        }
+        context = getInstrumentation().targetContext
+        previewItemManager = PreviewItemManager(folderIcon)
+        modelHelper = LauncherModelHelper()
+        modelHelper
+            .setupDefaultLayoutProvider(
+                LauncherLayoutBuilder()
+                    .atWorkspace(0, 0, 1)
+                    .putFolder(R.string.copy)
+                    .addApp(LauncherModelHelper.TEST_PACKAGE, LauncherModelHelper.TEST_ACTIVITY)
+                    .addApp(LauncherModelHelper.TEST_PACKAGE, LauncherModelHelper.TEST_ACTIVITY2)
+                    .addApp(LauncherModelHelper.TEST_PACKAGE, LauncherModelHelper.TEST_ACTIVITY3)
+                    .addApp(LauncherModelHelper.TEST_PACKAGE, LauncherModelHelper.TEST_ACTIVITY4)
+                    .build()
+            )
+            .loadModelSync()
+        folderItems = modelHelper.bgDataModel.folders.valueAt(0).contents
+        folderIcon.mInfo = modelHelper.bgDataModel.folders.valueAt(0)
+        folderIcon.mInfo.contents = folderItems
+
+        // Set first icon to be themed.
+        folderItems[0]
+            .bitmap
+            .setMonoIcon(
+                folderItems[0].bitmap.icon,
+                BaseIconFactory(
+                    context,
+                    context.resources.configuration.densityDpi,
+                    previewItemManager.mIconSize
+                )
+            )
+
+        // Set second icon to be non-themed.
+        folderItems[1]
+            .bitmap
+            .setMonoIcon(
+                null,
+                BaseIconFactory(
+                    context,
+                    context.resources.configuration.densityDpi,
+                    previewItemManager.mIconSize
+                )
+            )
+
+        // Set third icon to be themed with badge.
+        folderItems[2]
+            .bitmap
+            .setMonoIcon(
+                folderItems[2].bitmap.icon,
+                BaseIconFactory(
+                    context,
+                    context.resources.configuration.densityDpi,
+                    previewItemManager.mIconSize
+                )
+            )
+        folderItems[2].bitmap =
+            folderItems[2].bitmap.withFlags(profileFlagOp(UserIconInfo.TYPE_WORK))
+
+        // Set fourth icon to be non-themed with badge.
+        folderItems[3].bitmap =
+            folderItems[3].bitmap.withFlags(profileFlagOp(UserIconInfo.TYPE_WORK))
+        folderItems[3]
+            .bitmap
+            .setMonoIcon(
+                null,
+                BaseIconFactory(
+                    context,
+                    context.resources.configuration.densityDpi,
+                    previewItemManager.mIconSize
+                )
+            )
+    }
+    @After
+    @Throws(Exception::class)
+    fun tearDown() {
+        modelHelper.destroy()
+    }
+
+    @Test
+    fun checkThemedIconWithThemingOn_iconShouldBeThemed() {
+        get(context).put(LauncherPrefs.THEMED_ICONS, true)
+        val drawingParams = PreviewItemDrawingParams(0f, 0f, 0f)
+
+        previewItemManager.setDrawable(drawingParams, folderItems[0])
+
+        assert((drawingParams.drawable as FastBitmapDrawable).isThemed)
+    }
+
+    @Test
+    fun checkThemedIconWithThemingOff_iconShouldNotBeThemed() {
+        get(context).put(LauncherPrefs.THEMED_ICONS, false)
+        val drawingParams = PreviewItemDrawingParams(0f, 0f, 0f)
+
+        previewItemManager.setDrawable(drawingParams, folderItems[0])
+
+        assert(!(drawingParams.drawable as FastBitmapDrawable).isThemed)
+    }
+
+    @Test
+    fun checkUnthemedIconWithThemingOn_iconShouldNotBeThemed() {
+        get(context).put(LauncherPrefs.THEMED_ICONS, true)
+        val drawingParams = PreviewItemDrawingParams(0f, 0f, 0f)
+
+        previewItemManager.setDrawable(drawingParams, folderItems[1])
+
+        assert(!(drawingParams.drawable as FastBitmapDrawable).isThemed)
+    }
+
+    @Test
+    fun checkUnthemedIconWithThemingOff_iconShouldNotBeThemed() {
+        get(context).put(LauncherPrefs.THEMED_ICONS, false)
+        val drawingParams = PreviewItemDrawingParams(0f, 0f, 0f)
+
+        previewItemManager.setDrawable(drawingParams, folderItems[1])
+
+        assert(!(drawingParams.drawable as FastBitmapDrawable).isThemed)
+    }
+
+    @Test
+    fun checkThemedIconWithBadgeWithThemingOn_iconAndBadgeShouldBeThemed() {
+        get(context).put(LauncherPrefs.THEMED_ICONS, true)
+        val drawingParams = PreviewItemDrawingParams(0f, 0f, 0f)
+
+        previewItemManager.setDrawable(drawingParams, folderItems[2])
+
+        assert((drawingParams.drawable as FastBitmapDrawable).isThemed)
+        assert(
+            ((drawingParams.drawable as FastBitmapDrawable).badge as UserBadgeDrawable).mIsThemed
+        )
+    }
+
+    @Test
+    fun checkUnthemedIconWithBadgeWithThemingOn_badgeShouldBeThemed() {
+        get(context).put(LauncherPrefs.THEMED_ICONS, true)
+        val drawingParams = PreviewItemDrawingParams(0f, 0f, 0f)
+
+        previewItemManager.setDrawable(drawingParams, folderItems[3])
+
+        assert(!(drawingParams.drawable as FastBitmapDrawable).isThemed)
+        assert(
+            ((drawingParams.drawable as FastBitmapDrawable).badge as UserBadgeDrawable).mIsThemed
+        )
+    }
+
+    @Test
+    fun checkUnthemedIconWithBadgeWithThemingOff_iconAndBadgeShouldNotBeThemed() {
+        get(context).put(LauncherPrefs.THEMED_ICONS, false)
+        val drawingParams = PreviewItemDrawingParams(0f, 0f, 0f)
+
+        previewItemManager.setDrawable(drawingParams, folderItems[3])
+
+        assert(!(drawingParams.drawable as FastBitmapDrawable).isThemed)
+        assert(
+            !((drawingParams.drawable as FastBitmapDrawable).badge as UserBadgeDrawable).mIsThemed
+        )
+    }
+
+    private fun profileFlagOp(type: Int) =
+        UserIconInfo(Process.myUserHandle(), type).applyBitmapInfoFlags(FlagOp.NO_OP)
+}
diff --git a/tests/src/com/android/launcher3/model/LoaderTaskTest.kt b/tests/src/com/android/launcher3/model/LoaderTaskTest.kt
index def27b8..dbca9d1 100644
--- a/tests/src/com/android/launcher3/model/LoaderTaskTest.kt
+++ b/tests/src/com/android/launcher3/model/LoaderTaskTest.kt
@@ -1,6 +1,5 @@
 package com.android.launcher3.model
 
-import android.content.Context
 import android.os.UserHandle
 import android.platform.test.flag.junit.SetFlagsRule
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -11,16 +10,20 @@
 import com.android.launcher3.LauncherAppState
 import com.android.launcher3.LauncherModel
 import com.android.launcher3.LauncherModel.LoaderTransaction
+import com.android.launcher3.LauncherPrefs
 import com.android.launcher3.icons.IconCache
 import com.android.launcher3.icons.cache.CachingLogic
 import com.android.launcher3.icons.cache.IconCacheUpdateHandler
+import com.android.launcher3.pm.InstallSessionHelper
 import com.android.launcher3.pm.UserCache
+import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper
 import com.android.launcher3.util.Executors.MODEL_EXECUTOR
 import com.android.launcher3.util.LooperIdleLock
+import com.android.launcher3.util.MainThreadInitializedObject.SandboxContext
 import com.android.launcher3.util.UserIconInfo
-import com.android.launcher3.util.rule.StaticMockitoRule
 import com.google.common.truth.Truth
 import java.util.concurrent.CountDownLatch
+import org.junit.After
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
@@ -39,6 +42,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class LoaderTaskTest {
+    private lateinit var context: SandboxContext
     @Mock private lateinit var app: LauncherAppState
     @Mock private lateinit var bgAllAppsList: AllAppsList
     @Mock private lateinit var modelDelegate: ModelDelegate
@@ -52,21 +56,28 @@
 
     @Spy private var userManagerState: UserManagerState? = UserManagerState()
 
-    @get:Rule(order = 0) val staticMockitoRule = StaticMockitoRule(UserCache::class.java)
-    @get:Rule(order = 1)
-    val setFlagsRule = SetFlagsRule().apply { initAllFlagsToReleaseConfigDefault() }
+    @get:Rule val setFlagsRule = SetFlagsRule().apply { initAllFlagsToReleaseConfigDefault() }
 
     @Before
     fun setup() {
-        val context = InstrumentationRegistry.getInstrumentation().targetContext
+        MockitoAnnotations.initMocks(this)
+
+        context =
+            SandboxContext(
+                InstrumentationRegistry.getInstrumentation().targetContext,
+                InstallSessionHelper.INSTANCE,
+                LauncherPrefs.INSTANCE,
+                ItemInstallQueue.INSTANCE,
+                PluginManagerWrapper.INSTANCE
+            )
         val idp =
-            InvariantDeviceProfile.INSTANCE[context].apply {
+            InvariantDeviceProfile().apply {
                 numRows = 5
                 numColumns = 6
                 numDatabaseHotseatIcons = 5
             }
+        context.putObject(InvariantDeviceProfile.INSTANCE, idp)
 
-        MockitoAnnotations.initMocks(this)
         `when`(app.context).thenReturn(context)
         `when`(app.model).thenReturn(launcherModel)
         `when`(launcherModel.beginLoader(any(LoaderTask::class.java))).thenReturn(transaction)
@@ -77,7 +88,12 @@
         `when`(launcherBinder.newIdleLock(any(LoaderTask::class.java))).thenReturn(idleLock)
         `when`(idleLock.awaitLocked(1000)).thenReturn(false)
         `when`(iconCache.updateHandler).thenReturn(iconCacheUpdateHandler)
-        `when`(UserCache.getInstance(any(Context::class.java))).thenReturn(userCache)
+        context.putObject(UserCache.INSTANCE, userCache)
+    }
+
+    @After
+    fun tearDown() {
+        context.onDestroy()
     }
 
     @Test
diff --git a/tests/src/com/android/launcher3/model/ModelMultiCallbacksTest.java b/tests/src/com/android/launcher3/model/ModelMultiCallbacksTest.java
index 3dfd6b4..25a4c4e 100644
--- a/tests/src/com/android/launcher3/model/ModelMultiCallbacksTest.java
+++ b/tests/src/com/android/launcher3/model/ModelMultiCallbacksTest.java
@@ -73,6 +73,10 @@
         TestUtil.uninstallDummyApp();
     }
 
+    private ModelLauncherCallbacks getCallbacks() {
+        return mModelHelper.getModel().newModelCallbacks();
+    }
+
     @Test
     public void testTwoCallbacks_loadedTogether() throws Exception {
         setupWorkspacePages(3);
@@ -127,14 +131,14 @@
 
         // Install package 1
         TestUtil.installDummyApp();
-        mModelHelper.getModel().onPackageAdded(TestUtil.DUMMY_PACKAGE, Process.myUserHandle());
+        getCallbacks().onPackageAdded(TestUtil.DUMMY_PACKAGE, Process.myUserHandle());
         waitForLoaderAndTempMainThread();
         assertTrue(cb1.allApps().contains(TestUtil.DUMMY_PACKAGE));
         assertTrue(cb2.allApps().contains(TestUtil.DUMMY_PACKAGE));
 
         // Uninstall package 2
         TestUtil.uninstallDummyApp();
-        mModelHelper.getModel().onPackageRemoved(TestUtil.DUMMY_PACKAGE, Process.myUserHandle());
+        getCallbacks().onPackageRemoved(TestUtil.DUMMY_PACKAGE, Process.myUserHandle());
         waitForLoaderAndTempMainThread();
         assertFalse(cb1.allApps().contains(TestUtil.DUMMY_PACKAGE));
         assertFalse(cb2.allApps().contains(TestUtil.DUMMY_PACKAGE));
@@ -142,7 +146,7 @@
         // Unregister a callback and verify updates no longer received
         Executors.MAIN_EXECUTOR.execute(() -> mModelHelper.getModel().removeCallbacks(cb2));
         TestUtil.installDummyApp();
-        mModelHelper.getModel().onPackageAdded(TestUtil.DUMMY_PACKAGE, Process.myUserHandle());
+        getCallbacks().onPackageAdded(TestUtil.DUMMY_PACKAGE, Process.myUserHandle());
         waitForLoaderAndTempMainThread();
 
         // cb2 didn't get the update
diff --git a/tests/src/com/android/launcher3/responsive/ResponsiveSpecsProviderTest.kt b/tests/src/com/android/launcher3/responsive/ResponsiveSpecsProviderTest.kt
index 9681ca8..54a1dc5 100644
--- a/tests/src/com/android/launcher3/responsive/ResponsiveSpecsProviderTest.kt
+++ b/tests/src/com/android/launcher3/responsive/ResponsiveSpecsProviderTest.kt
@@ -84,7 +84,7 @@
                     startPadding = SizeSpec(1f.dpToPx()),
                     endPadding = SizeSpec(1f.dpToPx()),
                     gutter = SizeSpec(8f.dpToPx()),
-                    cellSize = SizeSpec(ofRemainderSpace = .25f)
+                    cellSize = SizeSpec(ofRemainderSpace = 1f)
                 )
             )
 
@@ -97,7 +97,7 @@
                     startPadding = SizeSpec(2f.dpToPx()),
                     endPadding = SizeSpec(2f.dpToPx()),
                     gutter = SizeSpec(8f.dpToPx()),
-                    cellSize = SizeSpec(ofRemainderSpace = .25f)
+                    cellSize = SizeSpec(ofRemainderSpace = 1f)
                 ),
             )
 
@@ -182,7 +182,7 @@
                     startPadding = SizeSpec(22f.dpToPx()),
                     endPadding = SizeSpec(22f.dpToPx()),
                     gutter = sizeSpec16,
-                    cellSize = SizeSpec(ofRemainderSpace = .25f)
+                    cellSize = SizeSpec(ofRemainderSpace = 1f)
                 )
             )
 
@@ -231,7 +231,7 @@
                     startPadding = SizeSpec(0f.dpToPx()),
                     endPadding = SizeSpec(36f.dpToPx()),
                     gutter = sizeSpec12,
-                    cellSize = SizeSpec(ofRemainderSpace = .25f)
+                    cellSize = SizeSpec(ofRemainderSpace = 1f)
                 ),
                 ResponsiveSpec(
                     maxAvailableSize = 716.dpToPx(),
@@ -240,7 +240,7 @@
                     startPadding = SizeSpec(16f.dpToPx()),
                     endPadding = SizeSpec(64f.dpToPx()),
                     gutter = sizeSpec12,
-                    cellSize = SizeSpec(ofRemainderSpace = .25f)
+                    cellSize = SizeSpec(ofRemainderSpace = 1f)
                 ),
                 ResponsiveSpec(
                     maxAvailableSize = 9999.dpToPx(),
@@ -249,7 +249,7 @@
                     startPadding = SizeSpec(36f.dpToPx()),
                     endPadding = SizeSpec(80f.dpToPx()),
                     gutter = sizeSpec12,
-                    cellSize = SizeSpec(ofRemainderSpace = .25f)
+                    cellSize = SizeSpec(ofRemainderSpace = 1f)
                 )
             )
 
@@ -262,7 +262,7 @@
                     startPadding = SizeSpec(0f),
                     endPadding = SizeSpec(24f.dpToPx()),
                     gutter = sizeSpec12,
-                    cellSize = SizeSpec(ofRemainderSpace = .25f)
+                    cellSize = SizeSpec(ofRemainderSpace = 1f)
                 ),
                 ResponsiveSpec(
                     maxAvailableSize = 9999.dpToPx(),
@@ -271,7 +271,7 @@
                     startPadding = SizeSpec(0f),
                     endPadding = SizeSpec(34f.dpToPx()),
                     gutter = sizeSpec12,
-                    cellSize = SizeSpec(ofRemainderSpace = .25f)
+                    cellSize = SizeSpec(ofRemainderSpace = 1f)
                 ),
             )
 
diff --git a/tests/src/com/android/launcher3/responsive/WorkspaceSpecsTest.kt b/tests/src/com/android/launcher3/responsive/WorkspaceSpecsTest.kt
index 9781645..17b0ee4 100644
--- a/tests/src/com/android/launcher3/responsive/WorkspaceSpecsTest.kt
+++ b/tests/src/com/android/launcher3/responsive/WorkspaceSpecsTest.kt
@@ -159,7 +159,7 @@
                     "maxSize=2147483647), " +
                     "cellSize=SizeSpec(fixedSize=0.0, " +
                     "ofAvailableSpace=0.0, " +
-                    "ofRemainderSpace=0.25, " +
+                    "ofRemainderSpace=1.0, " +
                     "matchWorkspace=false, " +
                     "maxSize=2147483647)" +
                     ")"
diff --git a/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java
index d75b387..db38c68 100644
--- a/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java
@@ -18,7 +18,6 @@
 import static org.junit.Assert.assertNotNull;
 
 import android.platform.test.annotations.PlatinumTest;
-import android.platform.test.rule.ScreenRecordRule;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.LargeTest;
@@ -29,9 +28,11 @@
 import com.android.launcher3.ui.AbstractLauncherUiTest;
 import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
 import com.android.launcher3.ui.TestViewHelpers;
+import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
 import com.android.launcher3.util.rule.ShellCommandRule;
 import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
 
+import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
@@ -47,9 +48,16 @@
     @Rule
     public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind();
 
+    @Override
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+        AbstractLauncherUiTest.initialize(this);
+    }
+
     @Test
     @PortraitLandscape
-    @ScreenRecordRule.ScreenRecord // b/289161193
+    @ScreenRecord // b/316910614
     public void testDragIcon() throws Throwable {
         mLauncher.enableDebugTracing(); // b/289161193
         new FavoriteItemsTransaction(mTargetContext).commitAndLoadHome(mLauncher);
@@ -99,6 +107,7 @@
      */
     @PlatinumTest(focusArea = "launcher")
     @Test
+    @ScreenRecord // b/316910614
     public void testResizeWidget() throws Throwable {
         new FavoriteItemsTransaction(mTargetContext).commitAndLoadHome(mLauncher);
 
diff --git a/tests/src/com/android/launcher3/util/rule/StaticMockitoRule.java b/tests/src/com/android/launcher3/util/rule/StaticMockitoRule.java
deleted file mode 100644
index 6b91474..0000000
--- a/tests/src/com/android/launcher3/util/rule/StaticMockitoRule.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.util.rule;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
-
-import com.android.dx.mockito.inline.extended.StaticMockitoSession;
-import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
-
-import org.junit.rules.MethodRule;
-import org.junit.runners.model.FrameworkMethod;
-import org.junit.runners.model.Statement;
-import org.mockito.junit.MockitoRule;
-import org.mockito.quality.Strictness;
-
-/**
- * Similar to {@link MockitoRule}, but uses {@link StaticMockitoSession}, which allows mocking
- * static methods.
- */
-public class StaticMockitoRule implements MethodRule {
-    private Class<?>[] mClasses;
-
-    public StaticMockitoRule(Class<?>... classes) {
-        mClasses = classes;
-    }
-
-    @Override
-    public Statement apply(Statement base, FrameworkMethod method, Object target) {
-        return new Statement() {
-            public void evaluate() throws Throwable {
-                StaticMockitoSessionBuilder builder =
-                        mockitoSession()
-                                .name(target.getClass().getSimpleName() + "." + method.getName())
-                                .initMocks(target)
-                                .strictness(Strictness.STRICT_STUBS);
-
-                for (Class<?> clazz : mClasses) {
-                    builder.mockStatic(clazz);
-                }
-
-                StaticMockitoSession session = builder.startMocking();
-                Throwable testFailure = evaluateSafely(base);
-                session.finishMocking(testFailure);
-                if (testFailure != null) {
-                    throw testFailure;
-                }
-            }
-
-            private Throwable evaluateSafely(Statement base) {
-                try {
-                    base.evaluate();
-                    return null;
-                } catch (Throwable throwable) {
-                    return throwable;
-                }
-            }
-        };
-    }
-}
diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java
index 3cbc2c2..2dbc29d 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java
@@ -21,6 +21,7 @@
 
 import static com.android.launcher3.tapl.LauncherInstrumentation.DEFAULT_POLL_INTERVAL;
 import static com.android.launcher3.tapl.LauncherInstrumentation.WAIT_TIME_MS;
+import static com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL;
 
 import android.graphics.Point;
 import android.graphics.Rect;
@@ -362,6 +363,7 @@
 
     /**
      * Taps outside bottom sheet to dismiss it. Available on tablets only.
+     *
      * @param tapRight Tap on the right of bottom sheet if true, or left otherwise.
      */
     public void dismissByTappingOutsideForTablet(boolean tapRight) {
@@ -390,7 +392,7 @@
     /** Presses the meta keyboard shortcut to dismiss AllApps. */
     public void dismissByKeyboardShortcut() {
         try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
-            mLauncher.getDevice().pressKeyCode(KEYCODE_META_RIGHT);
+            pressMetaKey();
             try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
                     "pressed meta key")) {
                 verifyVisibleContainerOnDismiss();
@@ -398,12 +400,19 @@
         }
     }
 
+    protected void pressMetaKey() {
+        mLauncher.getDevice().pressKeyCode(KEYCODE_META_RIGHT);
+    }
+
     /** Presses the esc key to dismiss AllApps. */
     public void dismissByEscKey() {
         try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
             mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ALT_ESC_DOWN);
             mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ALT_ESC_UP);
-            mLauncher.getDevice().pressKeyCode(KEYCODE_ESCAPE);
+            mLauncher.runToState(
+                    () -> mLauncher.getDevice().pressKeyCode(KEYCODE_ESCAPE),
+                    NORMAL_STATE_ORDINAL,
+                    "pressing esc key");
             try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
                     "pressed esc key")) {
                 verifyVisibleContainerOnDismiss();
@@ -415,6 +424,7 @@
 
     /**
      * Return the QSB UI object on the AllApps screen.
+     *
      * @return the QSB UI object.
      */
     @NonNull
diff --git a/tests/tapl/com/android/launcher3/tapl/AppIcon.java b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
index 867a1a8..156568b 100644
--- a/tests/tapl/com/android/launcher3/tapl/AppIcon.java
+++ b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
@@ -42,7 +42,8 @@
      * @param appName app icon to look for
      */
     static BySelector getAppIconSelector(String appName) {
-        return By.clazz(TextView.class).text(makeMultilinePattern(appName));
+        // focusable=true to avoid matching folder labels
+        return By.clazz(TextView.class).text(makeMultilinePattern(appName)).focusable(true);
     }
 
     /**
diff --git a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
index 6c0010d..1bc489c 100644
--- a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
+++ b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
@@ -140,8 +140,19 @@
                 flingForwardImpl();
             }
 
-            mLauncher.clickLauncherObject(
-                    mLauncher.waitForObjectInContainer(verifyActiveContainer(), clearAllSelector));
+            final Runnable clickClearAll = () -> mLauncher.clickLauncherObject(
+                    mLauncher.waitForObjectInContainer(verifyActiveContainer(),
+                            clearAllSelector));
+            if (mLauncher.is3PLauncher()) {
+                mLauncher.executeAndWaitForLauncherStop(
+                        clickClearAll,
+                        "clicking 'Clear All'");
+            } else {
+                mLauncher.runToState(
+                        clickClearAll,
+                        NORMAL_STATE_ORDINAL,
+                        "clicking 'Clear All'");
+            }
 
             mLauncher.waitUntilLauncherObjectGone(clearAllSelector);
         }
@@ -162,9 +173,12 @@
 
             OverviewTask currentTask = flingToFirstTask();
 
-            mLauncher.touchOutsideContainer(currentTask.getUiObject(),
-                    /* tapRight= */ true,
-                    /* halfwayToEdge= */ false);
+            mLauncher.runToState(
+                    () -> mLauncher.touchOutsideContainer(currentTask.getUiObject(),
+                            /* tapRight= */ true,
+                            /* halfwayToEdge= */ false),
+                    NORMAL_STATE_ORDINAL,
+                    "touching outside of first task");
 
             return new Workspace(mLauncher);
         }
@@ -197,11 +211,15 @@
     public void touchTaskbarBottomCorner(boolean tapRight) {
         try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
             Taskbar taskbar = new Taskbar(mLauncher);
-            taskbar.touchBottomCorner(tapRight);
             if (mLauncher.isTransientTaskbar()) {
+                mLauncher.runToState(
+                        () -> taskbar.touchBottomCorner(tapRight),
+                        NORMAL_STATE_ORDINAL,
+                        "touching taskbar");
                 // Tapping outside Transient Taskbar returns to Workspace, wait for that state.
                 new Workspace(mLauncher);
             } else {
+                taskbar.touchBottomCorner(tapRight);
                 // Should stay in Overview.
                 verifyActiveContainer();
                 verifyActionsViewVisibility();
diff --git a/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java b/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java
index 11a0cfb..f8e1c10 100644
--- a/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java
@@ -30,8 +30,8 @@
 
     /**
      * Swipes up or down to dismiss to Workspace.
-     * @param swipeDown Swipe all apps down to dismiss, otherwise swipe up to dismiss by going home.
      *
+     * @param swipeDown Swipe all apps down to dismiss, otherwise swipe up to dismiss by going home.
      * @return the Workspace object.
      */
     @NonNull
@@ -139,4 +139,12 @@
                 NORMAL_STATE_ORDINAL,
                 "touching outside");
     }
+
+    @Override
+    protected void pressMetaKey() {
+        mLauncher.runToState(
+                () -> super.pressMetaKey(),
+                NORMAL_STATE_ORDINAL,
+                "pressing meta key");
+    }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/HomeQsb.java b/tests/tapl/com/android/launcher3/tapl/HomeQsb.java
index 5385c65..c1fc45f 100644
--- a/tests/tapl/com/android/launcher3/tapl/HomeQsb.java
+++ b/tests/tapl/com/android/launcher3/tapl/HomeQsb.java
@@ -15,6 +15,8 @@
  */
 package com.android.launcher3.tapl;
 
+import static com.android.launcher3.testing.shared.TestProtocol.ALL_APPS_STATE_ORDINAL;
+
 import androidx.test.uiautomator.UiObject2;
 
 /**
@@ -25,4 +27,13 @@
     HomeQsb(LauncherInstrumentation launcher, UiObject2 hotseat) {
         super(launcher, hotseat, "search_container_hotseat");
     }
+
+    @Override
+    protected void clickQsb() {
+        // Clicking Qsb will switch to All Apps state.
+        mLauncher.runToState(
+                () -> super.clickQsb(),
+                ALL_APPS_STATE_ORDINAL,
+                "Clicking Qsb");
+    }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/Launchable.java b/tests/tapl/com/android/launcher3/tapl/Launchable.java
index 28e2590..ed47334 100644
--- a/tests/tapl/com/android/launcher3/tapl/Launchable.java
+++ b/tests/tapl/com/android/launcher3/tapl/Launchable.java
@@ -56,8 +56,8 @@
      */
     public LaunchedAppState launch(String expectedPackageName) {
         try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
-            try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
-                    "want to launch an app from " + launchableType())) {
+            try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(String.format(
+                    "want to launch an app (%s) from %s", expectedPackageName, launchableType()))) {
                 LauncherInstrumentation.log("Launchable.launch before click "
                         + mObject.getVisibleCenter() + " in "
                         + mLauncher.getVisibleBounds(mObject));
diff --git a/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java b/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java
index 501c4c3..200f2ff 100644
--- a/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java
+++ b/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java
@@ -130,8 +130,11 @@
             int endX = startX;
             int endY = startY - taskbarFromNavThreshold;
 
-            mLauncher.linearGesture(startX, startY, endX, endY, 10, /* slowDown= */ true,
-                    LauncherInstrumentation.GestureScope.EXPECT_PILFER);
+            mLauncher.executeAndWaitForLauncherStop(
+                    () -> mLauncher.linearGesture(startX, startY, endX, endY, 10,
+                            /* slowDown= */ true,
+                            LauncherInstrumentation.GestureScope.EXPECT_PILFER),
+                    "swiping");
             LauncherInstrumentation.log("swipeUpToUnstashTaskbar: sent linear swipe up gesture");
 
             return new Taskbar(mLauncher);
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index d17f034..f68e12c 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -385,8 +385,8 @@
                 .getParcelable(TestProtocol.TEST_INFO_RESPONSE_FIELD);
     }
 
-    Insets getImeInsets() {
-        return getTestInfo(TestProtocol.REQUEST_IME_INSETS)
+    Insets getSystemGestureRegion() {
+        return getTestInfo(TestProtocol.REQUEST_SYSTEM_GESTURE_REGION)
                 .getParcelable(TestProtocol.TEST_INFO_RESPONSE_FIELD);
     }
 
@@ -1027,11 +1027,11 @@
             return;
         }
 
-        linearGesture(
-                displaySize.x / 2, displaySize.y - 1,
-                displaySize.x / 2, 0,
-                ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME,
-                false, GestureScope.EXPECT_PILFER);
+        if (isLauncher3()) {
+            gestureToDismissPopup(displaySize);
+        } else {
+            runToState(() -> gestureToDismissPopup(displaySize), NORMAL_STATE_ORDINAL, "swiping");
+        }
 
         try (LauncherInstrumentation.Closable c1 = addContextLayer(
                 String.format("Swiped up from floating view %s to home", floatingRes.get()))) {
@@ -1040,6 +1040,14 @@
         }
     }
 
+    private void gestureToDismissPopup(Point displaySize) {
+        linearGesture(
+                displaySize.x / 2, displaySize.y - 1,
+                displaySize.x / 2, 0,
+                ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME,
+                false, GestureScope.EXPECT_PILFER);
+    }
+
     /**
      * @return the Workspace object.
      * @deprecated use goHome().
@@ -1741,32 +1749,38 @@
         final long downTime = SystemClock.uptimeMillis();
         final Point start = new Point(startX, startY);
         final Point end = new Point(endX, endY);
+        long endTime = downTime;
         sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, start, gestureScope);
-        if (mTrackpadGestureType != TrackpadGestureType.NONE) {
-            sendPointer(downTime, downTime, getPointerAction(MotionEvent.ACTION_POINTER_DOWN, 1),
-                    start, gestureScope);
-            if (mTrackpadGestureType == TrackpadGestureType.THREE_FINGER
-                    || mTrackpadGestureType == TrackpadGestureType.FOUR_FINGER) {
+        try {
+
+            if (mTrackpadGestureType != TrackpadGestureType.NONE) {
                 sendPointer(downTime, downTime,
-                        getPointerAction(MotionEvent.ACTION_POINTER_DOWN, 2),
+                        getPointerAction(MotionEvent.ACTION_POINTER_DOWN, 1),
                         start, gestureScope);
-                if (mTrackpadGestureType == TrackpadGestureType.FOUR_FINGER) {
+                if (mTrackpadGestureType == TrackpadGestureType.THREE_FINGER
+                        || mTrackpadGestureType == TrackpadGestureType.FOUR_FINGER) {
                     sendPointer(downTime, downTime,
-                            getPointerAction(MotionEvent.ACTION_POINTER_DOWN, 3),
+                            getPointerAction(MotionEvent.ACTION_POINTER_DOWN, 2),
+                            start, gestureScope);
+                    if (mTrackpadGestureType == TrackpadGestureType.FOUR_FINGER) {
+                        sendPointer(downTime, downTime,
+                                getPointerAction(MotionEvent.ACTION_POINTER_DOWN, 3),
+                                start, gestureScope);
+                    }
+                }
+            }
+            endTime = movePointer(
+                    start, end, steps, false, downTime, downTime, slowDown, gestureScope);
+            if (mTrackpadGestureType != TrackpadGestureType.NONE) {
+                for (int i = mPointerCount; i >= 2; i--) {
+                    sendPointer(downTime, downTime,
+                            getPointerAction(MotionEvent.ACTION_POINTER_UP, i - 1),
                             start, gestureScope);
                 }
             }
+        } finally {
+            sendPointer(downTime, endTime, MotionEvent.ACTION_UP, end, gestureScope);
         }
-        final long endTime = movePointer(
-                start, end, steps, false, downTime, downTime, slowDown, gestureScope);
-        if (mTrackpadGestureType != TrackpadGestureType.NONE) {
-            for (int i = mPointerCount; i >= 2; i--) {
-                sendPointer(downTime, downTime,
-                        getPointerAction(MotionEvent.ACTION_POINTER_UP, i - 1),
-                        start, gestureScope);
-            }
-        }
-        sendPointer(downTime, endTime, MotionEvent.ACTION_UP, end, gestureScope);
     }
 
     private static int getPointerAction(int action, int index) {
@@ -2358,12 +2372,13 @@
                         : containerBounds.left - 1;
             }
             // If IME is visible and overlaps the container bounds, touch above it.
+            final Insets systemGestureRegion = getSystemGestureRegion();
             int bottomBound = Math.min(
                     containerBounds.bottom,
-                    getRealDisplaySize().y - getImeInsets().bottom);
+                    getRealDisplaySize().y - systemGestureRegion.bottom);
             int y = (bottomBound - containerBounds.top) / 2;
             // Do not tap in the status bar.
-            y = Math.max(y, getWindowInsets().top);
+            y = Math.max(y, systemGestureRegion.top);
 
             final long downTime = SystemClock.uptimeMillis();
             final Point tapTarget = new Point(x, y);
diff --git a/tests/tapl/com/android/launcher3/tapl/Qsb.java b/tests/tapl/com/android/launcher3/tapl/Qsb.java
index fe2a63d..d67b8a3 100644
--- a/tests/tapl/com/android/launcher3/tapl/Qsb.java
+++ b/tests/tapl/com/android/launcher3/tapl/Qsb.java
@@ -118,9 +118,7 @@
         try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
                 "want to open search result page");
              LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
-            mLauncher.clickLauncherObject(waitForQsbObject());
-            // wait for the result rendering to complete
-            mLauncher.waitForIdle();
+            clickQsb();
             try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer(
                     "clicked qsb to open search result page")) {
                 return createSearchResult();
@@ -128,6 +126,10 @@
         }
     }
 
+    protected void clickQsb() {
+        mLauncher.clickLauncherObject(waitForQsbObject());
+    }
+
     @Override
     public LauncherInstrumentation getLauncher() {
         return mLauncher;
diff --git a/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java b/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java
index 963bf79..8d3a631 100644
--- a/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java
+++ b/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java
@@ -15,6 +15,8 @@
  */
 package com.android.launcher3.tapl;
 
+import static com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL;
+
 import android.widget.TextView;
 
 import androidx.test.uiautomator.By;
@@ -87,7 +89,7 @@
                              + (tapRight ? "right" : "left"))) {
             final UiObject2 allAppsBottomSheet =
                     mLauncher.waitForLauncherObject(BOTTOM_SHEET_RES_ID);
-            mLauncher.touchOutsideContainer(allAppsBottomSheet, tapRight);
+            tapOutside(tapRight, allAppsBottomSheet);
             try (LauncherInstrumentation.Closable tapped = mLauncher.addContextLayer(
                     "tapped outside AllApps bottom sheet")) {
                 verifyVisibleContainerOnDismiss();
@@ -95,6 +97,13 @@
         }
     }
 
+    protected void tapOutside(boolean tapRight, UiObject2 allAppsBottomSheet) {
+        mLauncher.runToState(
+                () -> mLauncher.touchOutsideContainer(allAppsBottomSheet, tapRight),
+                NORMAL_STATE_ORDINAL,
+                "tappig outside");
+    }
+
     protected void verifyVisibleContainerOnDismiss() {
         mLauncher.getWorkspace();
     }
diff --git a/tests/tapl/com/android/launcher3/tapl/SearchResultFromTaskbarQsb.java b/tests/tapl/com/android/launcher3/tapl/SearchResultFromTaskbarQsb.java
index 00291a3..f4b4a91 100644
--- a/tests/tapl/com/android/launcher3/tapl/SearchResultFromTaskbarQsb.java
+++ b/tests/tapl/com/android/launcher3/tapl/SearchResultFromTaskbarQsb.java
@@ -50,4 +50,9 @@
     protected void verifyVisibleContainerOnDismiss() {
         mLauncher.getLaunchedAppState().assertTaskbarVisible();
     }
+
+    @Override
+    protected void tapOutside(boolean tapRight, UiObject2 allAppsBottomSheet) {
+        mLauncher.touchOutsideContainer(allAppsBottomSheet, tapRight);
+    }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/SelectModeButtons.java b/tests/tapl/com/android/launcher3/tapl/SelectModeButtons.java
index 146a67c..e2bc17b 100644
--- a/tests/tapl/com/android/launcher3/tapl/SelectModeButtons.java
+++ b/tests/tapl/com/android/launcher3/tapl/SelectModeButtons.java
@@ -18,6 +18,8 @@
 
 import static android.view.KeyEvent.KEYCODE_ESCAPE;
 
+import static com.android.launcher3.testing.shared.TestProtocol.OVERVIEW_STATE_ORDINAL;
+
 import androidx.annotation.NonNull;
 import androidx.test.uiautomator.UiObject2;
 
@@ -69,7 +71,10 @@
         try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
             mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ALT_ESC_DOWN);
             mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ALT_ESC_UP);
-            mLauncher.getDevice().pressKeyCode(KEYCODE_ESCAPE);
+            mLauncher.runToState(
+                    () -> mLauncher.getDevice().pressKeyCode(KEYCODE_ESCAPE),
+                    OVERVIEW_STATE_ORDINAL,
+                    "pressing Esc");
             try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
                     "pressed esc key")) {
                 return new Overview(mLauncher);
diff --git a/tests/tapl/com/android/launcher3/tapl/Taskbar.java b/tests/tapl/com/android/launcher3/tapl/Taskbar.java
index a202c53..e6315f3 100644
--- a/tests/tapl/com/android/launcher3/tapl/Taskbar.java
+++ b/tests/tapl/com/android/launcher3/tapl/Taskbar.java
@@ -153,7 +153,7 @@
         return By.clazz(TextView.class).text("");
     }
 
-    private Rect getVisibleBounds() {
+    public Rect getVisibleBounds() {
         return mLauncher.waitForSystemLauncherObject(TASKBAR_RES_ID).getVisibleBounds();
     }