Merge "Log a-z apps count." into tm-qpr-dev
diff --git a/Android.bp b/Android.bp
index 330c32e..6267e9f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -19,6 +19,54 @@
 
 min_launcher3_sdk_version = "26"
 
+// Common source files used to build launcher (java and kotlin)
+// All sources are split so they can be reused in many other libraries/apps in other folders
+filegroup {
+    name: "launcher-src",
+    srcs: [ "src/**/*.java", "src/**/*.kt" ],
+}
+
+filegroup {
+    name: "launcher-quickstep-src",
+    srcs: [ "quickstep/src/**/*.java", "quickstep/src/**/*.kt" ],
+}
+
+filegroup {
+    name: "launcher-go-src",
+    srcs: [ "go/src/**/*.java", "go/src/**/*.kt" ],
+}
+
+filegroup {
+    name: "launcher-go-quickstep-src",
+    srcs: [ "go/quickstep/src/**/*.java", "go/quickstep/src/**/*.kt" ],
+}
+
+filegroup {
+    name: "launcher-src_shortcuts_overrides",
+    srcs: [ "src_shortcuts_overrides/**/*.java", "src_shortcuts_overrides/**/*.kt" ],
+}
+
+filegroup {
+    name: "launcher-src_ui_overrides",
+    srcs: [ "src_ui_overrides/**/*.java", "src_ui_overrides/**/*.kt" ],
+}
+
+filegroup {
+    name: "launcher-ext_tests",
+    srcs: [ "ext_tests/**/*.java", "ext_tests/**/*.kt" ],
+}
+
+filegroup {
+    name: "launcher-quickstep-ext_tests",
+    srcs: [ "quickstep/ext_tests/**/*.java", "quickstep/ext_tests/**/*.kt" ],
+}
+
+// Proguard files for Launcher3
+filegroup {
+    name: "launcher-proguard-rules",
+    srcs: ["proguard.flags"],
+}
+
 android_library {
     name: "launcher-aosp-tapl",
     libs: [
@@ -139,14 +187,10 @@
         "Launcher3CommonDepsLib",
     ],
     srcs: [
-        "src/**/*.java",
-        "src/**/*.kt",
-        "src_shortcuts_overrides/**/*.java",
-        "src_shortcuts_overrides/**/*.kt",
-        "src_ui_overrides/**/*.java",
-        "src_ui_overrides/**/*.kt",
-        "ext_tests/src/**/*.java",
-        "ext_tests/src/**/*.kt",
+        ":launcher-src",
+        ":launcher-src_shortcuts_overrides",
+        ":launcher-src_ui_overrides",
+        ":launcher-ext_tests",
     ],
     resource_dirs: [
         "ext_tests/res",
@@ -202,61 +246,14 @@
 }
 
 
-// Source code used for test helpers
-filegroup {
-    name: "launcher-src-ext-tests",
-    srcs: [
-        "ext_tests/src/**/*.java",
-        "ext_tests/src/**/*.kt",
-        "quickstep/ext_tests/src/**/*.java",
-        "quickstep/ext_tests/src/**/*.kt",
-    ],
-}
-
-// Common source files used to build launcher
-filegroup {
-    name: "launcher-src-no-build-config",
-    srcs: [
-        "src/**/*.java",
-        "src/**/*.kt",
-        "src_shortcuts_overrides/**/*.java",
-        "src_shortcuts_overrides/**/*.kt",
-        "quickstep/src/**/*.java",
-        "quickstep/src/**/*.kt",
-    ],
-}
-
-// Common source files used to build go launcher except go/src files
-filegroup {
-    name: "launcher-go-src-no-build-config",
-    srcs: [
-        "src/**/*.java",
-        "src/**/*.kt",
-        "quickstep/src/**/*.java",
-        "quickstep/src/**/*.kt",
-        "go/quickstep/src/**/*.java",
-        "go/quickstep/src/**/*.kt",
-    ],
-}
-
-// Proguard files for Launcher3
-filegroup {
-    name: "launcher-proguard-rules",
-    srcs: ["proguard.flags"],
-}
-
 // Library with all the dependencies for building Launcher Go
 android_library {
     name: "LauncherGoResLib",
     srcs: [
-        "src/**/*.java",
-        "src/**/*.kt",
-        "quickstep/src/**/*.java",
-        "quickstep/src/**/*.kt",
-        "go/src/**/*.java",
-        "go/src/**/*.kt",
-        "go/quickstep/src/**/*.java",
-        "go/quickstep/src/**/*.kt",
+        ":launcher-src",
+        ":launcher-quickstep-src",
+        ":launcher-go-src",
+        ":launcher-go-quickstep-src",
     ],
     resource_dirs: [
         "go/res",
@@ -287,7 +284,9 @@
 android_library {
     name: "Launcher3QuickStepLib",
     srcs: [
-        ":launcher-src-no-build-config",
+        ":launcher-src",
+        ":launcher-quickstep-src",
+        ":launcher-src_shortcuts_overrides",
     ],
     resource_dirs: [],
     libs: [
@@ -319,9 +318,9 @@
     static_libs: ["Launcher3CommonDepsLib"],
 
     srcs: [
-        "src/**/*.java",
-        "src_ui_overrides/**/*.java",
-        "go/src/**/*.java",
+        ":launcher-src",
+        ":launcher-go-src",
+        ":launcher-src_ui_overrides",
     ],
 
     resource_dirs: ["go/res"],
@@ -405,12 +404,7 @@
     min_sdk_version: "current",
     target_sdk_version: "current",
 
-    srcs: [
-        "src/**/*.java",
-        "quickstep/src/**/*.java",
-        "go/src/**/*.java",
-        "go/quickstep/src/**/*.java",
-    ],
+    srcs: [ ],
 
     resource_dirs: [
         "go/quickstep/res",
diff --git a/protos/launcher_atom.proto b/protos/launcher_atom.proto
index 10eedc8..151ec5a 100644
--- a/protos/launcher_atom.proto
+++ b/protos/launcher_atom.proto
@@ -45,6 +45,9 @@
 
   // Stores the origin of the Item
   repeated Attribute item_attributes = 12;
+
+  // Stores whether the navigation bar is in kids mode.
+  optional bool is_kids_mode = 13;
 }
 
 message LauncherAttributes{
diff --git a/quickstep/Android.bp b/quickstep/Android.bp
index f739f81..7292c44 100644
--- a/quickstep/Android.bp
+++ b/quickstep/Android.bp
@@ -18,6 +18,11 @@
 }
 
 filegroup {
+    name: "launcher3-quickstep-manifest",
+    srcs: ["AndroidManifest.xml"],
+}
+
+filegroup {
     name: "launcher3-quickstep-robolectric-src",
     path: "robolectric_tests",
     srcs: ["robolectric_tests/src/**/*.java"],
diff --git a/quickstep/res/drawable/ic_floating_task_button.xml b/quickstep/res/drawable/ic_floating_task_button.xml
index e50f65c..63b2fd8 100644
--- a/quickstep/res/drawable/ic_floating_task_button.xml
+++ b/quickstep/res/drawable/ic_floating_task_button.xml
@@ -19,11 +19,17 @@
     android:height="24dp"
     android:viewportWidth="24"
     android:viewportHeight="24">
-  <path
-      android:pathData="M17.6258,4.96L19.0358,6.37L7.4058,18.01L5.9958,16.6L17.6258,4.96ZM16.1358,3.62L4.1258,15.63L3.0158,19.83C2.9058,20.45 3.3858,21 3.9958,21C4.0558,21 4.1058,21 4.1658,20.99L8.3658,19.88L20.3758,7.86C20.7758,7.46 20.9958,6.93 20.9958,6.37C20.9958,5.81 20.7758,5.28 20.3758,4.88L19.1058,3.61C18.7158,3.22 18.1858,3 17.6258,3C17.0658,3 16.5358,3.22 16.1358,3.62Z"
-      android:fillColor="#636C6F"/>
-  <path
-      android:pathData="M20.1936,15.3369C20.3748,16.3837 19.9151,17.5414 18.8846,18.7597C19.1546,18.872 19.4576,18.9452 19.7724,18.9867C20.0839,19.0278 20.3683,19.0325 20.5749,19.0266C20.6772,19.0236 20.7578,19.0181 20.8101,19.0138C20.8362,19.0116 20.855,19.0097 20.8657,19.0085L20.8754,19.0074L20.875,19.0075C21.4217,18.9385 21.9214,19.325 21.9918,19.8718C22.0624,20.4195 21.6756,20.9208 21.1279,20.9914L21,19.9996C21.1279,20.9914 21.1265,20.9916 21.1265,20.9916L21.1249,20.9918L21.1211,20.9923L21.1107,20.9935L21.0795,20.997C21.0542,20.9998 21.0199,21.0032 20.9775,21.0067C20.8929,21.0138 20.7753,21.0216 20.6323,21.0257C20.3481,21.0339 19.9533,21.0279 19.5109,20.9695C18.873,20.8854 18.0393,20.6793 17.3106,20.1662C16.9605,20.3559 16.5876,20.4952 16.2299,20.6003C15.5742,20.7927 14.8754,20.8968 14.2534,20.9534C13.6801,21.0055 13.4553,21.0037 13.1015,21.0008C13.0689,21.0005 13.0352,21.0002 13,21H12.8594C12.8214,21.0002 12.785,21.0006 12.7504,21.0009C12.6524,21.0019 12.5683,21.0027 12.5,21H12.0562C12.0277,21.0003 12.0054,21.0006 11.9926,21.001L11.9751,21H9L11,19H11.9795C11.9929,18.9997 12.0064,18.9997 12.0199,19H12.4117C12.4534,18.9996 12.4864,18.9995 12.5,19H12.9675C12.977,18.9999 12.9878,18.9999 13,19C13.0446,19.0003 13.0859,19.0007 13.1249,19.0011C13.4259,19.0038 13.591,19.0054 14.0723,18.9616C14.6201,18.9118 15.1795,18.8242 15.6665,18.6813C15.753,18.6559 15.8346,18.6295 15.9114,18.6022C15.0315,17.2981 14.7125,16.1044 15.015,15.0829C15.4095,13.7511 16.6784,13.2418 17.7026,13.2864C18.7262,13.3309 19.954,13.9529 20.1936,15.3369ZM16.9327,15.6508C16.873,15.8523 16.8651,16.3878 17.4697,17.334C18.2007,16.4284 18.2585,15.8839 18.2229,15.6781C18.1939,15.5108 18.0297,15.3025 17.6157,15.2845C17.2025,15.2665 16.9885,15.4626 16.9327,15.6508Z"
-      android:fillColor="#636C6F"
-      android:fillType="evenOdd"/>
+  <group
+      android:pivotY="12"
+      android:pivotX="12"
+      android:scaleX=".75"
+      android:scaleY=".75">
+    <path
+        android:pathData="M17.6258,4.96L19.0358,6.37L7.4058,18.01L5.9958,16.6L17.6258,4.96ZM16.1358,3.62L4.1258,15.63L3.0158,19.83C2.9058,20.45 3.3858,21 3.9958,21C4.0558,21 4.1058,21 4.1658,20.99L8.3658,19.88L20.3758,7.86C20.7758,7.46 20.9958,6.93 20.9958,6.37C20.9958,5.81 20.7758,5.28 20.3758,4.88L19.1058,3.61C18.7158,3.22 18.1858,3 17.6258,3C17.0658,3 16.5358,3.22 16.1358,3.62Z"
+        android:fillColor="#636C6F"/>
+    <path
+        android:pathData="M20.1936,15.3369C20.3748,16.3837 19.9151,17.5414 18.8846,18.7597C19.1546,18.872 19.4576,18.9452 19.7724,18.9867C20.0839,19.0278 20.3683,19.0325 20.5749,19.0266C20.6772,19.0236 20.7578,19.0181 20.8101,19.0138C20.8362,19.0116 20.855,19.0097 20.8657,19.0085L20.8754,19.0074L20.875,19.0075C21.4217,18.9385 21.9214,19.325 21.9918,19.8718C22.0624,20.4195 21.6756,20.9208 21.1279,20.9914L21,19.9996C21.1279,20.9914 21.1265,20.9916 21.1265,20.9916L21.1249,20.9918L21.1211,20.9923L21.1107,20.9935L21.0795,20.997C21.0542,20.9998 21.0199,21.0032 20.9775,21.0067C20.8929,21.0138 20.7753,21.0216 20.6323,21.0257C20.3481,21.0339 19.9533,21.0279 19.5109,20.9695C18.873,20.8854 18.0393,20.6793 17.3106,20.1662C16.9605,20.3559 16.5876,20.4952 16.2299,20.6003C15.5742,20.7927 14.8754,20.8968 14.2534,20.9534C13.6801,21.0055 13.4553,21.0037 13.1015,21.0008C13.0689,21.0005 13.0352,21.0002 13,21H12.8594C12.8214,21.0002 12.785,21.0006 12.7504,21.0009C12.6524,21.0019 12.5683,21.0027 12.5,21H12.0562C12.0277,21.0003 12.0054,21.0006 11.9926,21.001L11.9751,21H9L11,19H11.9795C11.9929,18.9997 12.0064,18.9997 12.0199,19H12.4117C12.4534,18.9996 12.4864,18.9995 12.5,19H12.9675C12.977,18.9999 12.9878,18.9999 13,19C13.0446,19.0003 13.0859,19.0007 13.1249,19.0011C13.4259,19.0038 13.591,19.0054 14.0723,18.9616C14.6201,18.9118 15.1795,18.8242 15.6665,18.6813C15.753,18.6559 15.8346,18.6295 15.9114,18.6022C15.0315,17.2981 14.7125,16.1044 15.015,15.0829C15.4095,13.7511 16.6784,13.2418 17.7026,13.2864C18.7262,13.3309 19.954,13.9529 20.1936,15.3369ZM16.9327,15.6508C16.873,15.8523 16.8651,16.3878 17.4697,17.334C18.2007,16.4284 18.2585,15.8839 18.2229,15.6781C18.1939,15.5108 18.0297,15.3025 17.6157,15.2845C17.2025,15.2665 16.9885,15.4626 16.9327,15.6508Z"
+        android:fillColor="#636C6F"
+        android:fillType="evenOdd"/>
+  </group>
 </vector>
diff --git a/quickstep/res/layout/taskbar_all_apps_button.xml b/quickstep/res/layout/taskbar_all_apps_button.xml
new file mode 100644
index 0000000..b275305
--- /dev/null
+++ b/quickstep/res/layout/taskbar_all_apps_button.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<com.android.launcher3.views.IconButtonView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="@dimen/taskbar_icon_touch_size"
+    android:layout_height="@dimen/taskbar_icon_touch_size"
+    android:contentDescription="@string/all_apps_button_label"
+    android:backgroundTint="@color/all_apps_button_bg_color"
+    android:icon="@drawable/ic_all_apps_button"
+    />
diff --git a/quickstep/res/layout/taskbar_floating_task_button.xml b/quickstep/res/layout/taskbar_floating_task_button.xml
new file mode 100644
index 0000000..b5beded
--- /dev/null
+++ b/quickstep/res/layout/taskbar_floating_task_button.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<com.android.launcher3.views.IconButtonView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="@dimen/taskbar_icon_touch_size"
+    android:layout_height="@dimen/taskbar_icon_touch_size"
+    android:icon="@drawable/ic_floating_task_button"
+    />
diff --git a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
index 62603e9..880aa6f 100644
--- a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
+++ b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
@@ -28,13 +28,13 @@
 import android.content.Context;
 import android.os.Build;
 import android.os.Handler;
+import android.view.RemoteAnimationTarget;
 
 import androidx.annotation.BinderThread;
 import androidx.annotation.Nullable;
 import androidx.annotation.UiThread;
 
 import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
 import java.lang.ref.WeakReference;
 
@@ -82,9 +82,9 @@
     @BinderThread
     public void onAnimationStart(
             int transit,
-            RemoteAnimationTargetCompat[] appTargets,
-            RemoteAnimationTargetCompat[] wallpaperTargets,
-            RemoteAnimationTargetCompat[] nonAppTargets,
+            RemoteAnimationTarget[] appTargets,
+            RemoteAnimationTarget[] wallpaperTargets,
+            RemoteAnimationTarget[] nonAppTargets,
             Runnable runnable) {
         Runnable r = () -> {
             finishExistingAnimation();
@@ -101,17 +101,17 @@
 
     // Called only in R platform
     @BinderThread
-    public void onAnimationStart(RemoteAnimationTargetCompat[] appTargets,
-            RemoteAnimationTargetCompat[] wallpaperTargets, Runnable runnable) {
+    public void onAnimationStart(RemoteAnimationTarget[] appTargets,
+            RemoteAnimationTarget[] wallpaperTargets, Runnable runnable) {
         onAnimationStart(0 /* transit */, appTargets, wallpaperTargets,
-                new RemoteAnimationTargetCompat[0], runnable);
+                new RemoteAnimationTarget[0], runnable);
     }
 
     // Called only in Q platform
     @BinderThread
     @Deprecated
-    public void onAnimationStart(RemoteAnimationTargetCompat[] appTargets, Runnable runnable) {
-        onAnimationStart(appTargets, new RemoteAnimationTargetCompat[0], runnable);
+    public void onAnimationStart(RemoteAnimationTarget[] appTargets, Runnable runnable) {
+        onAnimationStart(appTargets, new RemoteAnimationTarget[0], runnable);
     }
 
 
@@ -229,9 +229,9 @@
          * call {@link AnimationResult#setAnimation} with the target animation to be run.
          */
         void onCreateAnimation(int transit,
-                RemoteAnimationTargetCompat[] appTargets,
-                RemoteAnimationTargetCompat[] wallpaperTargets,
-                RemoteAnimationTargetCompat[] nonAppTargets,
+                RemoteAnimationTarget[] appTargets,
+                RemoteAnimationTarget[] wallpaperTargets,
+                RemoteAnimationTarget[] nonAppTargets,
                 LauncherAnimationRunner.AnimationResult result);
 
         /**
diff --git a/quickstep/src/com/android/launcher3/LauncherInitListener.java b/quickstep/src/com/android/launcher3/LauncherInitListener.java
index c4e85f6..28bd701 100644
--- a/quickstep/src/com/android/launcher3/LauncherInitListener.java
+++ b/quickstep/src/com/android/launcher3/LauncherInitListener.java
@@ -19,11 +19,11 @@
 import android.annotation.TargetApi;
 import android.os.Build;
 import android.os.CancellationSignal;
+import android.view.RemoteAnimationTarget;
 
 import com.android.launcher3.uioverrides.QuickstepLauncher;
 import com.android.quickstep.util.ActivityInitListener;
 import com.android.quickstep.util.RemoteAnimationProvider;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
 import java.util.function.BiPredicate;
 
@@ -52,8 +52,8 @@
             CancellationSignal cancellationSignal = new CancellationSignal();
             appTransitionManager.setRemoteAnimationProvider(new RemoteAnimationProvider() {
                 @Override
-                public AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] appTargets,
-                        RemoteAnimationTargetCompat[] wallpaperTargets) {
+                public AnimatorSet createWindowAnimation(RemoteAnimationTarget[] appTargets,
+                        RemoteAnimationTarget[] wallpaperTargets) {
 
                     // On the first call clear the reference.
                     cancellationSignal.cancel();
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 9a1ed4d..58b3a83 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -17,6 +17,8 @@
 package com.android.launcher3;
 
 import static android.provider.Settings.Secure.LAUNCHER_TASKBAR_EDUCATION_SHOWING;
+import static android.view.RemoteAnimationTarget.MODE_CLOSING;
+import static android.view.RemoteAnimationTarget.MODE_OPENING;
 import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_NONE;
 import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN;
 
@@ -49,8 +51,6 @@
 import static com.android.quickstep.TaskViewUtils.findTaskViewToLaunch;
 import static com.android.systemui.shared.system.QuickStepContract.getWindowCornerRadius;
 import static com.android.systemui.shared.system.QuickStepContract.supportsRoundedCornersOnWindows;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -81,6 +81,7 @@
 import android.util.Pair;
 import android.util.Size;
 import android.view.CrossWindowBlurListeners;
+import android.view.RemoteAnimationTarget;
 import android.view.SurfaceControl;
 import android.view.View;
 import android.view.ViewRootImpl;
@@ -98,6 +99,7 @@
 import com.android.launcher3.LauncherAnimationRunner.RemoteAnimationFactory;
 import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.anim.AnimatorListeners;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.icons.FastBitmapDrawable;
 import com.android.launcher3.shortcuts.DeepShortcutView;
@@ -122,6 +124,8 @@
 import com.android.quickstep.util.RectFSpringAnim;
 import com.android.quickstep.util.RemoteAnimationProvider;
 import com.android.quickstep.util.StaggeredWorkspaceAnim;
+import com.android.quickstep.util.SurfaceTransaction;
+import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties;
 import com.android.quickstep.util.SurfaceTransactionApplier;
 import com.android.quickstep.util.WorkspaceRevealAnim;
 import com.android.quickstep.views.FloatingWidgetView;
@@ -132,9 +136,7 @@
 import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
 import com.android.systemui.shared.system.RemoteAnimationDefinitionCompat;
 import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 import com.android.systemui.shared.system.RemoteTransitionCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
 import com.android.wm.shell.startingsurface.IStartingWindowListener;
 
 import java.util.ArrayList;
@@ -310,7 +312,7 @@
      * @return true if the app is launching from recents, false if it most likely is not
      */
     protected boolean isLaunchingFromRecents(@NonNull View v,
-            @Nullable RemoteAnimationTargetCompat[] targets) {
+            @Nullable RemoteAnimationTarget[] targets) {
         return mLauncher.getStateManager().getState().overviewUi
                 && findTaskViewToLaunch(mLauncher.getOverviewPanel(), v, targets) != null;
     }
@@ -324,18 +326,18 @@
      * @param launcherClosing true if the launcher app is closing
      */
     protected void composeRecentsLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
-            @NonNull RemoteAnimationTargetCompat[] appTargets,
-            @NonNull RemoteAnimationTargetCompat[] wallpaperTargets,
-            @NonNull RemoteAnimationTargetCompat[] nonAppTargets, boolean launcherClosing) {
+            @NonNull RemoteAnimationTarget[] appTargets,
+            @NonNull RemoteAnimationTarget[] wallpaperTargets,
+            @NonNull RemoteAnimationTarget[] nonAppTargets, boolean launcherClosing) {
         TaskViewUtils.composeRecentsLaunchAnimator(anim, v, appTargets, wallpaperTargets,
                 nonAppTargets, launcherClosing, mLauncher.getStateManager(),
                 mLauncher.getOverviewPanel(), mLauncher.getDepthController());
     }
 
-    private boolean areAllTargetsTranslucent(@NonNull RemoteAnimationTargetCompat[] targets) {
+    private boolean areAllTargetsTranslucent(@NonNull RemoteAnimationTarget[] targets) {
         boolean isAllOpeningTargetTrs = true;
         for (int i = 0; i < targets.length; i++) {
-            RemoteAnimationTargetCompat target = targets[i];
+            RemoteAnimationTarget target = targets[i];
             if (target.mode == MODE_OPENING) {
                 isAllOpeningTargetTrs &= target.isTranslucent;
             }
@@ -353,9 +355,9 @@
      * @param launcherClosing true if launcher is closing
      */
     private void composeIconLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
-            @NonNull RemoteAnimationTargetCompat[] appTargets,
-            @NonNull RemoteAnimationTargetCompat[] wallpaperTargets,
-            @NonNull RemoteAnimationTargetCompat[] nonAppTargets,
+            @NonNull RemoteAnimationTarget[] appTargets,
+            @NonNull RemoteAnimationTarget[] wallpaperTargets,
+            @NonNull RemoteAnimationTarget[] nonAppTargets,
             boolean launcherClosing) {
         // Set the state animation first so that any state listeners are called
         // before our internal listeners.
@@ -397,9 +399,9 @@
     private void composeWidgetLaunchAnimator(
             @NonNull AnimatorSet anim,
             @NonNull LauncherAppWidgetHostView v,
-            @NonNull RemoteAnimationTargetCompat[] appTargets,
-            @NonNull RemoteAnimationTargetCompat[] wallpaperTargets,
-            @NonNull RemoteAnimationTargetCompat[] nonAppTargets) {
+            @NonNull RemoteAnimationTarget[] appTargets,
+            @NonNull RemoteAnimationTarget[] wallpaperTargets,
+            @NonNull RemoteAnimationTarget[] nonAppTargets) {
         mLauncher.getStateManager().setCurrentAnimation(anim);
 
         Rect windowTargetBounds = getWindowTargetBounds(appTargets, getRotationChange(appTargets));
@@ -422,10 +424,10 @@
      * In multiwindow mode, we need to get the final size of the opening app window target to help
      * figure out where the floating view should animate to.
      */
-    private Rect getWindowTargetBounds(@NonNull RemoteAnimationTargetCompat[] appTargets,
+    private Rect getWindowTargetBounds(@NonNull RemoteAnimationTarget[] appTargets,
             int rotationChange) {
-        RemoteAnimationTargetCompat target = null;
-        for (RemoteAnimationTargetCompat t : appTargets) {
+        RemoteAnimationTarget target = null;
+        for (RemoteAnimationTarget t : appTargets) {
             if (t.mode != MODE_OPENING) continue;
             target = t;
             break;
@@ -551,7 +553,8 @@
 
             final boolean scrimEnabled = ENABLE_SCRIM_FOR_APP_LAUNCH.get();
             if (scrimEnabled) {
-                boolean useTaskbarColor = mDeviceProfile.isTaskbarPresentInApps;
+                boolean useTaskbarColor = mDeviceProfile.isTaskbarPresentInApps
+                        && !FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get();
                 int scrimColor = useTaskbarColor
                         ? mLauncher.getResources().getColor(R.color.taskbar_background)
                         : Themes.getAttrColor(mLauncher, R.attr.overviewScrimColor);
@@ -646,9 +649,9 @@
      * @return Animator that controls the window of the opening targets from app icons.
      */
     private Animator getOpeningWindowAnimators(View v,
-            RemoteAnimationTargetCompat[] appTargets,
-            RemoteAnimationTargetCompat[] wallpaperTargets,
-            RemoteAnimationTargetCompat[] nonAppTargets,
+            RemoteAnimationTarget[] appTargets,
+            RemoteAnimationTarget[] wallpaperTargets,
+            RemoteAnimationTarget[] nonAppTargets,
             Rect windowTargetBounds, boolean appTargetsAreTranslucent, int rotationChange) {
         RectF launcherIconBounds = new RectF();
         FloatingIconView floatingView = FloatingIconView.getFloatingIconView(mLauncher, v,
@@ -661,7 +664,7 @@
         SurfaceTransactionApplier surfaceApplier =
                 new SurfaceTransactionApplier(floatingView);
         openingTargets.addReleaseCheck(surfaceApplier);
-        RemoteAnimationTargetCompat navBarTarget = openingTargets.getNavBarRemoteAnimationTarget();
+        RemoteAnimationTarget navBarTarget = openingTargets.getNavBarRemoteAnimationTarget();
 
         int[] dragLayerBounds = new int[2];
         mDragLayer.getLocationOnScreen(dragLayerBounds);
@@ -813,10 +816,11 @@
                     return;
                 }
 
-                ArrayList<SurfaceParams> params = new ArrayList<>();
+                SurfaceTransaction transaction = new SurfaceTransaction();
+
                 for (int i = appTargets.length - 1; i >= 0; i--) {
-                    RemoteAnimationTargetCompat target = appTargets[i];
-                    SurfaceParams.Builder builder = new SurfaceParams.Builder(target.leash);
+                    RemoteAnimationTarget target = appTargets[i];
+                    SurfaceProperties builder = transaction.forSurface(target.leash);
 
                     if (target.mode == MODE_OPENING) {
                         matrix.setScale(scale, scale);
@@ -837,11 +841,11 @@
 
                         floatingView.update(mIconAlpha.value, 255, floatingIconBounds, percent, 0f,
                                 mWindowRadius.value * scale, true /* isOpening */);
-                        builder.withMatrix(matrix)
-                                .withWindowCrop(crop)
-                                .withAlpha(1f - mIconAlpha.value)
-                                .withCornerRadius(mWindowRadius.value)
-                                .withShadowRadius(mShadowRadius.value);
+                        builder.setMatrix(matrix)
+                                .setWindowCrop(crop)
+                                .setAlpha(1f - mIconAlpha.value)
+                                .setCornerRadius(mWindowRadius.value)
+                                .setShadowRadius(mShadowRadius.value);
                     } else if (target.mode == MODE_CLOSING) {
                         if (target.localBounds != null) {
                             final Rect localBounds = target.localBounds;
@@ -861,29 +865,26 @@
                             tmpPos.y = tmp;
                         }
                         matrix.setTranslate(tmpPos.x, tmpPos.y);
-                        builder.withMatrix(matrix)
-                                .withWindowCrop(crop)
-                                .withAlpha(1f);
+                        builder.setMatrix(matrix)
+                                .setWindowCrop(crop)
+                                .setAlpha(1f);
                     }
-                    params.add(builder.build());
                 }
 
                 if (navBarTarget != null) {
-                    final SurfaceParams.Builder navBuilder =
-                            new SurfaceParams.Builder(navBarTarget.leash);
+                    SurfaceProperties navBuilder =
+                            transaction.forSurface(navBarTarget.leash);
                     if (mNavFadeIn.value > mNavFadeIn.getStartValue()) {
                         matrix.setScale(scale, scale);
                         matrix.postTranslate(windowTransX0, windowTransY0);
-                        navBuilder.withMatrix(matrix)
-                                .withWindowCrop(crop)
-                                .withAlpha(mNavFadeIn.value);
+                        navBuilder.setMatrix(matrix)
+                                .setWindowCrop(crop)
+                                .setAlpha(mNavFadeIn.value);
                     } else {
-                        navBuilder.withAlpha(mNavFadeOut.value);
+                        navBuilder.setAlpha(mNavFadeOut.value);
                     }
-                    params.add(navBuilder.build());
                 }
-
-                surfaceApplier.scheduleApply(params.toArray(new SurfaceParams[params.size()]));
+                surfaceApplier.scheduleApply(transaction);
             }
         };
         appAnimator.addUpdateListener(listener);
@@ -901,9 +902,9 @@
     }
 
     private Animator getOpeningWindowAnimatorsForWidget(LauncherAppWidgetHostView v,
-            RemoteAnimationTargetCompat[] appTargets,
-            RemoteAnimationTargetCompat[] wallpaperTargets,
-            RemoteAnimationTargetCompat[] nonAppTargets, Rect windowTargetBounds,
+            RemoteAnimationTarget[] appTargets,
+            RemoteAnimationTarget[] wallpaperTargets,
+            RemoteAnimationTarget[] nonAppTargets, Rect windowTargetBounds,
             boolean appTargetsAreTranslucent) {
         final RectF widgetBackgroundBounds = new RectF();
         final Rect appWindowCrop = new Rect();
@@ -911,7 +912,7 @@
         RemoteAnimationTargets openingTargets = new RemoteAnimationTargets(appTargets,
                 wallpaperTargets, nonAppTargets, MODE_OPENING);
 
-        RemoteAnimationTargetCompat openingTarget = openingTargets.getFirstAppTarget();
+        RemoteAnimationTarget openingTarget = openingTargets.getFirstAppTarget();
         int fallbackBackgroundColor = 0;
         if (openingTarget != null && supportsSSplashScreen()) {
             fallbackBackgroundColor = mTaskStartParams.containsKey(openingTarget.taskId)
@@ -935,7 +936,7 @@
         SurfaceTransactionApplier surfaceApplier = new SurfaceTransactionApplier(floatingView);
         openingTargets.addReleaseCheck(surfaceApplier);
 
-        RemoteAnimationTargetCompat navBarTarget = openingTargets.getNavBarRemoteAnimationTarget();
+        RemoteAnimationTarget navBarTarget = openingTargets.getNavBarRemoteAnimationTarget();
 
         AnimatorSet animatorSet = new AnimatorSet();
         ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
@@ -999,37 +1000,33 @@
                 matrix.postScale(mAppWindowScale, mAppWindowScale, widgetBackgroundBounds.left,
                         widgetBackgroundBounds.top);
 
-                ArrayList<SurfaceParams> params = new ArrayList<>();
+                SurfaceTransaction transaction = new SurfaceTransaction();
                 float floatingViewAlpha = appTargetsAreTranslucent ? 1 - mPreviewAlpha.value : 1;
                 for (int i = appTargets.length - 1; i >= 0; i--) {
-                    RemoteAnimationTargetCompat target = appTargets[i];
-                    SurfaceParams.Builder builder = new SurfaceParams.Builder(target.leash);
+                    RemoteAnimationTarget target = appTargets[i];
+                    SurfaceProperties builder = transaction.forSurface(target.leash);
                     if (target.mode == MODE_OPENING) {
                         floatingView.update(widgetBackgroundBounds, floatingViewAlpha,
                                 mWidgetForegroundAlpha.value, mWidgetFallbackBackgroundAlpha.value,
                                 mCornerRadiusProgress.value);
-                        builder.withMatrix(matrix)
-                                .withWindowCrop(appWindowCrop)
-                                .withAlpha(mPreviewAlpha.value)
-                                .withCornerRadius(mWindowRadius.value / mAppWindowScale);
+                        builder.setMatrix(matrix)
+                                .setWindowCrop(appWindowCrop)
+                                .setAlpha(mPreviewAlpha.value)
+                                .setCornerRadius(mWindowRadius.value / mAppWindowScale);
                     }
-                    params.add(builder.build());
                 }
 
                 if (navBarTarget != null) {
-                    final SurfaceParams.Builder navBuilder =
-                            new SurfaceParams.Builder(navBarTarget.leash);
+                    SurfaceProperties navBuilder = transaction.forSurface(navBarTarget.leash);
                     if (mNavFadeIn.value > mNavFadeIn.getStartValue()) {
-                        navBuilder.withMatrix(matrix)
-                                .withWindowCrop(appWindowCrop)
-                                .withAlpha(mNavFadeIn.value);
+                        navBuilder.setMatrix(matrix)
+                                .setWindowCrop(appWindowCrop)
+                                .setAlpha(mNavFadeIn.value);
                     } else {
-                        navBuilder.withAlpha(mNavFadeOut.value);
+                        navBuilder.setAlpha(mNavFadeOut.value);
                     }
-                    params.add(navBuilder.build());
                 }
-
-                surfaceApplier.scheduleApply(params.toArray(new SurfaceParams[params.size()]));
+                surfaceApplier.scheduleApply(transaction);
             }
         });
 
@@ -1177,8 +1174,8 @@
         }
     }
 
-    private boolean launcherIsATargetWithMode(RemoteAnimationTargetCompat[] targets, int mode) {
-        for (RemoteAnimationTargetCompat target : targets) {
+    private boolean launcherIsATargetWithMode(RemoteAnimationTarget[] targets, int mode) {
+        for (RemoteAnimationTarget target : targets) {
             if (target.mode == mode && target.taskInfo != null
                     // Compare component name instead of task-id because transitions will promote
                     // the target up to the root task while getTaskId returns the leaf.
@@ -1190,9 +1187,9 @@
         return false;
     }
 
-    private boolean hasMultipleTargetsWithMode(RemoteAnimationTargetCompat[] targets, int mode) {
+    private boolean hasMultipleTargetsWithMode(RemoteAnimationTarget[] targets, int mode) {
         int numTargets = 0;
-        for (RemoteAnimationTargetCompat target : targets) {
+        for (RemoteAnimationTarget target : targets) {
             if (target.mode == mode) {
                 numTargets++;
             }
@@ -1214,8 +1211,8 @@
     /**
      * Animator that controls the transformations of the windows when unlocking the device.
      */
-    private Animator getUnlockWindowAnimator(RemoteAnimationTargetCompat[] appTargets,
-            RemoteAnimationTargetCompat[] wallpaperTargets) {
+    private Animator getUnlockWindowAnimator(RemoteAnimationTarget[] appTargets,
+            RemoteAnimationTarget[] wallpaperTargets) {
         SurfaceTransactionApplier surfaceApplier = new SurfaceTransactionApplier(mDragLayer);
         ValueAnimator unlockAnimator = ValueAnimator.ofFloat(0, 1);
         unlockAnimator.setDuration(CLOSING_TRANSITION_DURATION_MS);
@@ -1224,24 +1221,23 @@
         unlockAnimator.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationStart(Animator animation) {
-                SurfaceParams[] params = new SurfaceParams[appTargets.length];
+                SurfaceTransaction transaction = new SurfaceTransaction();
                 for (int i = appTargets.length - 1; i >= 0; i--) {
-                    RemoteAnimationTargetCompat target = appTargets[i];
-                    params[i] = new SurfaceParams.Builder(target.leash)
-                            .withAlpha(1f)
-                            .withWindowCrop(target.screenSpaceBounds)
-                            .withCornerRadius(cornerRadius)
-                            .build();
+                    RemoteAnimationTarget target = appTargets[i];
+                    transaction.forSurface(target.leash)
+                            .setAlpha(1f)
+                            .setWindowCrop(target.screenSpaceBounds)
+                            .setCornerRadius(cornerRadius);
                 }
-                surfaceApplier.scheduleApply(params);
+                surfaceApplier.scheduleApply(transaction);
             }
         });
         return unlockAnimator;
     }
 
-    private static int getRotationChange(RemoteAnimationTargetCompat[] appTargets) {
+    private static int getRotationChange(RemoteAnimationTarget[] appTargets) {
         int rotationChange = 0;
-        for (RemoteAnimationTargetCompat target : appTargets) {
+        for (RemoteAnimationTarget target : appTargets) {
             if (Math.abs(target.rotationChange) > Math.abs(rotationChange)) {
                 rotationChange = target.rotationChange;
             }
@@ -1252,8 +1248,8 @@
     /**
      * Returns view on launcher that corresponds to the closing app in the list of app targets
      */
-    private @Nullable View findLauncherView(RemoteAnimationTargetCompat[] appTargets) {
-        for (RemoteAnimationTargetCompat appTarget : appTargets) {
+    private @Nullable View findLauncherView(RemoteAnimationTarget[] appTargets) {
+        for (RemoteAnimationTarget appTarget : appTargets) {
             if (appTarget.mode == MODE_CLOSING) {
                 View launcherView = findLauncherView(appTarget);
                 if (launcherView != null) {
@@ -1267,7 +1263,7 @@
     /**
      * Returns view on launcher that corresponds to the {@param runningTaskTarget}.
      */
-    private @Nullable View findLauncherView(RemoteAnimationTargetCompat runningTaskTarget) {
+    private @Nullable View findLauncherView(RemoteAnimationTarget runningTaskTarget) {
         if (runningTaskTarget == null || runningTaskTarget.taskInfo == null) {
             return null;
         }
@@ -1328,15 +1324,15 @@
      * Closing animator that animates the window into its final location on the workspace.
      */
     private RectFSpringAnim getClosingWindowAnimators(AnimatorSet animation,
-            RemoteAnimationTargetCompat[] targets, View launcherView, PointF velocityPxPerS,
+            RemoteAnimationTarget[] targets, View launcherView, PointF velocityPxPerS,
             RectF closingWindowStartRect, float startWindowCornerRadius) {
         FloatingIconView floatingIconView = null;
         FloatingWidgetView floatingWidget = null;
         RectF targetRect = new RectF();
 
-        RemoteAnimationTargetCompat runningTaskTarget = null;
+        RemoteAnimationTarget runningTaskTarget = null;
         boolean isTransluscent = false;
-        for (RemoteAnimationTargetCompat target : targets) {
+        for (RemoteAnimationTarget target : targets) {
             if (target.mode == MODE_CLOSING) {
                 runningTaskTarget = target;
                 isTransluscent = runningTaskTarget.isTranslucent;
@@ -1430,7 +1426,7 @@
     /**
      * Closing window animator that moves the window down and offscreen.
      */
-    private Animator getFallbackClosingWindowAnimators(RemoteAnimationTargetCompat[] appTargets) {
+    private Animator getFallbackClosingWindowAnimators(RemoteAnimationTarget[] appTargets) {
         final int rotationChange = getRotationChange(appTargets);
         SurfaceTransactionApplier surfaceApplier = new SurfaceTransactionApplier(mDragLayer);
         Matrix matrix = new Matrix();
@@ -1451,10 +1447,10 @@
 
             @Override
             public void onUpdate(float percent, boolean initOnly) {
-                SurfaceParams[] params = new SurfaceParams[appTargets.length];
+                SurfaceTransaction transaction = new SurfaceTransaction();
                 for (int i = appTargets.length - 1; i >= 0; i--) {
-                    RemoteAnimationTargetCompat target = appTargets[i];
-                    SurfaceParams.Builder builder = new SurfaceParams.Builder(target.leash);
+                    RemoteAnimationTarget target = appTargets[i];
+                    SurfaceProperties builder = transaction.forSurface(target.leash);
 
                     if (target.localBounds != null) {
                         tmpPos.set(target.localBounds.left, target.localBounds.top);
@@ -1476,20 +1472,19 @@
                                 tmpRect.centerY());
                         matrix.postTranslate(0, mDy.value);
                         matrix.postTranslate(tmpPos.x, tmpPos.y);
-                        builder.withMatrix(matrix)
-                                .withWindowCrop(crop)
-                                .withAlpha(mAlpha.value)
-                                .withCornerRadius(windowCornerRadius)
-                                .withShadowRadius(mShadowRadius.value);
+                        builder.setMatrix(matrix)
+                                .setWindowCrop(crop)
+                                .setAlpha(mAlpha.value)
+                                .setCornerRadius(windowCornerRadius)
+                                .setShadowRadius(mShadowRadius.value);
                     } else if (target.mode == MODE_OPENING) {
                         matrix.setTranslate(tmpPos.x, tmpPos.y);
-                        builder.withMatrix(matrix)
-                                .withWindowCrop(crop)
-                                .withAlpha(1f);
+                        builder.setMatrix(matrix)
+                                .setWindowCrop(crop)
+                                .setAlpha(1f);
                     }
-                    params[i] = builder.build();
                 }
-                surfaceApplier.scheduleApply(params);
+                surfaceApplier.scheduleApply(transaction);
             }
         });
 
@@ -1553,8 +1548,8 @@
      * the transition.
      */
     public Pair<RectFSpringAnim, AnimatorSet> createWallpaperOpenAnimations(
-            RemoteAnimationTargetCompat[] appTargets,
-            RemoteAnimationTargetCompat[] wallpaperTargets,
+            RemoteAnimationTarget[] appTargets,
+            RemoteAnimationTarget[] wallpaperTargets,
             boolean fromUnlock,
             RectF startRect,
             float startWindowCornerRadius) {
@@ -1663,9 +1658,9 @@
 
         @Override
         public void onCreateAnimation(int transit,
-                RemoteAnimationTargetCompat[] appTargets,
-                RemoteAnimationTargetCompat[] wallpaperTargets,
-                RemoteAnimationTargetCompat[] nonAppTargets,
+                RemoteAnimationTarget[] appTargets,
+                RemoteAnimationTarget[] wallpaperTargets,
+                RemoteAnimationTarget[] nonAppTargets,
                 LauncherAnimationRunner.AnimationResult result) {
             if (mLauncher.isDestroyed()) {
                 AnimatorSet anim = new AnimatorSet();
@@ -1704,9 +1699,9 @@
 
         @Override
         public void onCreateAnimation(int transit,
-                RemoteAnimationTargetCompat[] appTargets,
-                RemoteAnimationTargetCompat[] wallpaperTargets,
-                RemoteAnimationTargetCompat[] nonAppTargets,
+                RemoteAnimationTarget[] appTargets,
+                RemoteAnimationTarget[] wallpaperTargets,
+                RemoteAnimationTarget[] nonAppTargets,
                 LauncherAnimationRunner.AnimationResult result) {
             AnimatorSet anim = new AnimatorSet();
             boolean launcherClosing =
@@ -1833,7 +1828,7 @@
      * RectFSpringAnim update listener to be used for app to home animation.
      */
     private class SpringAnimRunner implements RectFSpringAnim.OnUpdateListener {
-        private final RemoteAnimationTargetCompat[] mAppTargets;
+        private final RemoteAnimationTarget[] mAppTargets;
         private final Matrix mMatrix = new Matrix();
         private final Point mTmpPos = new Point();
         private final Rect mCurrentRect = new Rect();
@@ -1844,7 +1839,7 @@
 
         private final Rect mTmpRect = new Rect();
 
-        SpringAnimRunner(RemoteAnimationTargetCompat[] appTargets, RectF targetRect,
+        SpringAnimRunner(RemoteAnimationTarget[] appTargets, RectF targetRect,
                 Rect windowTargetBounds, float startWindowCornerRadius) {
             mAppTargets = appTargets;
             mStartRadius = startWindowCornerRadius;
@@ -1859,10 +1854,10 @@
 
         @Override
         public void onUpdate(RectF currentRectF, float progress) {
-            SurfaceParams[] params = new SurfaceParams[mAppTargets.length];
+            SurfaceTransaction transaction = new SurfaceTransaction();
             for (int i = mAppTargets.length - 1; i >= 0; i--) {
-                RemoteAnimationTargetCompat target = mAppTargets[i];
-                SurfaceParams.Builder builder = new SurfaceParams.Builder(target.leash);
+                RemoteAnimationTarget target = mAppTargets[i];
+                SurfaceProperties builder = transaction.forSurface(target.leash);
 
                 if (target.localBounds != null) {
                     mTmpPos.set(target.localBounds.left, target.localBounds.top);
@@ -1897,18 +1892,17 @@
                     mMatrix.setScale(scale, scale);
                     mMatrix.postTranslate(mCurrentRect.left, mCurrentRect.top);
 
-                    builder.withMatrix(mMatrix)
-                            .withWindowCrop(mTmpRect)
-                            .withAlpha(getWindowAlpha(progress))
-                            .withCornerRadius(getCornerRadius(progress) / scale);
+                    builder.setMatrix(mMatrix)
+                            .setWindowCrop(mTmpRect)
+                            .setAlpha(getWindowAlpha(progress))
+                            .setCornerRadius(getCornerRadius(progress) / scale);
                 } else if (target.mode == MODE_OPENING) {
                     mMatrix.setTranslate(mTmpPos.x, mTmpPos.y);
-                    builder.withMatrix(mMatrix)
-                            .withAlpha(1f);
+                    builder.setMatrix(mMatrix)
+                            .setAlpha(1f);
                 }
-                params[i] = builder.build();
             }
-            mSurfaceApplier.scheduleApply(params);
+            mSurfaceApplier.scheduleApply(transaction);
         }
 
         protected float getWindowAlpha(float progress) {
diff --git a/quickstep/src/com/android/launcher3/secondarydisplay/SecondaryDisplayPredictionsImpl.java b/quickstep/src/com/android/launcher3/secondarydisplay/SecondaryDisplayPredictionsImpl.java
index 5bf727a..8720bd8 100644
--- a/quickstep/src/com/android/launcher3/secondarydisplay/SecondaryDisplayPredictionsImpl.java
+++ b/quickstep/src/com/android/launcher3/secondarydisplay/SecondaryDisplayPredictionsImpl.java
@@ -18,7 +18,9 @@
 import static com.android.launcher3.util.OnboardingPrefs.ALL_APPS_VISITED_COUNT;
 
 import android.content.Context;
+import android.view.View;
 
+import com.android.launcher3.allapps.ActivityAllAppsContainerView;
 import com.android.launcher3.appprediction.AppsDividerView;
 import com.android.launcher3.appprediction.PredictionRowView;
 import com.android.launcher3.model.BgDataModel;
@@ -39,10 +41,13 @@
     @Override
     void updateAppDivider() {
         OnboardingPrefs<?> onboardingPrefs = mActivityContext.getOnboardingPrefs();
-        mActivityContext.getAppsView().getFloatingHeaderView()
-                .findFixedRowByType(AppsDividerView.class)
-                .setShowAllAppsLabel(!onboardingPrefs.hasReachedMaxCount(ALL_APPS_VISITED_COUNT));
-        onboardingPrefs.incrementEventCount(ALL_APPS_VISITED_COUNT);
+        if (onboardingPrefs != null) {
+            mActivityContext.getAppsView().getFloatingHeaderView()
+                    .findFixedRowByType(AppsDividerView.class)
+                    .setShowAllAppsLabel(
+                            !onboardingPrefs.hasReachedMaxCount(ALL_APPS_VISITED_COUNT));
+            onboardingPrefs.incrementEventCount(ALL_APPS_VISITED_COUNT);
+        }
     }
 
     @Override
@@ -51,4 +56,12 @@
                 .findFixedRowByType(PredictionRowView.class)
                 .setPredictedApps(item.items);
     }
+
+    @Override
+    public void setLongClickListener(ActivityAllAppsContainerView<?> appsView,
+            View.OnLongClickListener onIconLongClickListener) {
+        appsView.getFloatingHeaderView()
+                .findFixedRowByType(PredictionRowView.class)
+                .setOnIconLongClickListener(onIconLongClickListener);
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
index e3fd3f9..2a78bdf 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
@@ -23,7 +23,6 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
-import android.util.FloatProperty;
 import android.view.CrossWindowBlurListeners;
 import android.view.View;
 import android.view.ViewRootImpl;
@@ -32,7 +31,6 @@
 import com.android.launcher3.BaseActivity;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
-import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.statemanager.StateManager.StateHandler;
 import com.android.launcher3.states.StateAnimationConfig;
@@ -47,43 +45,12 @@
 public class DepthController extends BaseDepthController implements StateHandler<LauncherState>,
         BaseActivity.MultiWindowModeChangedListener {
 
-    /**
-     * A property that updates the background blur within a given range of values (ie. even if the
-     * animator goes beyond 0..1, the interpolated value will still be bounded).
-     */
-    public static class ClampedDepthProperty extends FloatProperty<DepthController> {
-        private final float mMinValue;
-        private final float mMaxValue;
-
-        public ClampedDepthProperty(float minValue, float maxValue) {
-            super("depthClamped");
-            mMinValue = minValue;
-            mMaxValue = maxValue;
-        }
-
-        @Override
-        public void setValue(DepthController depthController, float depth) {
-            depthController.setDepth(Utilities.boundToRange(depth, mMinValue, mMaxValue));
-        }
-
-        @Override
-        public Float get(DepthController depthController) {
-            return depthController.mDepth;
-        }
-    }
-
     private final ViewTreeObserver.OnDrawListener mOnDrawListener = this::onLauncherDraw;
 
     private final Consumer<Boolean> mCrossWindowBlurListener = this::setCrossWindowBlursEnabled;
 
     private final Runnable mOpaquenessListener = this::applyDepthAndBlur;
 
-    /**
-     * If we're launching and app and should not be blurring the screen for performance reasons.
-     */
-    private boolean mBlurDisabledForAppLaunch;
-
-
     // Workaround for animating the depth when multiwindow mode changes.
     private boolean mIgnoreStateChangesDuringMultiWindowAnimation = false;
 
@@ -145,12 +112,8 @@
             return;
         }
 
-        float toDepth = toState.getDepth(mLauncher);
-        if (Float.compare(mDepth, toDepth) != 0) {
-            setDepth(toDepth);
-        } else if (toState == LauncherState.OVERVIEW) {
-            applyDepthAndBlur();
-        } else if (toState == LauncherState.BACKGROUND_APP) {
+        STATE_DEPTH.set(this, toState.getDepth(mLauncher));
+        if (toState == LauncherState.BACKGROUND_APP) {
             mLauncher.getDragLayer().getViewTreeObserver().addOnDrawListener(mOnDrawListener);
         }
     }
@@ -164,10 +127,7 @@
         }
 
         float toDepth = toState.getDepth(mLauncher);
-        if (Float.compare(mDepth, toDepth) != 0) {
-            animation.setFloat(this, STATE_DEPTH, toDepth,
-                    config.getInterpolator(ANIM_DEPTH, LINEAR));
-        }
+        animation.setFloat(this, STATE_DEPTH, toDepth, config.getInterpolator(ANIM_DEPTH, LINEAR));
     }
 
     @Override
@@ -198,9 +158,9 @@
         writer.println(prefix + "\tmMaxBlurRadius=" + mMaxBlurRadius);
         writer.println(prefix + "\tmCrossWindowBlursEnabled=" + mCrossWindowBlursEnabled);
         writer.println(prefix + "\tmSurface=" + mSurface);
-        writer.println(prefix + "\tmDepth=" + mDepth);
+        writer.println(prefix + "\tmStateDepth=" + STATE_DEPTH.get(this));
+        writer.println(prefix + "\tmWidgetDepth=" + WIDGET_DEPTH.get(this));
         writer.println(prefix + "\tmCurrentBlur=" + mCurrentBlur);
-        writer.println(prefix + "\tmBlurDisabledForAppLaunch=" + mBlurDisabledForAppLaunch);
         writer.println(prefix + "\tmInEarlyWakeUp=" + mInEarlyWakeUp);
         writer.println(prefix + "\tmIgnoreStateChangesDuringMultiWindowAnimation="
                 + mIgnoreStateChangesDuringMultiWindowAnimation);
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
new file mode 100644
index 0000000..0c8952d
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
@@ -0,0 +1,98 @@
+/*
+ * 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.statehandlers;
+
+import android.os.SystemProperties;
+import android.view.View;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.launcher3.uioverrides.QuickstepLauncher;
+
+/**
+ * Controls the visibility of the workspace and the resumed / paused state when desktop mode
+ * is enabled.
+ */
+public class DesktopVisibilityController {
+
+    private final Launcher mLauncher;
+
+    private boolean mFreeformTasksVisible;
+    private boolean mInOverviewState;
+
+    public DesktopVisibilityController(Launcher launcher) {
+        mLauncher = launcher;
+    }
+
+    /**
+     * Whether desktop mode is supported.
+     */
+    private boolean isDesktopModeSupported() {
+        return SystemProperties.getBoolean("persist.wm.debug.desktop_mode", false);
+    }
+
+    /**
+     * Whether freeform windows are visible in desktop mode.
+     */
+    public boolean areFreeformTasksVisible() {
+        return mFreeformTasksVisible;
+    }
+
+    /**
+     * Sets whether freeform windows are visible and updates launcher visibility based on that.
+     */
+    public void setFreeformTasksVisible(boolean freeformTasksVisible) {
+        if (freeformTasksVisible != mFreeformTasksVisible) {
+            mFreeformTasksVisible = freeformTasksVisible;
+            updateLauncherVisibility();
+        }
+    }
+
+    /**
+     * Sets whether the overview is visible and updates launcher visibility based on that.
+     */
+    public void setOverviewStateEnabled(boolean overviewStateEnabled) {
+        if (overviewStateEnabled != mInOverviewState) {
+            mInOverviewState = overviewStateEnabled;
+            updateLauncherVisibility();
+        }
+    }
+
+    /**
+     * Updates launcher visibility and state to look like it is paused or resumed depending on
+     * whether freeform windows are showing in desktop mode.
+     */
+    private void updateLauncherVisibility() {
+        StatefulActivity<LauncherState> activity =
+                QuickstepLauncher.ACTIVITY_TRACKER.getCreatedActivity();
+        View workspaceView = mLauncher.getWorkspace();
+        if (activity == null || workspaceView == null || !isDesktopModeSupported()) return;
+
+        if (mFreeformTasksVisible) {
+            workspaceView.setVisibility(View.INVISIBLE);
+            if (!mInOverviewState) {
+                // When freeform is visible & we're not in overview, we want launcher to appear
+                // paused, this ensures that taskbar displays.
+                activity.setPaused();
+            }
+        } else {
+            workspaceView.setVisibility(View.VISIBLE);
+            // If freeform isn't visible ensure that launcher appears resumed to behave normally.
+            activity.setResumed();
+        }
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/LaunchFloatingTaskButton.java b/quickstep/src/com/android/launcher3/taskbar/LaunchFloatingTaskButton.java
deleted file mode 100644
index b15669b..0000000
--- a/quickstep/src/com/android/launcher3/taskbar/LaunchFloatingTaskButton.java
+++ /dev/null
@@ -1,51 +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.taskbar;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.util.AttributeSet;
-import android.view.ContextThemeWrapper;
-
-import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.R;
-import com.android.launcher3.icons.FastBitmapDrawable;
-
-/**
- * Button in Taskbar that opens something in a floating task.
- */
-public class LaunchFloatingTaskButton extends BubbleTextView {
-
-    public LaunchFloatingTaskButton(Context context) {
-        this(context, null);
-    }
-
-    public LaunchFloatingTaskButton(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public LaunchFloatingTaskButton(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-
-        Context theme = new ContextThemeWrapper(context, R.style.AllAppsButtonTheme);
-        Bitmap bitmap = LauncherAppState.getInstance(context).getIconCache().getIconFactory()
-                .createScaledBitmapWithShadow(
-                        theme.getDrawable(R.drawable.ic_floating_task_button));
-        setIcon(new FastBitmapDrawable(bitmap));
-    }
-}
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index b9b4fc3..555cd65 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -276,13 +276,6 @@
                 && !mLauncher.getOnboardingPrefs().getBoolean(OnboardingPrefs.TASKBAR_EDU_SEEN);
     }
 
-    /**
-     * Manually ends the taskbar education flow.
-     */
-    public void hideEdu() {
-        mControllers.taskbarEduController.hideEdu();
-    }
-
     @Override
     public void onTaskbarIconLaunched(ItemInfo item) {
         InstanceId instanceId = new InstanceIdSequence().newInstanceId();
@@ -293,8 +286,6 @@
     @Override
     public void setSystemGestureInProgress(boolean inProgress) {
         super.setSystemGestureInProgress(inProgress);
-        // TODO(b/250645563): Don't show round corners when leaving in-app state, and remove
-        // forceHideBackground call entirely.
         if (!FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get()) {
             // Launcher's ScrimView will draw the background throughout the gesture. But once the
             // gesture ends, start drawing taskbar's background again since launcher might stop
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index af422cb..026fa23 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -694,14 +694,10 @@
     }
 
     /**
-     * Adds the correct spacing to 3 button nav container. No-op if using gesture nav, setup
-     * is incomplete, or in kids mode.
+     * Adds the correct spacing to 3 button nav container depending on if device is in kids mode,
+     * setup wizard, or normal 3 button nav.
      */
     private void updateButtonLayoutSpacing() {
-        if (!mContext.isThreeButtonNav() || mContext.isNavBarKidsModeActive()
-                || !mContext.isUserSetupComplete()) {
-            return;
-        }
         DeviceProfile dp = mContext.getDeviceProfile();
         Resources res = mContext.getResources();
         boolean isInSetup = !mContext.isUserSetupComplete();
@@ -719,41 +715,6 @@
             return;
         }
 
-        // Add spacing after the end of the last nav button
-        FrameLayout.LayoutParams navButtonParams =
-                (FrameLayout.LayoutParams) mNavButtonContainer.getLayoutParams();
-        navButtonParams.gravity = Gravity.END;
-        navButtonParams.width = FrameLayout.LayoutParams.WRAP_CONTENT;
-        navButtonParams.height = MATCH_PARENT;
-
-        int navMarginEnd = (int) res.getDimension(dp.inv.inlineNavButtonsEndSpacing);
-        int contextualWidth = mEndContextualContainer.getWidth();
-        // If contextual buttons are showing, we check if the end margin is enough for the
-        // contextual button to be showing - if not, move the nav buttons over a smidge
-        if (isContextualButtonShowing() && navMarginEnd < contextualWidth) {
-            // Additional spacing, eat up half of space between last icon and nav button
-            navMarginEnd += res.getDimensionPixelSize(R.dimen.taskbar_hotseat_nav_spacing) / 2;
-        }
-        navButtonParams.setMarginEnd(navMarginEnd);
-        mNavButtonContainer.setLayoutParams(navButtonParams);
-
-        // Add the spaces in between the nav buttons
-        int spaceInBetween = res.getDimensionPixelSize(R.dimen.taskbar_button_space_inbetween);
-        for (int i = 0; i < mNavButtonContainer.getChildCount(); i++) {
-            View navButton = mNavButtonContainer.getChildAt(i);
-            LinearLayout.LayoutParams buttonLayoutParams =
-                    (LinearLayout.LayoutParams) navButton.getLayoutParams();
-            buttonLayoutParams.weight = 0;
-            if (i == 0) {
-                buttonLayoutParams.setMarginEnd(spaceInBetween / 2);
-            } else if (i == mNavButtonContainer.getChildCount() - 1) {
-                buttonLayoutParams.setMarginStart(spaceInBetween / 2);
-            } else {
-                buttonLayoutParams.setMarginStart(spaceInBetween / 2);
-                buttonLayoutParams.setMarginEnd(spaceInBetween / 2);
-            }
-        }
-
         if (isInSetup) {
             handleSetupUi();
 
@@ -829,6 +790,42 @@
             mNavButtonContainer.requestLayout();
 
             mHomeButton.setOnLongClickListener(null);
+        } else if (mContext.isThreeButtonNav()) {
+            // Setup normal 3 button
+            // Add spacing after the end of the last nav button
+            FrameLayout.LayoutParams navButtonParams =
+                    (FrameLayout.LayoutParams) mNavButtonContainer.getLayoutParams();
+            navButtonParams.gravity = Gravity.END;
+            navButtonParams.width = FrameLayout.LayoutParams.WRAP_CONTENT;
+            navButtonParams.height = MATCH_PARENT;
+
+            int navMarginEnd = (int) res.getDimension(dp.inv.inlineNavButtonsEndSpacing);
+            int contextualWidth = mEndContextualContainer.getWidth();
+            // If contextual buttons are showing, we check if the end margin is enough for the
+            // contextual button to be showing - if not, move the nav buttons over a smidge
+            if (isContextualButtonShowing() && navMarginEnd < contextualWidth) {
+                // Additional spacing, eat up half of space between last icon and nav button
+                navMarginEnd += res.getDimensionPixelSize(R.dimen.taskbar_hotseat_nav_spacing) / 2;
+            }
+            navButtonParams.setMarginEnd(navMarginEnd);
+            mNavButtonContainer.setLayoutParams(navButtonParams);
+
+            // Add the spaces in between the nav buttons
+            int spaceInBetween = res.getDimensionPixelSize(R.dimen.taskbar_button_space_inbetween);
+            for (int i = 0; i < mNavButtonContainer.getChildCount(); i++) {
+                View navButton = mNavButtonContainer.getChildAt(i);
+                LinearLayout.LayoutParams buttonLayoutParams =
+                        (LinearLayout.LayoutParams) navButton.getLayoutParams();
+                buttonLayoutParams.weight = 0;
+                if (i == 0) {
+                    buttonLayoutParams.setMarginEnd(spaceInBetween / 2);
+                } else if (i == mNavButtonContainer.getChildCount() - 1) {
+                    buttonLayoutParams.setMarginStart(spaceInBetween / 2);
+                } else {
+                    buttonLayoutParams.setMarginStart(spaceInBetween / 2);
+                    buttonLayoutParams.setMarginEnd(spaceInBetween / 2);
+                }
+            }
         }
 
     }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 0c488cb..4c5e0be 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -76,6 +76,7 @@
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.popup.PopupDataProvider;
 import com.android.launcher3.taskbar.allapps.TaskbarAllAppsController;
+import com.android.launcher3.taskbar.overlay.TaskbarOverlayController;
 import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.touch.ItemClickHandler;
@@ -211,7 +212,8 @@
                 new TaskbarAutohideSuspendController(this),
                 new TaskbarPopupController(this),
                 new TaskbarForceVisibleImmersiveController(this),
-                new TaskbarAllAppsController(this, dp),
+                new TaskbarOverlayController(this, dp),
+                new TaskbarAllAppsController(),
                 new TaskbarInsetsController(this),
                 new VoiceInteractionWindowController(this),
                 isDesktopMode
@@ -243,7 +245,7 @@
     /** Updates {@link DeviceProfile} instances for any Taskbar windows. */
     public void updateDeviceProfile(DeviceProfile dp, NavigationMode navMode) {
         mNavMode = navMode;
-        mControllers.taskbarAllAppsController.updateDeviceProfile(dp);
+        mControllers.taskbarOverlayController.updateDeviceProfile(dp);
         mDeviceProfile = dp.copy(this);
         updateIconSize(getResources());
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
index 707023b..9c2d21e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
@@ -22,6 +22,7 @@
 import androidx.annotation.VisibleForTesting;
 
 import com.android.launcher3.taskbar.allapps.TaskbarAllAppsController;
+import com.android.launcher3.taskbar.overlay.TaskbarOverlayController;
 import com.android.systemui.shared.rotation.RotationButtonController;
 
 import java.io.PrintWriter;
@@ -54,6 +55,7 @@
     public final TaskbarInsetsController taskbarInsetsController;
     public final VoiceInteractionWindowController voiceInteractionWindowController;
     public final TaskbarRecentAppsController taskbarRecentAppsController;
+    public final TaskbarOverlayController taskbarOverlayController;
 
     @Nullable private LoggableTaskbarController[] mControllersToLog = null;
 
@@ -81,6 +83,7 @@
             TaskbarAutohideSuspendController taskbarAutoHideSuspendController,
             TaskbarPopupController taskbarPopupController,
             TaskbarForceVisibleImmersiveController taskbarForceVisibleImmersiveController,
+            TaskbarOverlayController taskbarOverlayController,
             TaskbarAllAppsController taskbarAllAppsController,
             TaskbarInsetsController taskbarInsetsController,
             VoiceInteractionWindowController voiceInteractionWindowController,
@@ -101,6 +104,7 @@
         this.taskbarAutohideSuspendController = taskbarAutoHideSuspendController;
         this.taskbarPopupController = taskbarPopupController;
         this.taskbarForceVisibleImmersiveController = taskbarForceVisibleImmersiveController;
+        this.taskbarOverlayController = taskbarOverlayController;
         this.taskbarAllAppsController = taskbarAllAppsController;
         this.taskbarInsetsController = taskbarInsetsController;
         this.voiceInteractionWindowController = voiceInteractionWindowController;
@@ -129,6 +133,7 @@
         taskbarEduController.init(this);
         taskbarPopupController.init(this);
         taskbarForceVisibleImmersiveController.init(this);
+        taskbarOverlayController.init(this);
         taskbarAllAppsController.init(this, sharedState.allAppsVisible);
         navButtonController.init(this);
         taskbarInsetsController.init(this);
@@ -179,7 +184,7 @@
         taskbarAutohideSuspendController.onDestroy();
         taskbarPopupController.onDestroy();
         taskbarForceVisibleImmersiveController.onDestroy();
-        taskbarAllAppsController.onDestroy();
+        taskbarOverlayController.onDestroy();
         navButtonController.onDestroy();
         taskbarInsetsController.onDestroy();
         voiceInteractionWindowController.onDestroy();
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduController.java
index 95b93fe..16cc0ac 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduController.java
@@ -35,6 +35,7 @@
 import com.android.launcher3.R;
 import com.android.launcher3.icons.BitmapInfo;
 import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
 import com.android.launcher3.uioverrides.PredictedAppIcon;
 
 import java.io.PrintWriter;
@@ -87,8 +88,10 @@
     void showEdu() {
         mActivity.setTaskbarWindowFullscreen(true);
         mActivity.getDragLayer().post(() -> {
-            mTaskbarEduView = (TaskbarEduView) mActivity.getLayoutInflater().inflate(
-                    R.layout.taskbar_edu, mActivity.getDragLayer(), false);
+            TaskbarOverlayContext overlayContext =
+                    mControllers.taskbarOverlayController.requestWindow();
+            mTaskbarEduView = (TaskbarEduView) overlayContext.getLayoutInflater().inflate(
+                    R.layout.taskbar_edu, overlayContext.getDragLayer(), false);
             mTaskbarEduView.init(new TaskbarEduCallbacks());
             mControllers.navbarButtonsViewController.setSlideInViewVisible(true);
             mTaskbarEduView.setOnCloseBeginListener(
@@ -99,12 +102,6 @@
         });
     }
 
-    void hideEdu() {
-        if (mTaskbarEduView != null) {
-            mTaskbarEduView.close(true /* animate */);
-        }
-    }
-
     /**
      * Starts the given animation, ending the previous animation first if it's still playing.
      */
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduView.java
index c0cbbd6..bb87f48 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduView.java
@@ -28,10 +28,11 @@
 
 import com.android.launcher3.Insettable;
 import com.android.launcher3.R;
+import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
 import com.android.launcher3.views.AbstractSlideInView;
 
 /** Education view about the Taskbar. */
-public class TaskbarEduView extends AbstractSlideInView<TaskbarActivityContext>
+public class TaskbarEduView extends AbstractSlideInView<TaskbarOverlayContext>
         implements Insettable {
 
     private static final int DEFAULT_OPEN_DURATION = 500;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
index bbbc1e6..32c1972 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
@@ -29,11 +29,11 @@
 import android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD
 import android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION
 import com.android.launcher3.AbstractFloatingView
-import com.android.launcher3.AbstractFloatingView.TYPE_TASKBAR_ALL_APPS
+import com.android.launcher3.AbstractFloatingView.TYPE_TASKBAR_OVERLAY_PROXY
 import com.android.launcher3.DeviceProfile
+import com.android.launcher3.R
 import com.android.launcher3.anim.AlphaUpdateListener
 import com.android.launcher3.taskbar.TaskbarControllers.LoggableTaskbarController
-import com.android.quickstep.KtR
 import java.io.PrintWriter
 
 /**
@@ -42,8 +42,7 @@
 class TaskbarInsetsController(val context: TaskbarActivityContext): LoggableTaskbarController {
 
     /** The bottom insets taskbar provides to the IME when IME is visible. */
-    val taskbarHeightForIme: Int = context.resources.getDimensionPixelSize(
-        KtR.dimen.taskbar_ime_size)
+    val taskbarHeightForIme: Int = context.resources.getDimensionPixelSize(R.dimen.taskbar_ime_size)
     private val touchableRegion: Region = Region()
     private val deviceProfileChangeListener = { _: DeviceProfile ->
         onTaskbarWindowHeightOrInsetsChanged()
@@ -158,8 +157,11 @@
         } else if (controllers.taskbarDragController.isSystemDragInProgress) {
             // Let touches pass through us.
             insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION)
-        } else if (AbstractFloatingView.hasOpenView(context, TYPE_TASKBAR_ALL_APPS)) {
-            // Let touches pass through us.
+        } else if (AbstractFloatingView.hasOpenView(context, TYPE_TASKBAR_OVERLAY_PROXY)) {
+            // Let touches pass through us if icons are hidden.
+            if (controllers.taskbarViewController.areIconsVisible()) {
+                insetsInfo.touchableRegion.set(touchableRegion)
+            }
             insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION)
         } else if (controllers.taskbarViewController.areIconsVisible()
             || AbstractFloatingView.hasOpenView(context, AbstractFloatingView.TYPE_ALL)
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index 114bfec..49dba95 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -80,10 +80,10 @@
     }
 
     /**
-     * Manually closes the all apps window.
+     * Manually closes the overlay window.
      */
-    public void hideAllApps() {
-        mControllers.taskbarAllAppsController.hide();
+    public void hideOverlayWindow() {
+        mControllers.taskbarOverlayController.hideWindow();
     }
 
     /**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index bb82d19..31c2132 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -26,7 +26,6 @@
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.ViewGroup;
 import android.widget.FrameLayout;
 
 import androidx.annotation.LayoutRes;
@@ -47,7 +46,6 @@
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.util.LauncherBindableItemsContainer;
 import com.android.launcher3.views.ActivityContext;
-import com.android.launcher3.views.AllAppsButton;
 import com.android.launcher3.views.DoubleShadowBubbleTextView;
 
 import java.util.function.Predicate;
@@ -81,12 +79,12 @@
     private @Nullable FolderIcon mLeaveBehindFolderIcon;
 
     // Only non-null when device supports having an All Apps button.
-    private @Nullable AllAppsButton mAllAppsButton;
+    private @Nullable View mAllAppsButton;
 
     private View mQsb;
 
     // Only non-null when device supports having a floating task.
-    private @Nullable BubbleTextView mFloatingTaskButton;
+    private @Nullable View mFloatingTaskButton;
     private @Nullable Intent mFloatingTaskIntent;
     private static final boolean FLOATING_TASKS_ENABLED =
             SystemProperties.getBoolean("persist.wm.debug.floating_tasks", false);
@@ -125,9 +123,8 @@
         mThemeIconsBackground = calculateThemeIconsBackground();
 
         if (FeatureFlags.ENABLE_ALL_APPS_IN_TASKBAR.get()) {
-            mAllAppsButton = new AllAppsButton(context);
-            mAllAppsButton.setLayoutParams(
-                    new ViewGroup.LayoutParams(mIconTouchSize, mIconTouchSize));
+            mAllAppsButton = LayoutInflater.from(context)
+                    .inflate(R.layout.taskbar_all_apps_button, this, false);
             mAllAppsButton.setPadding(mItemPadding, mItemPadding, mItemPadding, mItemPadding);
         }
 
@@ -137,9 +134,8 @@
         if (FLOATING_TASKS_ENABLED) {
             mFloatingTaskIntent = FloatingTaskIntentResolver.getIntent(context);
             if (mFloatingTaskIntent != null) {
-                mFloatingTaskButton = new LaunchFloatingTaskButton(context);
-                mFloatingTaskButton.setLayoutParams(
-                        new ViewGroup.LayoutParams(mIconTouchSize, mIconTouchSize));
+                mFloatingTaskButton = LayoutInflater.from(context)
+                        .inflate(R.layout.taskbar_floating_task_button, this, false);
                 mFloatingTaskButton.setPadding(mItemPadding, mItemPadding, mItemPadding,
                         mItemPadding);
             } else {
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java
index 51fa4d9..e41c75f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java
@@ -20,14 +20,11 @@
 import android.view.WindowInsets;
 
 import com.android.launcher3.allapps.ActivityAllAppsContainerView;
-import com.android.launcher3.allapps.AllAppsGridAdapter;
-import com.android.launcher3.allapps.AlphabeticalAppsList;
-import com.android.launcher3.allapps.BaseAdapterProvider;
-import com.android.launcher3.allapps.BaseAllAppsAdapter;
+import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
 
 /** All apps container accessible from taskbar. */
 public class TaskbarAllAppsContainerView extends
-        ActivityAllAppsContainerView<TaskbarAllAppsContext> {
+        ActivityAllAppsContainerView<TaskbarOverlayContext> {
 
     public TaskbarAllAppsContainerView(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
@@ -42,12 +39,4 @@
         setInsets(insets.getInsets(WindowInsets.Type.systemBars()).toRect());
         return super.onApplyWindowInsets(insets);
     }
-
-    @Override
-    protected BaseAllAppsAdapter<TaskbarAllAppsContext> createAdapter(
-            AlphabeticalAppsList<TaskbarAllAppsContext> appsList,
-            BaseAdapterProvider[] adapterProviders) {
-        return new AllAppsGridAdapter<>(mActivityContext, getLayoutInflater(), appsList,
-                adapterProviders);
-    }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContext.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContext.java
deleted file mode 100644
index 0372f67..0000000
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContext.java
+++ /dev/null
@@ -1,248 +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.taskbar.allapps;
-
-import static android.view.KeyEvent.ACTION_UP;
-import static android.view.KeyEvent.KEYCODE_BACK;
-import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
-
-import android.content.Context;
-import android.graphics.Insets;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewTreeObserver;
-import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
-import android.view.WindowInsets;
-
-import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.allapps.ActivityAllAppsContainerView;
-import com.android.launcher3.allapps.search.DefaultSearchAdapterProvider;
-import com.android.launcher3.allapps.search.SearchAdapterProvider;
-import com.android.launcher3.dot.DotInfo;
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.popup.PopupDataProvider;
-import com.android.launcher3.taskbar.BaseTaskbarContext;
-import com.android.launcher3.taskbar.TaskbarActivityContext;
-import com.android.launcher3.taskbar.TaskbarControllers;
-import com.android.launcher3.taskbar.TaskbarDragController;
-import com.android.launcher3.taskbar.TaskbarStashController;
-import com.android.launcher3.testing.TestLogging;
-import com.android.launcher3.testing.shared.TestProtocol;
-import com.android.launcher3.util.OnboardingPrefs;
-import com.android.launcher3.util.TouchController;
-import com.android.launcher3.views.BaseDragLayer;
-
-/**
- * Window context for the taskbar all apps overlay.
- * <p>
- * All apps has its own window and needs a window context. Some properties are delegated to the
- * {@link TaskbarActivityContext} such as {@link DeviceProfile} and {@link PopupDataProvider}.
- */
-class TaskbarAllAppsContext extends BaseTaskbarContext {
-    private final TaskbarActivityContext mTaskbarContext;
-    private final OnboardingPrefs<TaskbarAllAppsContext> mOnboardingPrefs;
-
-    private final TaskbarAllAppsController mWindowController;
-    private final TaskbarAllAppsViewController mAllAppsViewController;
-    private final TaskbarDragController mDragController;
-    private final TaskbarAllAppsDragLayer mDragLayer;
-    private final TaskbarAllAppsContainerView mAppsView;
-
-    // We automatically stash taskbar when all apps is opened in gesture navigation mode.
-    private final boolean mWillTaskbarBeVisuallyStashed;
-    private final int mStashedTaskbarHeight;
-
-    TaskbarAllAppsContext(
-            TaskbarActivityContext taskbarContext,
-            TaskbarAllAppsController windowController,
-            TaskbarControllers taskbarControllers) {
-        super(taskbarContext.createWindowContext(TYPE_APPLICATION_OVERLAY, null));
-        mTaskbarContext = taskbarContext;
-        mWindowController = windowController;
-        mDragController = new TaskbarDragController(this);
-        mOnboardingPrefs = new OnboardingPrefs<>(this, Utilities.getPrefs(this));
-
-        mDragLayer = new TaskbarAllAppsDragLayer(this);
-        TaskbarAllAppsSlideInView slideInView = (TaskbarAllAppsSlideInView) mLayoutInflater.inflate(
-                R.layout.taskbar_all_apps, mDragLayer, false);
-        mAllAppsViewController = new TaskbarAllAppsViewController(
-                this,
-                slideInView,
-                windowController,
-                taskbarControllers);
-        mAppsView = slideInView.getAppsView();
-
-        TaskbarStashController taskbarStashController = taskbarControllers.taskbarStashController;
-        mWillTaskbarBeVisuallyStashed = taskbarStashController.supportsVisualStashing();
-        mStashedTaskbarHeight = taskbarStashController.getStashedHeight();
-    }
-
-    TaskbarAllAppsViewController getAllAppsViewController() {
-        return mAllAppsViewController;
-    }
-
-    @Override
-    public DeviceProfile getDeviceProfile() {
-        return mWindowController.getDeviceProfile();
-    }
-
-    @Override
-    public TaskbarDragController getDragController() {
-        return mDragController;
-    }
-
-    @Override
-    public TaskbarAllAppsDragLayer getDragLayer() {
-        return mDragLayer;
-    }
-
-    @Override
-    public TaskbarAllAppsContainerView getAppsView() {
-        return mAppsView;
-    }
-
-    @Override
-    public OnboardingPrefs<TaskbarAllAppsContext> getOnboardingPrefs() {
-        return mOnboardingPrefs;
-    }
-
-    @Override
-    public boolean isBindingItems() {
-        return mTaskbarContext.isBindingItems();
-    }
-
-    @Override
-    public View.OnClickListener getItemOnClickListener() {
-        return mTaskbarContext.getItemOnClickListener();
-    }
-
-    @Override
-    public PopupDataProvider getPopupDataProvider() {
-        return mTaskbarContext.getPopupDataProvider();
-    }
-
-    @Override
-    public DotInfo getDotInfoForItem(ItemInfo info) {
-        return mTaskbarContext.getDotInfoForItem(info);
-    }
-
-    @Override
-    public void onDragStart() {}
-
-    @Override
-    public void onDragEnd() {
-        mWindowController.maybeCloseWindow();
-    }
-
-    @Override
-    public void onPopupVisibilityChanged(boolean isVisible) {}
-
-    @Override
-    public SearchAdapterProvider<?> createSearchAdapterProvider(
-            ActivityAllAppsContainerView<?> appsView) {
-        return new DefaultSearchAdapterProvider(this);
-    }
-
-    /** Root drag layer for this context. */
-    private static class TaskbarAllAppsDragLayer extends
-            BaseDragLayer<TaskbarAllAppsContext> implements OnComputeInternalInsetsListener {
-
-        private TaskbarAllAppsDragLayer(Context context) {
-            super(context, null, 1);
-            setClipChildren(false);
-            recreateControllers();
-        }
-
-        @Override
-        protected void onAttachedToWindow() {
-            super.onAttachedToWindow();
-            getViewTreeObserver().addOnComputeInternalInsetsListener(this);
-        }
-
-        @Override
-        protected void onDetachedFromWindow() {
-            super.onDetachedFromWindow();
-            getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
-        }
-
-        @Override
-        public void recreateControllers() {
-            mControllers = new TouchController[]{mActivity.mDragController};
-        }
-
-        @Override
-        public boolean dispatchTouchEvent(MotionEvent ev) {
-            TestLogging.recordMotionEvent(TestProtocol.SEQUENCE_MAIN, "Touch event", ev);
-            return super.dispatchTouchEvent(ev);
-        }
-
-        @Override
-        public boolean dispatchKeyEvent(KeyEvent event) {
-            if (event.getAction() == ACTION_UP && event.getKeyCode() == KEYCODE_BACK) {
-                AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mActivity);
-                if (topView != null && topView.onBackPressed()) {
-                    return true;
-                }
-            }
-            return super.dispatchKeyEvent(event);
-        }
-
-        @Override
-        public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) {
-            if (mActivity.mDragController.isSystemDragInProgress()) {
-                inoutInfo.touchableRegion.setEmpty();
-                inoutInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
-            }
-        }
-
-        @Override
-        public WindowInsets onApplyWindowInsets(WindowInsets insets) {
-            return updateInsetsDueToStashing(insets);
-        }
-
-        /**
-         * Taskbar automatically stashes when opening all apps, but we don't report the insets as
-         * changing to avoid moving the underlying app. But internally, the apps view should still
-         * layout according to the stashed insets rather than the unstashed insets. So this method
-         * does two things:
-         * 1) Sets navigationBars bottom inset to stashedHeight.
-         * 2) Sets tappableInsets bottom inset to 0.
-         */
-        private WindowInsets updateInsetsDueToStashing(WindowInsets oldInsets) {
-            if (!mActivity.mWillTaskbarBeVisuallyStashed) {
-                return oldInsets;
-            }
-            WindowInsets.Builder updatedInsetsBuilder = new WindowInsets.Builder(oldInsets);
-
-            Insets oldNavInsets = oldInsets.getInsets(WindowInsets.Type.navigationBars());
-            Insets newNavInsets = Insets.of(oldNavInsets.left, oldNavInsets.top, oldNavInsets.right,
-                    mActivity.mStashedTaskbarHeight);
-            updatedInsetsBuilder.setInsets(WindowInsets.Type.navigationBars(), newNavInsets);
-
-            Insets oldTappableInsets = oldInsets.getInsets(WindowInsets.Type.tappableElement());
-            Insets newTappableInsets = Insets.of(oldTappableInsets.left, oldTappableInsets.top,
-                    oldTappableInsets.right, 0);
-            updatedInsetsBuilder.setInsets(WindowInsets.Type.tappableElement(), newTappableInsets);
-
-            return updatedInsetsBuilder.build();
-        }
-    }
-}
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
index 1671a0f..ea37944 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
@@ -15,34 +15,17 @@
  */
 package com.android.launcher3.taskbar.allapps;
 
-import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
-
-import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
-import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE;
-
-import android.content.Context;
-import android.graphics.PixelFormat;
-import android.view.Gravity;
-import android.view.MotionEvent;
-import android.view.WindowManager;
-import android.view.WindowManager.LayoutParams;
-
 import androidx.annotation.Nullable;
 
-import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.R;
 import com.android.launcher3.appprediction.PredictionRowView;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.taskbar.TaskbarActivityContext;
 import com.android.launcher3.taskbar.TaskbarControllers;
-import com.android.systemui.shared.system.TaskStackChangeListener;
-import com.android.systemui.shared.system.TaskStackChangeListeners;
+import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
 
 import java.util.List;
-import java.util.Optional;
 
 /**
  * Handles the all apps overlay window initialization, updates, and its data.
@@ -57,36 +40,14 @@
  */
 public final class TaskbarAllAppsController {
 
-    private static final String WINDOW_TITLE = "Taskbar All Apps";
-
-    private final TaskbarActivityContext mTaskbarContext;
-    private final TaskbarAllAppsProxyView mProxyView;
-    private final LayoutParams mLayoutParams;
-
-    private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
-        @Override
-        public void onTaskStackChanged() {
-            mProxyView.close(false);
-        }
-    };
-
-    private DeviceProfile mDeviceProfile;
     private TaskbarControllers mControllers;
-    /** Window context for all apps if it is open. */
-    private @Nullable TaskbarAllAppsContext mAllAppsContext;
+    private @Nullable TaskbarAllAppsContainerView mAppsView;
 
     // Application data models.
     private AppInfo[] mApps;
     private int mAppsModelFlags;
     private List<ItemInfo> mPredictedApps;
 
-    public TaskbarAllAppsController(TaskbarActivityContext context, DeviceProfile dp) {
-        mDeviceProfile = dp;
-        mTaskbarContext = context;
-        mProxyView = new TaskbarAllAppsProxyView(mTaskbarContext);
-        mLayoutParams = createLayoutParams();
-    }
-
     /** Initialize the controller. */
     public void init(TaskbarControllers controllers, boolean allAppsVisible) {
         if (!FeatureFlags.ENABLE_ALL_APPS_IN_TASKBAR.get()) {
@@ -111,8 +72,8 @@
 
         mApps = apps;
         mAppsModelFlags = flags;
-        if (mAllAppsContext != null) {
-            mAllAppsContext.getAppsView().getAppsStore().setApps(mApps, mAppsModelFlags);
+        if (mAppsView != null) {
+            mAppsView.getAppsStore().setApps(mApps, mAppsModelFlags);
         }
     }
 
@@ -123,8 +84,8 @@
         }
 
         mPredictedApps = predictedApps;
-        if (mAllAppsContext != null) {
-            mAllAppsContext.getAppsView().getFloatingHeaderView()
+        if (mAppsView != null) {
+            mAppsView.getFloatingHeaderView()
                     .findFixedRowByType(PredictionRowView.class)
                     .setPredictedApps(mPredictedApps);
         }
@@ -136,120 +97,30 @@
     }
 
     private void show(boolean animate) {
-        if (mProxyView.isOpen()) {
+        if (mAppsView != null) {
             return;
         }
-        mProxyView.show();
         // mControllers and getSharedState should never be null here. Do not handle null-pointer
         // to catch invalid states.
         mControllers.getSharedState().allAppsVisible = true;
 
-        mAllAppsContext = new TaskbarAllAppsContext(mTaskbarContext, this, mControllers);
-        mAllAppsContext.getDragController().init(mControllers);
-        TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
-        Optional.ofNullable(mAllAppsContext.getSystemService(WindowManager.class))
-                .ifPresent(m -> m.addView(mAllAppsContext.getDragLayer(), mLayoutParams));
+        TaskbarOverlayContext overlayContext =
+                mControllers.taskbarOverlayController.requestWindow();
+        TaskbarAllAppsSlideInView slideInView =
+                (TaskbarAllAppsSlideInView) overlayContext.getLayoutInflater().inflate(
+                        R.layout.taskbar_all_apps, overlayContext.getDragLayer(), false);
+        slideInView.addOnCloseListener(() -> {
+            mControllers.getSharedState().allAppsVisible = false;
+            mAppsView = null;
+        });
+        TaskbarAllAppsViewController viewController = new TaskbarAllAppsViewController(
+                overlayContext, slideInView, mControllers);
 
-        mAllAppsContext.getAppsView().getAppsStore().setApps(mApps, mAppsModelFlags);
-        mAllAppsContext.getAppsView().getFloatingHeaderView()
+        viewController.show(animate);
+        mAppsView = overlayContext.getAppsView();
+        mAppsView.getAppsStore().setApps(mApps, mAppsModelFlags);
+        mAppsView.getFloatingHeaderView()
                 .findFixedRowByType(PredictionRowView.class)
                 .setPredictedApps(mPredictedApps);
-        mAllAppsContext.getAllAppsViewController().show(animate);
-    }
-
-    /** Closes the {@link TaskbarAllAppsContainerView}. */
-    public void hide() {
-        mProxyView.close(true);
-    }
-
-    /**
-     * Removes the all apps window from the hierarchy, if all floating views are closed and there is
-     * no system drag operation in progress.
-     * <p>
-     * This method should be called after an exit animation finishes, if applicable.
-     */
-    void maybeCloseWindow() {
-        if (mAllAppsContext != null && (AbstractFloatingView.hasOpenView(mAllAppsContext, TYPE_ALL)
-                || mAllAppsContext.getDragController().isSystemDragInProgress())) {
-            return;
-        }
-        mProxyView.close(false);
-        // mControllers and getSharedState should never be null here. Do not handle null-pointer
-        // to catch invalid states.
-        mControllers.getSharedState().allAppsVisible = false;
-        onDestroy();
-    }
-
-    /** Destroys the controller and any All Apps window if present. */
-    public void onDestroy() {
-        TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
-        Optional.ofNullable(mAllAppsContext)
-                .map(c -> c.getSystemService(WindowManager.class))
-                .ifPresent(m -> m.removeViewImmediate(mAllAppsContext.getDragLayer()));
-        mAllAppsContext = null;
-    }
-
-    /** Updates {@link DeviceProfile} instance for Taskbar's All Apps window. */
-    public void updateDeviceProfile(DeviceProfile dp) {
-        mDeviceProfile = dp;
-        Optional.ofNullable(mAllAppsContext).ifPresent(c -> {
-            AbstractFloatingView.closeAllOpenViewsExcept(c, false, TYPE_REBIND_SAFE);
-            c.dispatchDeviceProfileChanged();
-        });
-    }
-
-    DeviceProfile getDeviceProfile() {
-        return mDeviceProfile;
-    }
-
-    private LayoutParams createLayoutParams() {
-        LayoutParams layoutParams = new LayoutParams(
-                TYPE_APPLICATION_OVERLAY,
-                WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
-                PixelFormat.TRANSLUCENT);
-        layoutParams.setTitle(WINDOW_TITLE);
-        layoutParams.gravity = Gravity.BOTTOM;
-        layoutParams.packageName = mTaskbarContext.getPackageName();
-        layoutParams.setFitInsetsTypes(0); // Handled by container view.
-        layoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-        layoutParams.setSystemApplicationOverlay(true);
-        return layoutParams;
-    }
-
-    /**
-     * Proxy view connecting taskbar drag layer to the all apps window.
-     * <p>
-     * The all apps view is in a separate window and has its own drag layer, but this proxy lets it
-     * behave as though its in the taskbar drag layer. For instance, when the taskbar closes all
-     * {@link AbstractFloatingView} instances, the all apps window will also close.
-     */
-    private class TaskbarAllAppsProxyView extends AbstractFloatingView {
-
-        private TaskbarAllAppsProxyView(Context context) {
-            super(context, null);
-        }
-
-        private void show() {
-            mIsOpen = true;
-            mTaskbarContext.getDragLayer().addView(this);
-        }
-
-        @Override
-        protected void handleClose(boolean animate) {
-            mTaskbarContext.getDragLayer().removeView(this);
-            Optional.ofNullable(mAllAppsContext)
-                    .map(TaskbarAllAppsContext::getAllAppsViewController)
-                    .ifPresent(v -> v.close(animate));
-        }
-
-        @Override
-        protected boolean isOfType(int type) {
-            return (type & TYPE_TASKBAR_ALL_APPS) != 0;
-        }
-
-        @Override
-        public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
-            return false;
-        }
     }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
index 9d48c8d..c8bfc2a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
@@ -28,10 +28,11 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Insettable;
 import com.android.launcher3.R;
+import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
 import com.android.launcher3.views.AbstractSlideInView;
 
 /** Wrapper for taskbar all apps with slide-in behavior. */
-public class TaskbarAllAppsSlideInView extends AbstractSlideInView<TaskbarAllAppsContext>
+public class TaskbarAllAppsSlideInView extends AbstractSlideInView<TaskbarOverlayContext>
         implements Insettable, DeviceProfile.OnDeviceProfileChangeListener {
     private TaskbarAllAppsContainerView mAppsView;
     private float mShiftRange;
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java
index 128fa5e..54392b2 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java
@@ -25,6 +25,7 @@
 import com.android.launcher3.taskbar.NavbarButtonsViewController;
 import com.android.launcher3.taskbar.TaskbarControllers;
 import com.android.launcher3.taskbar.TaskbarStashController;
+import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
 
 /**
  * Handles the {@link TaskbarAllAppsContainerView} behavior and synchronizes its transitions with
@@ -32,16 +33,15 @@
  */
 final class TaskbarAllAppsViewController {
 
-    private final TaskbarAllAppsContext mContext;
+    private final TaskbarOverlayContext mContext;
     private final TaskbarAllAppsSlideInView mSlideInView;
     private final TaskbarAllAppsContainerView mAppsView;
     private final TaskbarStashController mTaskbarStashController;
     private final NavbarButtonsViewController mNavbarButtonsViewController;
 
     TaskbarAllAppsViewController(
-            TaskbarAllAppsContext context,
+            TaskbarOverlayContext context,
             TaskbarAllAppsSlideInView slideInView,
-            TaskbarAllAppsController windowController,
             TaskbarControllers taskbarControllers) {
 
         mContext = context;
@@ -53,7 +53,6 @@
         setUpIconLongClick();
         setUpAppDivider();
         setUpTaskbarStashing();
-        mSlideInView.addOnCloseListener(windowController::maybeCloseWindow);
     }
 
     /** Starts the {@link TaskbarAllAppsSlideInView} enter transition. */
diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java
new file mode 100644
index 0000000..5701de0
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java
@@ -0,0 +1,146 @@
+/*
+ * 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.taskbar.overlay;
+
+import android.content.Context;
+import android.view.View;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.allapps.ActivityAllAppsContainerView;
+import com.android.launcher3.allapps.search.DefaultSearchAdapterProvider;
+import com.android.launcher3.allapps.search.SearchAdapterProvider;
+import com.android.launcher3.dot.DotInfo;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.popup.PopupDataProvider;
+import com.android.launcher3.taskbar.BaseTaskbarContext;
+import com.android.launcher3.taskbar.TaskbarActivityContext;
+import com.android.launcher3.taskbar.TaskbarControllers;
+import com.android.launcher3.taskbar.TaskbarDragController;
+import com.android.launcher3.taskbar.TaskbarStashController;
+import com.android.launcher3.taskbar.allapps.TaskbarAllAppsContainerView;
+import com.android.launcher3.util.OnboardingPrefs;
+
+/**
+ * Window context for the taskbar overlays such as All Apps and EDU.
+ * <p>
+ * Overlays have their own window and need a window context. Some properties are delegated to the
+ * {@link TaskbarActivityContext} such as {@link PopupDataProvider}.
+ */
+public class TaskbarOverlayContext extends BaseTaskbarContext {
+    private final TaskbarActivityContext mTaskbarContext;
+    private final OnboardingPrefs<TaskbarOverlayContext> mOnboardingPrefs;
+
+    private final TaskbarOverlayController mOverlayController;
+    private final TaskbarDragController mDragController;
+    private final TaskbarOverlayDragLayer mDragLayer;
+
+    // We automatically stash taskbar when All Apps is opened in gesture navigation mode.
+    private final boolean mWillTaskbarBeVisuallyStashed;
+    private final int mStashedTaskbarHeight;
+
+    public TaskbarOverlayContext(
+            Context windowContext,
+            TaskbarActivityContext taskbarContext,
+            TaskbarControllers controllers) {
+        super(windowContext);
+        mTaskbarContext = taskbarContext;
+        mOverlayController = controllers.taskbarOverlayController;
+        mDragController = new TaskbarDragController(this);
+        mDragController.init(controllers);
+        mOnboardingPrefs = new OnboardingPrefs<>(this, Utilities.getPrefs(this));
+        mDragLayer = new TaskbarOverlayDragLayer(this);
+
+        TaskbarStashController taskbarStashController = controllers.taskbarStashController;
+        mWillTaskbarBeVisuallyStashed = taskbarStashController.supportsVisualStashing();
+        mStashedTaskbarHeight = taskbarStashController.getStashedHeight();
+    }
+
+    boolean willTaskbarBeVisuallyStashed() {
+        return mWillTaskbarBeVisuallyStashed;
+    }
+
+    int getStashedTaskbarHeight() {
+        return mStashedTaskbarHeight;
+    }
+
+    public TaskbarOverlayController getOverlayController() {
+        return mOverlayController;
+    }
+
+    @Override
+    public DeviceProfile getDeviceProfile() {
+        return mOverlayController.getDeviceProfile();
+    }
+
+    @Override
+    public TaskbarDragController getDragController() {
+        return mDragController;
+    }
+
+    @Override
+    public TaskbarOverlayDragLayer getDragLayer() {
+        return mDragLayer;
+    }
+
+    @Override
+    public TaskbarAllAppsContainerView getAppsView() {
+        return mDragLayer.findViewById(R.id.apps_view);
+    }
+
+    @Override
+    public OnboardingPrefs<TaskbarOverlayContext> getOnboardingPrefs() {
+        return mOnboardingPrefs;
+    }
+
+    @Override
+    public boolean isBindingItems() {
+        return mTaskbarContext.isBindingItems();
+    }
+
+    @Override
+    public View.OnClickListener getItemOnClickListener() {
+        return mTaskbarContext.getItemOnClickListener();
+    }
+
+    @Override
+    public PopupDataProvider getPopupDataProvider() {
+        return mTaskbarContext.getPopupDataProvider();
+    }
+
+    @Override
+    public DotInfo getDotInfoForItem(ItemInfo info) {
+        return mTaskbarContext.getDotInfoForItem(info);
+    }
+
+    @Override
+    public void onDragStart() {}
+
+    @Override
+    public void onDragEnd() {
+        mOverlayController.maybeCloseWindow();
+    }
+
+    @Override
+    public void onPopupVisibilityChanged(boolean isVisible) {}
+
+    @Override
+    public SearchAdapterProvider<?> createSearchAdapterProvider(
+            ActivityAllAppsContainerView<?> appsView) {
+        return new DefaultSearchAdapterProvider(this);
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java
new file mode 100644
index 0000000..0574058
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java
@@ -0,0 +1,197 @@
+/*
+ * 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.taskbar.overlay;
+
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
+import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
+import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.taskbar.TaskbarActivityContext;
+import com.android.launcher3.taskbar.TaskbarControllers;
+import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
+
+import java.util.Optional;
+
+/**
+ * Handles the Taskbar overlay window lifecycle.
+ * <p>
+ * Overlays need to be inflated in a separate window so that have the correct hierarchy. For
+ * instance, they need to be below the notification tray. If there are multiple overlays open, the
+ * same window is used.
+ */
+public final class TaskbarOverlayController {
+
+    private static final String WINDOW_TITLE = "Taskbar Overlay";
+
+    private final TaskbarActivityContext mTaskbarContext;
+    private final Context mWindowContext;
+    private final TaskbarOverlayProxyView mProxyView;
+    private final LayoutParams mLayoutParams;
+
+    private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
+        @Override
+        public void onTaskStackChanged() {
+            mProxyView.close(false);
+        }
+    };
+
+    private DeviceProfile mDeviceProfile;
+    private @Nullable TaskbarOverlayContext mOverlayContext;
+    private TaskbarControllers mControllers; // Initialized in init.
+
+    public TaskbarOverlayController(
+            TaskbarActivityContext taskbarContext, DeviceProfile deviceProfile) {
+        mTaskbarContext = taskbarContext;
+        mWindowContext = mTaskbarContext.createWindowContext(TYPE_APPLICATION_OVERLAY, null);
+        mProxyView = new TaskbarOverlayProxyView();
+        mLayoutParams = createLayoutParams();
+        mDeviceProfile = deviceProfile;
+    }
+
+    /** Initialize the controller. */
+    public void init(TaskbarControllers controllers) {
+        mControllers = controllers;
+    }
+
+    /**
+     * Creates a window for Taskbar overlays, if it does not already exist. Returns the window
+     * context for the current overlay window.
+     */
+    public TaskbarOverlayContext requestWindow() {
+        if (mOverlayContext == null) {
+            mOverlayContext = new TaskbarOverlayContext(
+                    mWindowContext, mTaskbarContext, mControllers);
+        }
+
+        if (!mProxyView.isOpen()) {
+            mProxyView.show();
+            Optional.ofNullable(mOverlayContext.getSystemService(WindowManager.class))
+                    .ifPresent(m -> m.addView(mOverlayContext.getDragLayer(), mLayoutParams));
+            TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
+        }
+
+        return mOverlayContext;
+    }
+
+    /** Hides the current overlay window with animation. */
+    public void hideWindow() {
+        mProxyView.close(true);
+    }
+
+    /**
+     * Removes the overlay window from the hierarchy, if all floating views are closed and there is
+     * no system drag operation in progress.
+     * <p>
+     * This method should be called after an exit animation finishes, if applicable.
+     */
+    @SuppressLint("WrongConstant")
+    void maybeCloseWindow() {
+        if (mOverlayContext != null && (AbstractFloatingView.hasOpenView(mOverlayContext, TYPE_ALL)
+                || mOverlayContext.getDragController().isSystemDragInProgress())) {
+            return;
+        }
+        mProxyView.close(false);
+        onDestroy();
+    }
+
+    /** Destroys the controller and any overlay window if present. */
+    public void onDestroy() {
+        TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
+        Optional.ofNullable(mOverlayContext)
+                .map(c -> c.getSystemService(WindowManager.class))
+                .ifPresent(m -> m.removeViewImmediate(mOverlayContext.getDragLayer()));
+        mOverlayContext = null;
+    }
+
+    /** The current device profile for the overlay window. */
+    public DeviceProfile getDeviceProfile() {
+        return mDeviceProfile;
+    }
+
+    /** Updates {@link DeviceProfile} instance for Taskbar's overlay window. */
+    public void updateDeviceProfile(DeviceProfile dp) {
+        mDeviceProfile = dp;
+        Optional.ofNullable(mOverlayContext).ifPresent(c -> {
+            AbstractFloatingView.closeAllOpenViewsExcept(c, false, TYPE_REBIND_SAFE);
+            c.dispatchDeviceProfileChanged();
+        });
+    }
+
+    @SuppressLint("WrongConstant")
+    private LayoutParams createLayoutParams() {
+        LayoutParams layoutParams = new LayoutParams(
+                TYPE_APPLICATION_OVERLAY,
+                LayoutParams.FLAG_SPLIT_TOUCH,
+                PixelFormat.TRANSLUCENT);
+        layoutParams.setTitle(WINDOW_TITLE);
+        layoutParams.gravity = Gravity.BOTTOM;
+        layoutParams.packageName = mTaskbarContext.getPackageName();
+        layoutParams.setFitInsetsTypes(0); // Handled by container view.
+        layoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+        layoutParams.setSystemApplicationOverlay(true);
+        return layoutParams;
+    }
+
+    /**
+     * Proxy view connecting taskbar drag layer to the overlay window.
+     *
+     * Overlays are in a separate window and has its own drag layer, but this proxy lets its views
+     * behave as though they are in the taskbar drag layer. For instance, when the taskbar closes
+     * all {@link AbstractFloatingView} instances, the overlay window will also close.
+     */
+    private class TaskbarOverlayProxyView extends AbstractFloatingView {
+
+        private TaskbarOverlayProxyView() {
+            super(mTaskbarContext, null);
+        }
+
+        private void show() {
+            mIsOpen = true;
+            mTaskbarContext.getDragLayer().addView(this);
+        }
+
+        @Override
+        protected void handleClose(boolean animate) {
+            mTaskbarContext.getDragLayer().removeView(this);
+            Optional.ofNullable(mOverlayContext).ifPresent(c -> closeAllOpenViews(c, animate));
+        }
+
+        @Override
+        protected boolean isOfType(int type) {
+            return (type & TYPE_TASKBAR_OVERLAY_PROXY) != 0;
+        }
+
+        @Override
+        public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+            return false;
+        }
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java
new file mode 100644
index 0000000..044afd6
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java
@@ -0,0 +1,126 @@
+/*
+ * 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.taskbar.overlay;
+
+import static android.view.KeyEvent.ACTION_UP;
+import static android.view.KeyEvent.KEYCODE_BACK;
+import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
+
+import android.content.Context;
+import android.graphics.Insets;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.WindowInsets;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.shared.TestProtocol;
+import com.android.launcher3.util.TouchController;
+import com.android.launcher3.views.BaseDragLayer;
+
+/** Root drag layer for the Taskbar overlay window. */
+public class TaskbarOverlayDragLayer extends
+        BaseDragLayer<TaskbarOverlayContext> implements
+        ViewTreeObserver.OnComputeInternalInsetsListener {
+
+    TaskbarOverlayDragLayer(Context context) {
+        super(context, null, 1);
+        setClipChildren(false);
+        recreateControllers();
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        getViewTreeObserver().addOnComputeInternalInsetsListener(this);
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
+    }
+
+    @Override
+    public void recreateControllers() {
+        mControllers = new TouchController[]{mActivity.getDragController()};
+    }
+
+    @Override
+    public boolean dispatchTouchEvent(MotionEvent ev) {
+        TestLogging.recordMotionEvent(TestProtocol.SEQUENCE_MAIN, "Touch event", ev);
+        return super.dispatchTouchEvent(ev);
+    }
+
+    @Override
+    public boolean dispatchKeyEvent(KeyEvent event) {
+        if (event.getAction() == ACTION_UP && event.getKeyCode() == KEYCODE_BACK) {
+            AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mActivity);
+            if (topView != null && topView.onBackPressed()) {
+                return true;
+            }
+        }
+        return super.dispatchKeyEvent(event);
+    }
+
+    @Override
+    public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) {
+        if (mActivity.getDragController().isSystemDragInProgress()) {
+            inoutInfo.touchableRegion.setEmpty();
+            inoutInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
+        }
+    }
+
+    @Override
+    public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+        return updateInsetsDueToStashing(insets);
+    }
+
+    @Override
+    public void onViewRemoved(View child) {
+        super.onViewRemoved(child);
+        mActivity.getOverlayController().maybeCloseWindow();
+    }
+
+    /**
+     * Taskbar automatically stashes when opening all apps, but we don't report the insets as
+     * changing to avoid moving the underlying app. But internally, the apps view should still
+     * layout according to the stashed insets rather than the unstashed insets. So this method
+     * does two things:
+     * 1) Sets navigationBars bottom inset to stashedHeight.
+     * 2) Sets tappableInsets bottom inset to 0.
+     */
+    private WindowInsets updateInsetsDueToStashing(WindowInsets oldInsets) {
+        if (!mActivity.willTaskbarBeVisuallyStashed()) {
+            return oldInsets;
+        }
+        WindowInsets.Builder updatedInsetsBuilder = new WindowInsets.Builder(oldInsets);
+
+        Insets oldNavInsets = oldInsets.getInsets(WindowInsets.Type.navigationBars());
+        Insets newNavInsets = Insets.of(oldNavInsets.left, oldNavInsets.top, oldNavInsets.right,
+                mActivity.getStashedTaskbarHeight());
+        updatedInsetsBuilder.setInsets(WindowInsets.Type.navigationBars(), newNavInsets);
+
+        Insets oldTappableInsets = oldInsets.getInsets(WindowInsets.Type.tappableElement());
+        Insets newTappableInsets = Insets.of(oldTappableInsets.left, oldTappableInsets.top,
+                oldTappableInsets.right, 0);
+        updatedInsetsBuilder.setInsets(WindowInsets.Type.tappableElement(), newTappableInsets);
+
+        return updatedInsetsBuilder.build();
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
index f450496..729eea9 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
@@ -17,10 +17,18 @@
 package com.android.launcher3.uioverrides;
 
 import android.app.Person;
+import android.appwidget.AppWidgetHost;
 import android.content.pm.ShortcutInfo;
 
-import com.android.launcher3.Utilities;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
+import com.android.launcher3.Utilities;
+import com.android.launcher3.widget.LauncherAppWidgetHost;
+
+/**
+ * A wrapper for the hidden API calls
+ */
 public class ApiWrapper {
 
     public static final boolean TASKBAR_DRAWN_IN_PROCESS = true;
@@ -29,4 +37,14 @@
         Person[] persons = si.getPersons();
         return persons == null ? Utilities.EMPTY_PERSON_ARRAY : persons;
     }
+
+    /**
+     * Set the interaction handler for the host
+     * @param host AppWidgetHost that needs the interaction handler
+     * @param handler InteractionHandler for the views in the host
+     */
+    public static void setHostInteractionHandler(@NonNull AppWidgetHost host,
+            @Nullable LauncherAppWidgetHost.LauncherWidgetInteractionHandler handler) {
+        host.setInteractionHandler(handler::onInteraction);
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
index 08d147f..2dde6b6 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
@@ -33,10 +33,12 @@
 import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.util.ActivityOptionsWrapper;
+import com.android.launcher3.widget.LauncherAppWidgetHost;
 import com.android.launcher3.widget.LauncherAppWidgetHostView;
 
 /** Provides a Quickstep specific animation when launching an activity from an app widget. */
-class QuickstepInteractionHandler implements RemoteViews.InteractionHandler {
+class QuickstepInteractionHandler implements
+        LauncherAppWidgetHost.LauncherWidgetInteractionHandler {
 
     private static final String TAG = "QuickstepInteractionHandler";
 
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index ee9845b..23794b0 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -65,6 +65,7 @@
 import android.os.SystemProperties;
 import android.view.Display;
 import android.view.HapticFeedbackConstants;
+import android.view.RemoteAnimationTarget;
 import android.view.View;
 import android.view.WindowManagerGlobal;
 import android.window.SplashScreen;
@@ -96,6 +97,7 @@
 import com.android.launcher3.proxy.ProxyActivityStarter;
 import com.android.launcher3.proxy.StartActivityParams;
 import com.android.launcher3.statehandlers.DepthController;
+import com.android.launcher3.statehandlers.DesktopVisibilityController;
 import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory;
 import com.android.launcher3.statemanager.StateManager.StateHandler;
 import com.android.launcher3.taskbar.LauncherTaskbarUIController;
@@ -139,7 +141,6 @@
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 import com.android.systemui.unfold.UnfoldSharedComponent;
 import com.android.systemui.unfold.UnfoldTransitionFactory;
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
@@ -167,6 +168,7 @@
     private FixedContainerItems mAllAppsPredictions;
     private HotseatPredictionController mHotseatPredictionController;
     private DepthController mDepthController;
+    private DesktopVisibilityController mDesktopVisibilityController;
     private QuickstepTransitionManager mAppTransitionManager;
     private OverviewActionsView mActionsView;
     private TISBindHelper mTISBindHelper;
@@ -186,6 +188,8 @@
 
     private SafeCloseable mViewCapture;
 
+    private boolean mEnableWidgetDepth;
+
     @Override
     protected void setupViews() {
         super.setupViews();
@@ -205,7 +209,11 @@
 
         mTISBindHelper = new TISBindHelper(this, this::onTISConnected);
         mDepthController = new DepthController(this);
+        mDesktopVisibilityController = new DesktopVisibilityController(this);
         mHotseatPredictionController = new HotseatPredictionController(this);
+
+        mEnableWidgetDepth = ENABLE_WIDGET_PICKER_DEPTH.get()
+                && SystemProperties.getBoolean("ro.launcher.depth.widget", true);
     }
 
     @Override
@@ -482,7 +490,8 @@
 
     protected LauncherAppWidgetHost createAppWidgetHost() {
         LauncherAppWidgetHost appWidgetHost = super.createAppWidgetHost();
-        appWidgetHost.setInteractionHandler(new QuickstepInteractionHandler(this));
+        ApiWrapper.setHostInteractionHandler(appWidgetHost,
+                new QuickstepInteractionHandler(this));
         return appWidgetHost;
     }
 
@@ -587,9 +596,7 @@
     public void onWidgetsTransition(float progress) {
         super.onWidgetsTransition(progress);
         onTaskbarInAppDisplayProgressUpdate(progress, WIDGETS_PAGE_PROGRESS_INDEX);
-        // Change of wallpaper depth in widget picker is disabled for tests as it causes flakiness
-        // on very slow cuttlefish devices.
-        if (ENABLE_WIDGET_PICKER_DEPTH.get() && !Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+        if (mEnableWidgetDepth) {
             WIDGET_DEPTH.set(getDepthController(),
                     Utilities.mapToRange(progress, 0f, 1f, 0f, getDeviceProfile().bottomSheetDepth,
                             EMPHASIZED));
@@ -729,6 +736,10 @@
         return mDepthController;
     }
 
+    public DesktopVisibilityController getDesktopVisibilityController() {
+        return mDesktopVisibilityController;
+    }
+
     @Nullable
     public UnfoldTransitionProgressProvider getUnfoldTransitionProgressProvider() {
         return mUnfoldTransitionProgressProvider;
@@ -758,8 +769,8 @@
         QuickstepTransitionManager appTransitionManager = getAppTransitionManager();
         appTransitionManager.setRemoteAnimationProvider(new RemoteAnimationProvider() {
             @Override
-            public AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] appTargets,
-                    RemoteAnimationTargetCompat[] wallpaperTargets) {
+            public AnimatorSet createWindowAnimation(RemoteAnimationTarget[] appTargets,
+                    RemoteAnimationTarget[] wallpaperTargets) {
 
                 // On the first call clear the reference.
                 signal.cancel();
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
index 4150d40..733c6a8 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
@@ -26,6 +26,7 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.allapps.AllAppsTransitionController;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.quickstep.util.LayoutUtils;
 import com.android.quickstep.views.RecentsView;
 
@@ -96,7 +97,7 @@
     @Override
     public int getWorkspaceScrimColor(Launcher launcher) {
         DeviceProfile dp = launcher.getDeviceProfile();
-        if (dp.isTaskbarPresentInApps) {
+        if (dp.isTaskbarPresentInApps && !FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get()) {
             return launcher.getColor(R.color.taskbar_background);
         }
         return Color.TRANSPARENT;
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 07ddcc8..d4b713b 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -15,6 +15,7 @@
  */
 package com.android.quickstep;
 
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.view.Surface.ROTATION_0;
 import static android.view.Surface.ROTATION_270;
 import static android.view.Surface.ROTATION_90;
@@ -52,7 +53,6 @@
 import static com.android.quickstep.util.VibratorWrapper.OVERVIEW_HAPTIC;
 import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -73,6 +73,7 @@
 import android.os.SystemClock;
 import android.util.Log;
 import android.view.MotionEvent;
+import android.view.RemoteAnimationTarget;
 import android.view.View;
 import android.view.View.OnApplyWindowInsetsListener;
 import android.view.ViewGroup;
@@ -116,6 +117,7 @@
 import com.android.quickstep.util.RecentsOrientedState;
 import com.android.quickstep.util.RectFSpringAnim;
 import com.android.quickstep.util.StaggeredWorkspaceAnim;
+import com.android.quickstep.util.SurfaceTransaction;
 import com.android.quickstep.util.SurfaceTransactionApplier;
 import com.android.quickstep.util.SwipePipToHomeAnimator;
 import com.android.quickstep.util.TaskViewSimulator;
@@ -127,9 +129,6 @@
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.InputConsumerController;
 import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
 import com.android.systemui.shared.system.TaskStackChangeListener;
 import com.android.systemui.shared.system.TaskStackChangeListeners;
 import com.android.wm.shell.common.TransactionPool;
@@ -684,7 +683,7 @@
         if (!mDeviceState.isFullyGesturalNavMode() || mRecentsView == null) {
             return;
         }
-        RemoteAnimationTargetCompat runningTaskTarget = mRecentsAnimationTargets != null
+        RemoteAnimationTarget runningTaskTarget = mRecentsAnimationTargets != null
                 ? mRecentsAnimationTargets.findTask(mGestureState.getRunningTaskId())
                 : null;
         final boolean recentsAttachedToAppWindow;
@@ -868,7 +867,7 @@
         // Only initialize the device profile, if it has not been initialized before, as in some
         // configurations targets.homeContentInsets may not be correct.
         if (mActivity == null) {
-            RemoteAnimationTargetCompat primaryTaskTarget = targets.apps[0];
+            RemoteAnimationTarget primaryTaskTarget = targets.apps[0];
             // orientation state is independent of which remote target handle we use since both
             // should be pointing to the same one. Just choose index 0 for now since that works for
             // both split and non-split
@@ -1077,7 +1076,7 @@
     }
 
     /** @return Whether this was the task we were waiting to appear, and thus handled it. */
-    protected boolean handleTaskAppeared(RemoteAnimationTargetCompat[] appearedTaskTarget) {
+    protected boolean handleTaskAppeared(RemoteAnimationTarget[] appearedTaskTarget) {
         if (mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)) {
             return false;
         }
@@ -1316,7 +1315,7 @@
 
     protected abstract HomeAnimationFactory createHomeAnimationFactory(
             ArrayList<IBinder> launchCookies, long duration, boolean isTargetTranslucent,
-            boolean appCanEnterPip, RemoteAnimationTargetCompat runningTaskTarget);
+            boolean appCanEnterPip, RemoteAnimationTarget runningTaskTarget);
 
     private final TaskStackChangeListener mActivityRestartListener = new TaskStackChangeListener() {
         @Override
@@ -1363,7 +1362,7 @@
 
         if (mGestureState.getEndTarget() == HOME) {
             getOrientationHandler().adjustFloatingIconStartVelocity(velocityPxPerMs);
-            final RemoteAnimationTargetCompat runningTaskTarget = mRecentsAnimationTargets != null
+            final RemoteAnimationTarget runningTaskTarget = mRecentsAnimationTargets != null
                     ? mRecentsAnimationTargets.findTask(mGestureState.getRunningTaskId())
                     : null;
             final ArrayList<IBinder> cookies = runningTaskTarget != null
@@ -1473,7 +1472,7 @@
         }
     }
 
-    private int calculateWindowRotation(RemoteAnimationTargetCompat runningTaskTarget,
+    private int calculateWindowRotation(RemoteAnimationTarget runningTaskTarget,
             RecentsOrientedState orientationState) {
         if (runningTaskTarget.rotationChange != 0
                 && TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) {
@@ -1486,7 +1485,7 @@
 
     @Nullable
     private SwipePipToHomeAnimator createWindowAnimationToPip(HomeAnimationFactory homeAnimFactory,
-            RemoteAnimationTargetCompat runningTaskTarget, float startProgress) {
+            RemoteAnimationTarget runningTaskTarget, float startProgress) {
         // Directly animate the app to PiP (picture-in-picture) mode
         final ActivityManager.RunningTaskInfo taskInfo = runningTaskTarget.taskInfo;
         final RecentsOrientedState orientationState = mRemoteTargetHandles[0].getTaskViewSimulator()
@@ -1835,6 +1834,17 @@
                         if (mRecentsAnimationController == null) return;
                         final ThumbnailData taskSnapshot =
                                 mRecentsAnimationController.screenshotTask(runningTaskId);
+                        // If split case, we should update all split tasks snapshot
+                        if (mIsSwipeForSplit) {
+                            int[] splitTaskIds = TopTaskTracker.INSTANCE.get(
+                                    mContext).getRunningSplitTaskIds();
+                            for (int i = 0; i < splitTaskIds.length; i++) {
+                                // Skip running one because done above.
+                                if (splitTaskIds[i] == runningTaskId) continue;
+
+                                mRecentsAnimationController.screenshotTask(splitTaskIds[i]);
+                            }
+                        }
                         MAIN_EXECUTOR.execute(() -> {
                             mTaskSnapshot = taskSnapshot;
                             if (!updateThumbnail(runningTaskId, false /* refreshView */)) {
@@ -1882,10 +1892,16 @@
     }
 
     private void finishCurrentTransitionToRecents() {
-        // TODO(b/245569277#comment2): enable once isFreeformActive is implemented
-        mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED);
-        if (mRecentsAnimationController != null) {
-            mRecentsAnimationController.detachNavigationBarFromApp(true);
+        if (mRecentsAnimationController != null
+                && mActivityInterface.getDesktopVisibilityController() != null
+                && mActivityInterface.getDesktopVisibilityController().areFreeformTasksVisible()) {
+            mRecentsAnimationController.finish(true /* toRecents */,
+                    () -> mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED));
+        } else {
+            mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED);
+            if (mRecentsAnimationController != null) {
+                mRecentsAnimationController.detachNavigationBarFromApp(true);
+            }
         }
         ActiveGestureLog.INSTANCE.addLog(
                 /* event= */ "finishRecentsAnimation",
@@ -1960,9 +1976,9 @@
         reset();
     }
 
-    private static boolean isNotInRecents(RemoteAnimationTargetCompat app) {
+    private static boolean isNotInRecents(RemoteAnimationTarget app) {
         return app.isNotInRecents
-                || app.activityType == ACTIVITY_TYPE_HOME;
+                || app.windowConfiguration.getActivityType() == ACTIVITY_TYPE_HOME;
     }
 
     /**
@@ -2085,10 +2101,10 @@
     }
 
     @Override
-    public void onTasksAppeared(RemoteAnimationTargetCompat[] appearedTaskTargets) {
+    public void onTasksAppeared(RemoteAnimationTarget[] appearedTaskTargets) {
         if (mRecentsAnimationController != null) {
             if (handleTaskAppeared(appearedTaskTargets)) {
-                Optional<RemoteAnimationTargetCompat> taskTargetOptional =
+                Optional<RemoteAnimationTarget> taskTargetOptional =
                         Arrays.stream(appearedTaskTargets)
                                 .filter(targetCompat ->
                                         targetCompat.taskId == mGestureState.getLastStartedTaskId())
@@ -2097,7 +2113,7 @@
                     finishRecentsAnimationOnTasksAppeared();
                     return;
                 }
-                RemoteAnimationTargetCompat taskTarget = taskTargetOptional.get();
+                RemoteAnimationTarget taskTarget = taskTargetOptional.get();
                 TaskView taskView = mRecentsView.getTaskViewByTaskId(taskTarget.taskId);
                 if (taskView == null || !taskView.getThumbnail().shouldShowSplashView()) {
                     finishRecentsAnimationOnTasksAppeared();
@@ -2108,16 +2124,13 @@
 
                 // When revealing the app with launcher splash screen, make the app visible
                 // and behind the splash view before the splash is animated away.
-                SyncRtSurfaceTransactionApplierCompat surfaceApplier =
-                        new SyncRtSurfaceTransactionApplierCompat(splashView);
-                ArrayList<SurfaceParams> params = new ArrayList<>();
-                for (RemoteAnimationTargetCompat target : appearedTaskTargets) {
-                    SurfaceParams.Builder builder = new SurfaceParams.Builder(target.leash);
-                    builder.withAlpha(1);
-                    builder.withLayer(-1);
-                    params.add(builder.build());
+                SurfaceTransactionApplier surfaceApplier =
+                        new SurfaceTransactionApplier(splashView);
+                SurfaceTransaction transaction = new SurfaceTransaction();
+                for (RemoteAnimationTarget target : appearedTaskTargets) {
+                    transaction.forSurface(target.leash).setAlpha(1).setLayer(-1);
                 }
-                surfaceApplier.scheduleApply(params.toArray(new SurfaceParams[0]));
+                surfaceApplier.scheduleApply(transaction);
 
                 SplashScreenExitAnimationUtils.startAnimations(splashView, taskTarget.leash,
                         mSplashMainWindowShiftLength, new TransactionPool(), new Rect(),
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index a343c2d..de150e1 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -40,6 +40,7 @@
 import android.os.Build;
 import android.view.Gravity;
 import android.view.MotionEvent;
+import android.view.RemoteAnimationTarget;
 import android.view.View;
 
 import androidx.annotation.Nullable;
@@ -50,6 +51,7 @@
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.statehandlers.DepthController;
+import com.android.launcher3.statehandlers.DesktopVisibilityController;
 import com.android.launcher3.statemanager.BaseState;
 import com.android.launcher3.statemanager.StatefulActivity;
 import com.android.launcher3.taskbar.TaskbarUIController;
@@ -62,7 +64,6 @@
 import com.android.quickstep.util.AnimatorControllerWithResistance;
 import com.android.quickstep.views.RecentsView;
 import com.android.systemui.shared.recents.model.ThumbnailData;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
 import java.util.HashMap;
 import java.util.Optional;
@@ -142,6 +143,11 @@
     }
 
     @Nullable
+    public DesktopVisibilityController getDesktopVisibilityController() {
+        return null;
+    }
+
+    @Nullable
     public abstract TaskbarUIController getTaskbarController();
 
     public final boolean isResumed() {
@@ -162,7 +168,7 @@
     public abstract boolean switchToRecentsIfVisible(Runnable onCompleteCallback);
 
     public abstract Rect getOverviewWindowBounds(
-            Rect homeBounds, RemoteAnimationTargetCompat target);
+            Rect homeBounds, RemoteAnimationTarget target);
 
     public abstract boolean allowMinimizeSplitScreen();
 
@@ -188,7 +194,8 @@
      * Closes any overlays.
      */
     public void closeOverlay() {
-        Optional.ofNullable(getTaskbarController()).ifPresent(TaskbarUIController::hideAllApps);
+        Optional.ofNullable(getTaskbarController()).ifPresent(
+                TaskbarUIController::hideOverlayWindow);
     }
 
     public void switchRunningTaskViewToScreenshot(HashMap<Integer, ThumbnailData> thumbnailDatas,
diff --git a/quickstep/src/com/android/quickstep/FallbackActivityInterface.java b/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
index 6e963f3..c62220f 100644
--- a/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
@@ -25,6 +25,7 @@
 import android.content.Context;
 import android.graphics.Rect;
 import android.view.MotionEvent;
+import android.view.RemoteAnimationTarget;
 
 import androidx.annotation.Nullable;
 
@@ -38,7 +39,6 @@
 import com.android.quickstep.util.ActivityInitListener;
 import com.android.quickstep.util.AnimatorControllerWithResistance;
 import com.android.quickstep.views.RecentsView;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
 import java.util.function.Consumer;
 import java.util.function.Predicate;
@@ -132,7 +132,7 @@
     }
 
     @Override
-    public Rect getOverviewWindowBounds(Rect homeBounds, RemoteAnimationTargetCompat target) {
+    public Rect getOverviewWindowBounds(Rect homeBounds, RemoteAnimationTarget target) {
         // TODO: Remove this once b/77875376 is fixed
         return target.screenSpaceBounds;
     }
diff --git a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
index 99f7bdd..374b839 100644
--- a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -15,6 +15,7 @@
  */
 package com.android.quickstep;
 
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.content.Intent.EXTRA_COMPONENT_NAME;
 import static android.content.Intent.EXTRA_USER;
 
@@ -26,7 +27,6 @@
 import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
 import static com.android.launcher3.anim.Interpolators.ACCEL;
 import static com.android.quickstep.OverviewComponentObserver.startHomeIntentSafely;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
 
 import android.animation.ObjectAnimator;
 import android.annotation.TargetApi;
@@ -48,6 +48,7 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.util.Log;
+import android.view.RemoteAnimationTarget;
 import android.view.Surface;
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
@@ -65,12 +66,11 @@
 import com.android.quickstep.fallback.FallbackRecentsView;
 import com.android.quickstep.fallback.RecentsState;
 import com.android.quickstep.util.RectFSpringAnim;
+import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties;
 import com.android.quickstep.util.TransformParams;
 import com.android.quickstep.util.TransformParams.BuilderProxy;
 import com.android.systemui.shared.recents.model.Task.TaskKey;
 import com.android.systemui.shared.system.InputConsumerController;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
 
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
@@ -125,24 +125,24 @@
         }
     }
 
-    private void updateHomeActivityTransformDuringSwipeUp(SurfaceParams.Builder builder,
-            RemoteAnimationTargetCompat app, TransformParams params) {
+    private void updateHomeActivityTransformDuringSwipeUp(SurfaceProperties builder,
+            RemoteAnimationTarget app, TransformParams params) {
         setHomeScaleAndAlpha(builder, app, mCurrentShift.value,
                 Utilities.boundToRange(1 - mCurrentShift.value, 0, 1));
     }
 
-    private void setHomeScaleAndAlpha(SurfaceParams.Builder builder,
-            RemoteAnimationTargetCompat app, float verticalShift, float alpha) {
+    private void setHomeScaleAndAlpha(SurfaceProperties builder,
+            RemoteAnimationTarget app, float verticalShift, float alpha) {
         float scale = Utilities.mapRange(verticalShift, 1, mMaxLauncherScale);
         mTmpMatrix.setScale(scale, scale,
                 app.localBounds.exactCenterX(), app.localBounds.exactCenterY());
-        builder.withMatrix(mTmpMatrix).withAlpha(alpha);
+        builder.setMatrix(mTmpMatrix).setAlpha(alpha);
     }
 
     @Override
     protected HomeAnimationFactory createHomeAnimationFactory(ArrayList<IBinder> launchCookies,
             long duration, boolean isTargetTranslucent, boolean appCanEnterPip,
-            RemoteAnimationTargetCompat runningTaskTarget) {
+            RemoteAnimationTarget runningTaskTarget) {
         mAppCanEnterPip = appCanEnterPip;
         if (appCanEnterPip) {
             return new FallbackPipToHomeAnimationFactory();
@@ -154,7 +154,7 @@
 
     private void startHomeIntent(
             @Nullable FallbackHomeAnimationFactory gestureContractAnimationFactory,
-            @Nullable RemoteAnimationTargetCompat runningTaskTarget) {
+            @Nullable RemoteAnimationTarget runningTaskTarget) {
         ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext, 0, 0);
         Intent intent = new Intent(mGestureState.getHomeIntent());
         if (gestureContractAnimationFactory != null && runningTaskTarget != null) {
@@ -164,7 +164,7 @@
     }
 
     @Override
-    protected boolean handleTaskAppeared(RemoteAnimationTargetCompat[] appearedTaskTarget) {
+    protected boolean handleTaskAppeared(RemoteAnimationTarget[] appearedTaskTarget) {
         if (mActiveAnimationFactory != null
                 && mActiveAnimationFactory.handleHomeTaskAppeared(appearedTaskTarget)) {
             mActiveAnimationFactory = null;
@@ -279,13 +279,13 @@
             return mTargetRect;
         }
 
-        private void updateRecentsActivityTransformDuringHomeAnim(SurfaceParams.Builder builder,
-                RemoteAnimationTargetCompat app, TransformParams params) {
-            builder.withAlpha(mRecentsAlpha.value);
+        private void updateRecentsActivityTransformDuringHomeAnim(SurfaceProperties builder,
+                RemoteAnimationTarget app, TransformParams params) {
+            builder.setAlpha(mRecentsAlpha.value);
         }
 
-        private void updateHomeActivityTransformDuringHomeAnim(SurfaceParams.Builder builder,
-                RemoteAnimationTargetCompat app, TransformParams params) {
+        private void updateHomeActivityTransformDuringHomeAnim(SurfaceProperties builder,
+                RemoteAnimationTarget app, TransformParams params) {
             setHomeScaleAndAlpha(builder, app, mVerticalShiftForScale.value, mHomeAlpha.value);
         }
 
@@ -304,12 +304,12 @@
             }
         }
 
-        public boolean handleHomeTaskAppeared(RemoteAnimationTargetCompat[] appearedTaskTargets) {
-            RemoteAnimationTargetCompat appearedTaskTarget = appearedTaskTargets[0];
-            if (appearedTaskTarget.activityType == ACTIVITY_TYPE_HOME) {
+        public boolean handleHomeTaskAppeared(RemoteAnimationTarget[] appearedTaskTargets) {
+            RemoteAnimationTarget appearedTaskTarget = appearedTaskTargets[0];
+            if (appearedTaskTarget.windowConfiguration.getActivityType() == ACTIVITY_TYPE_HOME) {
                 RemoteAnimationTargets targets = new RemoteAnimationTargets(
-                        new RemoteAnimationTargetCompat[] {appearedTaskTarget},
-                        new RemoteAnimationTargetCompat[0], new RemoteAnimationTargetCompat[0],
+                        new RemoteAnimationTarget[] {appearedTaskTarget},
+                        new RemoteAnimationTarget[0], new RemoteAnimationTarget[0],
                         appearedTaskTarget.mode);
                 mHomeAlphaParams.setTargetSet(targets);
                 updateHomeAlpha();
diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
index bcd9687..3c4ee75 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -28,6 +28,7 @@
 import android.annotation.TargetApi;
 import android.content.Intent;
 import android.os.Build;
+import android.view.RemoteAnimationTarget;
 
 import com.android.launcher3.statemanager.BaseState;
 import com.android.launcher3.statemanager.StatefulActivity;
@@ -37,7 +38,6 @@
 import com.android.quickstep.util.ActiveGestureErrorDetector;
 import com.android.quickstep.util.ActiveGestureLog;
 import com.android.systemui.shared.recents.model.ThumbnailData;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -143,7 +143,7 @@
 
     private CachedTaskInfo mRunningTask;
     private GestureEndTarget mEndTarget;
-    private RemoteAnimationTargetCompat mLastAppearedTaskTarget;
+    private RemoteAnimationTarget mLastAppearedTaskTarget;
     private Set<Integer> mPreviouslyAppearedTaskIds = new HashSet<>();
     private int mLastStartedTaskId = -1;
     private RecentsAnimationController mRecentsAnimationController;
@@ -272,7 +272,7 @@
     /**
      * Updates the last task that appeared during this gesture.
      */
-    public void updateLastAppearedTaskTarget(RemoteAnimationTargetCompat lastAppearedTaskTarget) {
+    public void updateLastAppearedTaskTarget(RemoteAnimationTarget lastAppearedTaskTarget) {
         mLastAppearedTaskTarget = lastAppearedTaskTarget;
         if (lastAppearedTaskTarget != null) {
             mPreviouslyAppearedTaskIds.add(lastAppearedTaskTarget.taskId);
diff --git a/quickstep/src/com/android/quickstep/KtR.java b/quickstep/src/com/android/quickstep/KtR.java
deleted file mode 100644
index 758c6e0..0000000
--- a/quickstep/src/com/android/quickstep/KtR.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.quickstep;
-
-import com.android.launcher3.R;
-
-/**
- * Bridge class to allow using resources in Kotlin.
- * <br/>
- * TODO(b/204069723) Can't use resources directly in Kotlin
- */
-public class KtR {
-    public static final class id {
-        public static int menu_option_layout = R.id.menu_option_layout;
-    }
-
-    public static final class dimen {
-        public static int task_menu_spacing = R.dimen.task_menu_spacing;
-        public static int task_menu_horizontal_padding = R.dimen.task_menu_horizontal_padding;
-        public static int taskbar_ime_size = R.dimen.taskbar_ime_size;
-    }
-
-    public static final class layout {
-        public static int task_menu_with_arrow = R.layout.task_menu_with_arrow;
-        public static int task_view_menu_option = R.layout.task_view_menu_option;
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
index 1cb17cb..0a7d226 100644
--- a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
@@ -28,17 +28,19 @@
 import android.content.Context;
 import android.graphics.Rect;
 import android.view.MotionEvent;
+import android.view.RemoteAnimationTarget;
 
 import androidx.annotation.Nullable;
 import androidx.annotation.UiThread;
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAnimUtils;
 import com.android.launcher3.LauncherInitListener;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.statehandlers.DepthController;
-import com.android.launcher3.statehandlers.DepthController.ClampedDepthProperty;
+import com.android.launcher3.statehandlers.DesktopVisibilityController;
 import com.android.launcher3.statemanager.StateManager;
 import com.android.launcher3.taskbar.LauncherTaskbarUIController;
 import com.android.launcher3.touch.PagedOrientationHandler;
@@ -51,7 +53,6 @@
 import com.android.quickstep.util.LayoutUtils;
 import com.android.quickstep.views.RecentsView;
 import com.android.systemui.plugins.shared.LauncherOverlayManager;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
 import java.util.function.Consumer;
 import java.util.function.Predicate;
@@ -129,7 +130,8 @@
                 float fromDepthRatio = BACKGROUND_APP.getDepth(activity);
                 float toDepthRatio = OVERVIEW.getDepth(activity);
                 pa.addFloat(getDepthController(),
-                        new ClampedDepthProperty(fromDepthRatio, toDepthRatio),
+                        new LauncherAnimUtils.ClampedProperty<>(
+                                DepthController.STATE_DEPTH, fromDepthRatio, toDepthRatio),
                         fromDepthRatio, toDepthRatio, LINEAR);
             }
         };
@@ -174,6 +176,16 @@
 
     @Nullable
     @Override
+    public DesktopVisibilityController getDesktopVisibilityController() {
+        QuickstepLauncher launcher = getCreatedActivity();
+        if (launcher == null) {
+            return null;
+        }
+        return launcher.getDesktopVisibilityController();
+    }
+
+    @Nullable
+    @Override
     public LauncherTaskbarUIController getTaskbarController() {
         QuickstepLauncher launcher = getCreatedActivity();
         if (launcher == null) {
@@ -251,7 +263,7 @@
     }
 
     @Override
-    public Rect getOverviewWindowBounds(Rect homeBounds, RemoteAnimationTargetCompat target) {
+    public Rect getOverviewWindowBounds(Rect homeBounds, RemoteAnimationTarget target) {
         return homeBounds;
     }
 
@@ -289,10 +301,6 @@
         } else {
             om.hideOverlay(150);
         }
-        LauncherTaskbarUIController taskbarController = getTaskbarController();
-        if (taskbarController != null) {
-            taskbarController.hideEdu();
-        }
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
index 7a281dd..2741751 100644
--- a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
+++ b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
@@ -36,6 +36,7 @@
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 import android.window.BackEvent;
+import android.window.BackProgressAnimator;
 import android.window.IOnBackInvokedCallback;
 
 import com.android.launcher3.AbstractFloatingView;
@@ -45,8 +46,6 @@
 import com.android.launcher3.uioverrides.QuickstepLauncher;
 import com.android.quickstep.util.RectFSpringAnim;
 import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
 
 /**
  * Controls the animation of swiping back and returning to launcher.
@@ -84,13 +83,14 @@
     private final Interpolator mCancelInterpolator;
     private final PointF mInitialTouchPos = new PointF();
 
-    private RemoteAnimationTargetCompat mBackTarget;
+    private RemoteAnimationTarget mBackTarget;
     private SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
     private boolean mSpringAnimationInProgress = false;
     private boolean mAnimatorSetInProgress = false;
     private float mBackProgress = 0;
     private boolean mBackInProgress = false;
     private IOnBackInvokedCallback mBackCallback;
+    private BackProgressAnimator mProgressAnimator = new BackProgressAnimator();
 
     public LauncherBackAnimationController(
             QuickstepLauncher launcher,
@@ -119,30 +119,41 @@
         mBackCallback = new IOnBackInvokedCallback.Stub() {
             @Override
             public void onBackCancelled() {
-                handler.post(() -> resetPositionAnimated());
+                handler.post(() -> {
+                    resetPositionAnimated();
+                    mProgressAnimator.reset();
+                });
             }
 
             @Override
             public void onBackInvoked() {
-                handler.post(() -> startTransition());
+                handler.post(() -> {
+                    startTransition();
+                    mProgressAnimator.reset();
+                });
             }
 
             @Override
             public void onBackProgressed(BackEvent backEvent) {
-                mBackProgress = backEvent.getProgress();
-                // TODO: Update once the interpolation curve spec is finalized.
-                mBackProgress =
-                        1 - (1 - mBackProgress) * (1 - mBackProgress) * (1
-                                - mBackProgress);
-                if (!mBackInProgress) {
-                    startBack(backEvent);
-                } else {
-                    updateBackProgress(mBackProgress, backEvent);
-                }
+                handler.post(() -> {
+                    mProgressAnimator.onBackProgressed(backEvent);
+                });
             }
 
             @Override
-            public void onBackStarted() { }
+            public void onBackStarted(BackEvent backEvent) {
+                handler.post(() -> {
+                    startBack(backEvent);
+                    mProgressAnimator.onBackStarted(backEvent, event -> {
+                        mBackProgress = event.getProgress();
+                        // TODO: Update once the interpolation curve spec is finalized.
+                        mBackProgress =
+                                1 - (1 - mBackProgress) * (1 - mBackProgress) * (1
+                                        - mBackProgress);
+                        updateBackProgress(mBackProgress, event);
+                    });
+                });
+            }
         };
         SystemUiProxy.INSTANCE.get(mLauncher).setBackToLauncherCallback(mBackCallback);
     }
@@ -170,6 +181,7 @@
         if (mBackCallback != null) {
             SystemUiProxy.INSTANCE.get(mLauncher).clearBackToLauncherCallback(mBackCallback);
         }
+        mProgressAnimator.reset();
         mBackCallback = null;
     }
 
@@ -183,33 +195,25 @@
 
         mTransaction.show(appTarget.leash).apply();
         mTransaction.setAnimationTransaction();
-        mBackTarget = new RemoteAnimationTargetCompat(appTarget);
+        mBackTarget = appTarget;
         mInitialTouchPos.set(backEvent.getTouchX(), backEvent.getTouchY());
 
         // TODO(b/218916755): Offset start rectangle in multiwindow mode.
         mStartRect.set(mBackTarget.windowConfiguration.getMaxBounds());
+        mCurrentRect.set(mStartRect);
     }
 
     private void updateBackProgress(float progress, BackEvent event) {
-        if (mBackTarget == null) {
+        if (!mBackInProgress || mBackTarget == null) {
             return;
         }
         float screenWidth = mStartRect.width();
         float screenHeight = mStartRect.height();
-        float dX = Math.abs(event.getTouchX() - mInitialTouchPos.x);
-        // The 'follow width' is the width of the window if it completely matches
-        // the gesture displacement.
-        float followWidth = screenWidth - dX;
-        // The 'progress width' is the width of the window if it strictly linearly interpolates
-        // to minimum scale base on progress.
-        float progressWidth = Utilities.mapRange(progress, 1, MIN_WINDOW_SCALE) * screenWidth;
-        // The final width is derived from interpolating between the follow with and progress width
-        // using gesture progress.
-        float width = Utilities.mapRange(progress, followWidth, progressWidth);
+        float width = Utilities.mapRange(progress, 1, MIN_WINDOW_SCALE) * screenWidth;
         float height = screenHeight / screenWidth * width;
         float deltaYRatio = (event.getTouchY() - mInitialTouchPos.y) / screenHeight;
         // Base the window movement in the Y axis on the touch movement in the Y axis.
-        float deltaY = (float) Math.sin(deltaYRatio * Math.PI * 0.5f) * mWindowMaxDeltaY;
+        float deltaY = (float) Math.sin(deltaYRatio * Math.PI * 0.5f) * mWindowMaxDeltaY * progress;
         // Move the window along the Y axis.
         float top = (screenHeight - height) * 0.5f + deltaY;
         // Move the window along the X axis.
@@ -242,20 +246,17 @@
 
     /** Transform the target window to match the target rect. */
     private void applyTransform(RectF targetRect, float cornerRadius) {
-        SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder builder =
-                new SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder(mBackTarget.leash);
         final float scale = targetRect.width() / mStartRect.width();
         mTransformMatrix.reset();
         mTransformMatrix.setScale(scale, scale);
         mTransformMatrix.postTranslate(targetRect.left, targetRect.top);
-        builder.withMatrix(mTransformMatrix)
-                .withWindowCrop(mStartRect)
-                .withCornerRadius(cornerRadius);
-        SyncRtSurfaceTransactionApplierCompat.SurfaceParams surfaceParams = builder.build();
 
-        if (surfaceParams.surface.isValid()) {
-            surfaceParams.applyTo(mTransaction);
+        if (mBackTarget.leash.isValid()) {
+            mTransaction.setMatrix(mBackTarget.leash, mTransformMatrix, new float[9]);
+            mTransaction.setWindowCrop(mBackTarget.leash, mStartRect);
+            mTransaction.setCornerRadius(mBackTarget.leash, cornerRadius);
         }
+
         mTransaction.apply();
     }
 
@@ -284,8 +285,8 @@
                 mBackProgress, mWindowScaleStartCornerRadius, mWindowScaleEndCornerRadius);
         Pair<RectFSpringAnim, AnimatorSet> pair =
                 mQuickstepTransitionManager.createWallpaperOpenAnimations(
-                    new RemoteAnimationTargetCompat[]{mBackTarget},
-                    new RemoteAnimationTargetCompat[]{},
+                    new RemoteAnimationTarget[]{mBackTarget},
+                    new RemoteAnimationTarget[0],
                     false /* fromUnlock */,
                     mCurrentRect,
                     cornerRadius);
diff --git a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
index d1533f0..bb781c8 100644
--- a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
+++ b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
@@ -30,6 +30,7 @@
 import android.os.IBinder;
 import android.os.UserHandle;
 import android.util.Size;
+import android.view.RemoteAnimationTarget;
 import android.view.View;
 
 import androidx.annotation.NonNull;
@@ -49,7 +50,6 @@
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.system.InputConsumerController;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
 import java.util.ArrayList;
 
@@ -70,7 +70,7 @@
     @Override
     protected HomeAnimationFactory createHomeAnimationFactory(ArrayList<IBinder> launchCookies,
             long duration, boolean isTargetTranslucent, boolean appCanEnterPip,
-            RemoteAnimationTargetCompat runningTaskTarget) {
+            RemoteAnimationTarget runningTaskTarget) {
         if (mActivity == null) {
             mStateCallback.addChangeListener(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED,
                     isPresent -> mRecentsView.startHome());
@@ -144,7 +144,7 @@
 
     private HomeAnimationFactory createWidgetHomeAnimationFactory(
             LauncherAppWidgetHostView hostView, boolean isTargetTranslucent,
-            RemoteAnimationTargetCompat runningTaskTarget) {
+            RemoteAnimationTarget runningTaskTarget) {
         final float floatingWidgetAlpha = isTargetTranslucent ? 0 : 1;
         RectF backgroundLocation = new RectF();
         Rect crop = new Rect();
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 4f5e216..97ce30f 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -15,6 +15,9 @@
  */
 package com.android.quickstep;
 
+import static android.view.RemoteAnimationTarget.MODE_CLOSING;
+import static android.view.RemoteAnimationTarget.MODE_OPENING;
+
 import static com.android.launcher3.QuickstepTransitionManager.RECENTS_LAUNCH_DURATION;
 import static com.android.launcher3.QuickstepTransitionManager.STATUS_BAR_TRANSITION_DURATION;
 import static com.android.launcher3.QuickstepTransitionManager.STATUS_BAR_TRANSITION_PRE_DELAY;
@@ -23,8 +26,6 @@
 import static com.android.quickstep.OverviewComponentObserver.startHomeIntentSafely;
 import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
 import static com.android.quickstep.TaskViewUtils.createRecentsWindowAnimator;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -36,6 +37,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.view.Display;
+import android.view.RemoteAnimationTarget;
 import android.view.SurfaceControl.Transaction;
 import android.view.View;
 import android.window.SplashScreen;
@@ -78,7 +80,6 @@
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -238,9 +239,9 @@
 
         mActivityLaunchAnimationRunner = new RemoteAnimationFactory() {
             @Override
-            public void onCreateAnimation(int transit, RemoteAnimationTargetCompat[] appTargets,
-                    RemoteAnimationTargetCompat[] wallpaperTargets,
-                    RemoteAnimationTargetCompat[] nonAppTargets, AnimationResult result) {
+            public void onCreateAnimation(int transit, RemoteAnimationTarget[] appTargets,
+                    RemoteAnimationTarget[] wallpaperTargets,
+                    RemoteAnimationTarget[] nonAppTargets, AnimationResult result) {
                 mHandler.removeCallbacks(mAnimationStartTimeoutRunnable);
                 AnimatorSet anim = composeRecentsLaunchAnimator(taskView, appTargets,
                         wallpaperTargets, nonAppTargets);
@@ -279,9 +280,9 @@
      * Composes the animations for a launch from the recents list if possible.
      */
     private AnimatorSet  composeRecentsLaunchAnimator(TaskView taskView,
-            RemoteAnimationTargetCompat[] appTargets,
-            RemoteAnimationTargetCompat[] wallpaperTargets,
-            RemoteAnimationTargetCompat[] nonAppTargets) {
+            RemoteAnimationTarget[] appTargets,
+            RemoteAnimationTarget[] wallpaperTargets,
+            RemoteAnimationTarget[] nonAppTargets) {
         AnimatorSet target = new AnimatorSet();
         boolean activityClosing = taskIsATargetWithMode(appTargets, getTaskId(), MODE_CLOSING);
         PendingAnimation pa = new PendingAnimation(RECENTS_LAUNCH_DURATION);
@@ -412,16 +413,16 @@
     private final RemoteAnimationFactory mAnimationToHomeFactory =
             new RemoteAnimationFactory() {
         @Override
-        public void onCreateAnimation(int transit, RemoteAnimationTargetCompat[] appTargets,
-                RemoteAnimationTargetCompat[] wallpaperTargets,
-                RemoteAnimationTargetCompat[] nonAppTargets, AnimationResult result) {
+        public void onCreateAnimation(int transit, RemoteAnimationTarget[] appTargets,
+                RemoteAnimationTarget[] wallpaperTargets,
+                RemoteAnimationTarget[] nonAppTargets, AnimationResult result) {
             AnimatorPlaybackController controller = getStateManager()
                     .createAnimationToNewWorkspace(RecentsState.BG_LAUNCHER, HOME_APPEAR_DURATION);
             controller.dispatchOnStart();
 
             RemoteAnimationTargets targets = new RemoteAnimationTargets(
                     appTargets, wallpaperTargets, nonAppTargets, MODE_OPENING);
-            for (RemoteAnimationTargetCompat app : targets.apps) {
+            for (RemoteAnimationTarget app : targets.apps) {
                 new Transaction().setAlpha(app.leash, 1).apply();
             }
             AnimatorSet anim = new AnimatorSet();
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
index b233521..2451ad8 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
@@ -33,9 +33,7 @@
 import com.android.quickstep.util.ActiveGestureLog;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
-import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Set;
 
@@ -88,17 +86,17 @@
     @BinderThread
     @Deprecated
     public final void onAnimationStart(RecentsAnimationControllerCompat controller,
-            RemoteAnimationTargetCompat[] appTargets, Rect homeContentInsets,
+            RemoteAnimationTarget[] appTargets, Rect homeContentInsets,
             Rect minimizedHomeBounds) {
-        onAnimationStart(controller, appTargets, new RemoteAnimationTargetCompat[0],
+        onAnimationStart(controller, appTargets, new RemoteAnimationTarget[0],
                 homeContentInsets, minimizedHomeBounds);
     }
 
     // Called only in R+ platform
     @BinderThread
     public final void onAnimationStart(RecentsAnimationControllerCompat animationController,
-            RemoteAnimationTargetCompat[] appTargets,
-            RemoteAnimationTargetCompat[] wallpaperTargets,
+            RemoteAnimationTarget[] appTargets,
+            RemoteAnimationTarget[] wallpaperTargets,
             Rect homeContentInsets, Rect minimizedHomeBounds) {
         mController = new RecentsAnimationController(animationController,
                 mAllowMinimizeSplitScreen, this::onAnimationFinished);
@@ -107,12 +105,13 @@
             Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(),
                     mController::finishAnimationToApp);
         } else {
-            final RemoteAnimationTarget[] nonAppTargets = mSystemUiProxy.onGoingToRecentsLegacy(
-                    Arrays.stream(appTargets).map(RemoteAnimationTargetCompat::unwrap)
-                            .toArray(RemoteAnimationTarget[]::new));
+            RemoteAnimationTarget[] nonAppTargets =
+                    mSystemUiProxy.onGoingToRecentsLegacy(appTargets);
+            if (nonAppTargets == null) {
+                nonAppTargets = new RemoteAnimationTarget[0];
+            }
             final RecentsAnimationTargets targets = new RecentsAnimationTargets(appTargets,
-                    wallpaperTargets, RemoteAnimationTargetCompat.wrap(nonAppTargets),
-                    homeContentInsets, minimizedHomeBounds);
+                    wallpaperTargets, nonAppTargets, homeContentInsets, minimizedHomeBounds);
 
             Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> {
                 ActiveGestureLog.INSTANCE.addLog(
@@ -141,7 +140,7 @@
 
     @BinderThread
     @Override
-    public void onTasksAppeared(RemoteAnimationTargetCompat[] apps) {
+    public void onTasksAppeared(RemoteAnimationTarget[] apps) {
         Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> {
             ActiveGestureLog.INSTANCE.addLog("onTasksAppeared",
                     ActiveGestureErrorDetector.GestureEvent.TASK_APPEARED);
@@ -197,7 +196,7 @@
         /**
          * Callback made when a task started from the recents is ready for an app transition.
          */
-        default void onTasksAppeared(@NonNull RemoteAnimationTargetCompat[] appearedTaskTarget) {}
+        default void onTasksAppeared(@NonNull RemoteAnimationTarget[] appearedTaskTarget) {}
 
         /**
          * @return whether this will call onFinished or not (onFinished should only be called once).
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
index 542c0d4..81e3782 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
@@ -23,6 +23,7 @@
 import android.os.RemoteException;
 import android.util.Log;
 import android.view.IRecentsAnimationController;
+import android.view.RemoteAnimationTarget;
 import android.view.SurfaceControl;
 import android.view.WindowManagerGlobal;
 import android.window.PictureInPictureSurfaceTransaction;
@@ -37,7 +38,6 @@
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
 import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
 import java.util.function.Consumer;
 
@@ -114,7 +114,7 @@
      * {@link RecentsAnimationCallbacks#onTasksAppeared}}.
      */
     @UiThread
-    public void removeTaskTarget(@NonNull RemoteAnimationTargetCompat target) {
+    public void removeTaskTarget(@NonNull RemoteAnimationTarget target) {
         UI_HELPER_EXECUTOR.execute(() -> mController.removeTask(target.taskId));
     }
 
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
index b6d9016..388e125 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
@@ -15,11 +15,10 @@
  */
 package com.android.quickstep;
 
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
+import static android.view.RemoteAnimationTarget.MODE_CLOSING;
 
 import android.graphics.Rect;
-
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import android.view.RemoteAnimationTarget;
 
 /**
  * Extension of {@link RemoteAnimationTargets} with additional information about swipe
@@ -30,8 +29,8 @@
     public final Rect homeContentInsets;
     public final Rect minimizedHomeBounds;
 
-    public RecentsAnimationTargets(RemoteAnimationTargetCompat[] apps,
-            RemoteAnimationTargetCompat[] wallpapers, RemoteAnimationTargetCompat[] nonApps,
+    public RecentsAnimationTargets(RemoteAnimationTarget[] apps,
+            RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
             Rect homeContentInsets, Rect minimizedHomeBounds) {
         super(apps, wallpapers, nonApps, MODE_CLOSING);
         this.homeContentInsets = homeContentInsets;
diff --git a/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java b/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java
index 1bd808d..80aaad0 100644
--- a/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java
+++ b/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java
@@ -15,9 +15,11 @@
  */
 package com.android.quickstep;
 
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
 
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import android.view.RemoteAnimationTarget;
 
 import java.util.ArrayList;
 import java.util.concurrent.CopyOnWriteArrayList;
@@ -29,41 +31,40 @@
 
     private final CopyOnWriteArrayList<ReleaseCheck> mReleaseChecks = new CopyOnWriteArrayList<>();
 
-    public final RemoteAnimationTargetCompat[] unfilteredApps;
-    public final RemoteAnimationTargetCompat[] apps;
-    public final RemoteAnimationTargetCompat[] wallpapers;
-    public final RemoteAnimationTargetCompat[] nonApps;
+    public final RemoteAnimationTarget[] unfilteredApps;
+    public final RemoteAnimationTarget[] apps;
+    public final RemoteAnimationTarget[] wallpapers;
+    public final RemoteAnimationTarget[] nonApps;
     public final int targetMode;
     public final boolean hasRecents;
 
     private boolean mReleased = false;
 
-    public RemoteAnimationTargets(RemoteAnimationTargetCompat[] apps,
-            RemoteAnimationTargetCompat[] wallpapers, RemoteAnimationTargetCompat[] nonApps,
+    public RemoteAnimationTargets(RemoteAnimationTarget[] apps,
+            RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
             int targetMode) {
-        ArrayList<RemoteAnimationTargetCompat> filteredApps = new ArrayList<>();
+        ArrayList<RemoteAnimationTarget> filteredApps = new ArrayList<>();
         boolean hasRecents = false;
         if (apps != null) {
-            for (RemoteAnimationTargetCompat target : apps) {
+            for (RemoteAnimationTarget target : apps) {
                 if (target.mode == targetMode) {
                     filteredApps.add(target);
                 }
 
-                hasRecents |= target.activityType ==
-                        RemoteAnimationTargetCompat.ACTIVITY_TYPE_RECENTS;
+                hasRecents |= target.windowConfiguration.getActivityType() == ACTIVITY_TYPE_RECENTS;
             }
         }
 
         this.unfilteredApps = apps;
-        this.apps = filteredApps.toArray(new RemoteAnimationTargetCompat[filteredApps.size()]);
+        this.apps = filteredApps.toArray(new RemoteAnimationTarget[filteredApps.size()]);
         this.wallpapers = wallpapers;
         this.targetMode = targetMode;
         this.hasRecents = hasRecents;
         this.nonApps = nonApps;
     }
 
-    public RemoteAnimationTargetCompat findTask(int taskId) {
-        for (RemoteAnimationTargetCompat target : apps) {
+    public RemoteAnimationTarget findTask(int taskId) {
+        for (RemoteAnimationTarget target : apps) {
             if (target.taskId == taskId) {
                 return target;
             }
@@ -74,12 +75,12 @@
     /**
      * Gets the navigation bar remote animation target if exists.
      */
-    public RemoteAnimationTargetCompat getNavBarRemoteAnimationTarget() {
+    public RemoteAnimationTarget getNavBarRemoteAnimationTarget() {
         return getNonAppTargetOfType(TYPE_NAVIGATION_BAR);
     }
 
-    public RemoteAnimationTargetCompat getNonAppTargetOfType(int type) {
-        for (RemoteAnimationTargetCompat target : nonApps) {
+    public RemoteAnimationTarget getNonAppTargetOfType(int type) {
+        for (RemoteAnimationTarget target : nonApps) {
             if (target.windowType == type) {
                 return target;
             }
@@ -88,19 +89,19 @@
     }
 
     /** Returns the first opening app target. */
-    public RemoteAnimationTargetCompat getFirstAppTarget() {
+    public RemoteAnimationTarget getFirstAppTarget() {
         return apps.length > 0 ? apps[0] : null;
     }
 
     /** Returns the task id of the first opening app target, or -1 if none is found. */
     public int getFirstAppTargetTaskId() {
-        RemoteAnimationTargetCompat target = getFirstAppTarget();
+        RemoteAnimationTarget target = getFirstAppTarget();
         return target == null ? -1 : target.taskId;
     }
 
     public boolean isAnimatingHome() {
-        for (RemoteAnimationTargetCompat target : unfilteredApps) {
-            if (target.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
+        for (RemoteAnimationTarget target : unfilteredApps) {
+            if (target.windowConfiguration.getActivityType() == ACTIVITY_TYPE_HOME) {
                 return true;
             }
         }
@@ -123,15 +124,19 @@
         }
         mReleaseChecks.clear();
         mReleased = true;
+        release(unfilteredApps);
+        release(wallpapers);
+        release(nonApps);
+    }
 
-        for (RemoteAnimationTargetCompat target : unfilteredApps) {
-            target.release();
-        }
-        for (RemoteAnimationTargetCompat target : wallpapers) {
-            target.release();
-        }
-        for (RemoteAnimationTargetCompat target : nonApps) {
-            target.release();
+    private static void release(RemoteAnimationTarget[] targets) {
+        for (RemoteAnimationTarget target : targets) {
+            if (target.leash != null) {
+                target.leash.release();
+            }
+            if (target.startLeash != null) {
+                target.startLeash.release();
+            }
         }
     }
 
diff --git a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
index 7183c49..4c41bef 100644
--- a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
+++ b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
@@ -17,6 +17,8 @@
 package com.android.quickstep;
 
 import android.content.Context;
+import android.graphics.Rect;
+import android.view.RemoteAnimationTarget;
 
 import androidx.annotation.Nullable;
 
@@ -24,7 +26,6 @@
 import com.android.quickstep.util.AnimatorControllerWithResistance;
 import com.android.quickstep.util.TaskViewSimulator;
 import com.android.quickstep.util.TransformParams;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
 import java.util.ArrayList;
 
@@ -75,7 +76,7 @@
      */
     public RemoteTargetHandle[] assignTargets(RemoteAnimationTargets targets) {
         for (int i = 0; i < mRemoteTargetHandles.length; i++) {
-            RemoteAnimationTargetCompat primaryTaskTarget = targets.apps[i];
+            RemoteAnimationTarget primaryTaskTarget = targets.apps[i];
             mRemoteTargetHandles[i].mTransformParams.setTargetSet(
                     createRemoteAnimationTargetsForTarget(targets, null));
             mRemoteTargetHandles[i].mTaskViewSimulator.setPreview(primaryTaskTarget, null);
@@ -100,8 +101,8 @@
      */
     public RemoteTargetHandle[] assignTargetsForSplitScreen(RemoteAnimationTargets targets,
             int[] splitIds) {
-        RemoteAnimationTargetCompat topLeftTarget; // only one set if single/fullscreen task
-        RemoteAnimationTargetCompat bottomRightTarget;
+        RemoteAnimationTarget topLeftTarget; // only one set if single/fullscreen task
+        RemoteAnimationTarget bottomRightTarget;
         if (mRemoteTargetHandles.length == 1) {
             // If we're not in split screen, the splitIds count doesn't really matter since we
             // should always hit this case.
@@ -119,8 +120,8 @@
             // remoteTargetHandle[0] denotes topLeft task, so we pass in the bottomRight to exclude,
             // vice versa
             mSplitBounds = new SplitBounds(
-                    topLeftTarget.startScreenSpaceBounds,
-                    bottomRightTarget.startScreenSpaceBounds, splitIds[0], splitIds[1]);
+                    getStartBounds(topLeftTarget),
+                    getStartBounds(bottomRightTarget), splitIds[0], splitIds[1]);
             mRemoteTargetHandles[0].mTransformParams.setTargetSet(
                     createRemoteAnimationTargetsForTarget(targets, bottomRightTarget));
             mRemoteTargetHandles[0].mTaskViewSimulator.setPreview(topLeftTarget,
@@ -134,6 +135,10 @@
         return mRemoteTargetHandles;
     }
 
+    private Rect getStartBounds(RemoteAnimationTarget target) {
+        return target.startBounds == null ? target.screenSpaceBounds : target.startBounds;
+    }
+
     /**
      * Ensures that we aren't excluding ancillary targets such as home/recents
      *
@@ -144,11 +149,10 @@
      */
     private RemoteAnimationTargets createRemoteAnimationTargetsForTarget(
             RemoteAnimationTargets targets,
-            RemoteAnimationTargetCompat targetToExclude) {
-        ArrayList<RemoteAnimationTargetCompat> targetsWithoutExcluded =
-                new ArrayList<RemoteAnimationTargetCompat>();
+            RemoteAnimationTarget targetToExclude) {
+        ArrayList<RemoteAnimationTarget> targetsWithoutExcluded = new ArrayList<>();
 
-        for (RemoteAnimationTargetCompat targetCompat : targets.unfilteredApps) {
+        for (RemoteAnimationTarget targetCompat : targets.unfilteredApps) {
             if (targetCompat == targetToExclude) {
                 continue;
             }
@@ -162,9 +166,8 @@
 
             targetsWithoutExcluded.add(targetCompat);
         }
-        final RemoteAnimationTargetCompat[] filteredApps =
-                targetsWithoutExcluded.toArray(
-                        new RemoteAnimationTargetCompat[targetsWithoutExcluded.size()]);
+        final RemoteAnimationTarget[] filteredApps = targetsWithoutExcluded.toArray(
+                new RemoteAnimationTarget[targetsWithoutExcluded.size()]);
         return new RemoteAnimationTargets(
                 filteredApps, targets.wallpapers, targets.nonApps, targets.targetMode);
     }
diff --git a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
index baeb514..f591a1c 100644
--- a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
+++ b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
@@ -24,6 +24,7 @@
 import android.graphics.Matrix.ScaleToFit;
 import android.graphics.Rect;
 import android.graphics.RectF;
+import android.view.RemoteAnimationTarget;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.UiThread;
@@ -37,11 +38,10 @@
 import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
 import com.android.quickstep.util.AnimatorControllerWithResistance;
 import com.android.quickstep.util.RectFSpringAnim;
+import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties;
 import com.android.quickstep.util.TaskViewSimulator;
 import com.android.quickstep.util.TransformParams;
 import com.android.quickstep.util.TransformParams.BuilderProxy;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder;
 
 import java.util.Arrays;
 import java.util.function.Consumer;
@@ -335,11 +335,11 @@
         }
 
         @Override
-        public void onBuildTargetParams(
-                Builder builder, RemoteAnimationTargetCompat app, TransformParams params) {
-            builder.withMatrix(mMatrix)
-                    .withWindowCrop(mCropRect)
-                    .withCornerRadius(params.getCornerRadius());
+        public void onBuildTargetParams(SurfaceProperties builder, RemoteAnimationTarget app,
+                TransformParams params) {
+            builder.setMatrix(mMatrix)
+                    .setWindowCrop(mCropRect)
+                    .setCornerRadius(params.getCornerRadius());
         }
 
         @Override
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index 129b88e..7705a25 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -671,6 +671,7 @@
      *
      * @return RemoteAnimationTargets of windows that need to animate but only exist in shell.
      */
+    @Nullable
     public RemoteAnimationTarget[] onGoingToRecentsLegacy(RemoteAnimationTarget[] apps) {
         if (mSplitScreen != null) {
             try {
@@ -682,6 +683,7 @@
         return null;
     }
 
+    @Nullable
     public RemoteAnimationTarget[] onStartingSplitLegacy(RemoteAnimationTarget[] apps) {
         if (mSplitScreen != null) {
             try {
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index 7f16565..30d445f 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -15,11 +15,12 @@
  */
 package com.android.quickstep;
 
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_INITIALIZED;
 import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_STARTED;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
 
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
@@ -38,12 +39,10 @@
 import com.android.quickstep.views.RecentsView;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 import com.android.systemui.shared.system.RemoteTransitionCompat;
 import com.android.systemui.shared.system.TaskStackChangeListener;
 import com.android.systemui.shared.system.TaskStackChangeListeners;
 
-import java.util.Arrays;
 import java.util.HashMap;
 
 public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAnimationListener {
@@ -57,7 +56,7 @@
     private RecentsAnimationTargets mTargets;
     // Temporary until we can hook into gesture state events
     private GestureState mLastGestureState;
-    private RemoteAnimationTargetCompat mLastAppearedTaskTarget;
+    private RemoteAnimationTarget mLastAppearedTaskTarget;
     private Runnable mLiveTileCleanUpHandler;
     private Context mCtx;
 
@@ -151,12 +150,12 @@
             }
 
             @Override
-            public void onTasksAppeared(RemoteAnimationTargetCompat[] appearedTaskTargets) {
-                RemoteAnimationTargetCompat appearedTaskTarget = appearedTaskTargets[0];
+            public void onTasksAppeared(RemoteAnimationTarget[] appearedTaskTargets) {
+                RemoteAnimationTarget appearedTaskTarget = appearedTaskTargets[0];
                 BaseActivityInterface activityInterface = mLastGestureState.getActivityInterface();
 
-                for (RemoteAnimationTargetCompat compat : appearedTaskTargets) {
-                    if (compat.activityType == ACTIVITY_TYPE_HOME
+                for (RemoteAnimationTarget compat : appearedTaskTargets) {
+                    if (compat.windowConfiguration.getActivityType() == ACTIVITY_TYPE_HOME
                             && activityInterface.getCreatedActivity() instanceof RecentsActivity) {
                         // When receive opening home activity while recents is running, enter home
                         // and dismiss recents.
@@ -165,11 +164,11 @@
                     }
                 }
 
-                RemoteAnimationTarget[] nonAppTargets = SystemUiProxy.INSTANCE.getNoCreate()
-                        .onStartingSplitLegacy(Arrays.stream(appearedTaskTargets)
-                                .map(RemoteAnimationTargetCompat::unwrap)
-                                .toArray(RemoteAnimationTarget[]::new));
-
+                RemoteAnimationTarget[] nonAppTargets = SystemUiProxy.INSTANCE.get(mCtx)
+                        .onStartingSplitLegacy(appearedTaskTargets);
+                if (nonAppTargets == null) {
+                    nonAppTargets = new RemoteAnimationTarget[0];
+                }
                 if (activityInterface.isInLiveTileMode()
                         && activityInterface.getCreatedActivity() != null) {
                     RecentsView recentsView =
@@ -177,13 +176,13 @@
                     if (recentsView != null) {
                         recentsView.launchSideTaskInLiveTileMode(appearedTaskTarget.taskId,
                                 appearedTaskTargets,
-                                new RemoteAnimationTargetCompat[0] /* wallpaper */,
-                                RemoteAnimationTargetCompat.wrap(nonAppTargets) /* nonApps */);
+                                new RemoteAnimationTarget[0] /* wallpaper */,
+                                nonAppTargets /* nonApps */);
                         return;
                     }
-                } else if (nonAppTargets != null && nonAppTargets.length > 0) {
+                } else if (nonAppTargets.length > 0) {
                     TaskViewUtils.createSplitAuxiliarySurfacesAnimator(
-                            RemoteAnimationTargetCompat.wrap(nonAppTargets) /* nonApps */,
+                            nonAppTargets /* nonApps */,
                             true /*shown*/, dividerAnimator -> {
                                 dividerAnimator.start();
                                 dividerAnimator.end();
diff --git a/quickstep/src/com/android/quickstep/TaskUtils.java b/quickstep/src/com/android/quickstep/TaskUtils.java
index d722778..67360c4 100644
--- a/quickstep/src/com/android/quickstep/TaskUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskUtils.java
@@ -26,6 +26,7 @@
 import android.content.pm.PackageManager;
 import android.os.UserHandle;
 import android.util.Log;
+import android.view.RemoteAnimationTarget;
 
 import androidx.annotation.Nullable;
 
@@ -34,7 +35,6 @@
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
 import java.util.List;
 
@@ -87,9 +87,9 @@
     }
 
 
-    public static boolean taskIsATargetWithMode(RemoteAnimationTargetCompat[] targets,
+    public static boolean taskIsATargetWithMode(RemoteAnimationTarget[] targets,
             int taskId, int mode) {
-        for (RemoteAnimationTargetCompat target : targets) {
+        for (RemoteAnimationTarget target : targets) {
             if (target.mode == mode && target.taskId == taskId) {
                 return true;
             }
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index df80e2f..7d6bbac 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -15,6 +15,8 @@
  */
 package com.android.quickstep;
 
+import static android.view.RemoteAnimationTarget.MODE_CLOSING;
+import static android.view.RemoteAnimationTarget.MODE_OPENING;
 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
 import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.view.WindowManager.TRANSIT_TO_FRONT;
@@ -35,8 +37,6 @@
 import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
 import static com.android.launcher3.anim.Interpolators.clampToProgress;
 import static com.android.launcher3.statehandlers.DepthController.STATE_DEPTH;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -51,6 +51,7 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.os.Build;
+import android.view.RemoteAnimationTarget;
 import android.view.SurfaceControl;
 import android.view.View;
 import android.window.TransitionInfo;
@@ -70,6 +71,8 @@
 import com.android.launcher3.util.DisplayController;
 import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
 import com.android.quickstep.util.MultiValueUpdateListener;
+import com.android.quickstep.util.SurfaceTransaction;
+import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties;
 import com.android.quickstep.util.SurfaceTransactionApplier;
 import com.android.quickstep.util.TaskViewSimulator;
 import com.android.quickstep.util.TransformParams;
@@ -80,7 +83,6 @@
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -102,7 +104,7 @@
      * opening remote target (which we don't get until onAnimationStart) will resolve to a TaskView.
      */
     public static TaskView findTaskViewToLaunch(
-            RecentsView recentsView, View v, RemoteAnimationTargetCompat[] targets) {
+            RecentsView recentsView, View v, RemoteAnimationTarget[] targets) {
         if (v instanceof TaskView) {
             TaskView taskView = (TaskView) v;
             return recentsView.isTaskViewVisible(taskView) ? taskView : null;
@@ -132,7 +134,7 @@
         }
         // Resolve the opening task id
         int openingTaskId = -1;
-        for (RemoteAnimationTargetCompat target : targets) {
+        for (RemoteAnimationTarget target : targets) {
             if (target.mode == MODE_OPENING) {
                 openingTaskId = target.taskId;
                 break;
@@ -155,9 +157,9 @@
 
     public static void createRecentsWindowAnimator(
             @NonNull TaskView v, boolean skipViewChanges,
-            @NonNull RemoteAnimationTargetCompat[] appTargets,
-            @NonNull RemoteAnimationTargetCompat[] wallpaperTargets,
-            @NonNull RemoteAnimationTargetCompat[] nonAppTargets,
+            @NonNull RemoteAnimationTarget[] appTargets,
+            @NonNull RemoteAnimationTarget[] wallpaperTargets,
+            @NonNull RemoteAnimationTarget[] nonAppTargets,
             @Nullable DepthController depthController,
             PendingAnimation out) {
         RecentsView recentsView = v.getRecentsView();
@@ -167,7 +169,7 @@
         final RemoteAnimationTargets targets =
                 new RemoteAnimationTargets(appTargets, wallpaperTargets, nonAppTargets,
                         MODE_OPENING);
-        final RemoteAnimationTargetCompat navBarTarget = targets.getNavBarRemoteAnimationTarget();
+        final RemoteAnimationTarget navBarTarget = targets.getNavBarRemoteAnimationTarget();
 
         SurfaceTransactionApplier applier = new SurfaceTransactionApplier(v);
         targets.addReleaseCheck(applier);
@@ -250,21 +252,24 @@
 
                     @Override
                     public void onUpdate(float percent, boolean initOnly) {
-                        final SurfaceParams.Builder navBuilder =
-                                new SurfaceParams.Builder(navBarTarget.leash);
+
 
                         // TODO Do we need to operate over multiple TVSs for the navbar leash?
                         for (RemoteTargetHandle handle : remoteTargetHandles) {
+                            SurfaceTransaction transaction = new SurfaceTransaction();
+                            SurfaceProperties navBuilder =
+                                    transaction.forSurface(navBarTarget.leash);
+
                             if (mNavFadeIn.value > mNavFadeIn.getStartValue()) {
                                 TaskViewSimulator taskViewSimulator = handle.getTaskViewSimulator();
                                 taskViewSimulator.getCurrentCropRect().round(cropRect);
-                                navBuilder.withMatrix(taskViewSimulator.getCurrentMatrix())
-                                        .withWindowCrop(cropRect)
-                                        .withAlpha(mNavFadeIn.value);
+                                navBuilder.setMatrix(taskViewSimulator.getCurrentMatrix())
+                                        .setWindowCrop(cropRect)
+                                        .setAlpha(mNavFadeIn.value);
                             } else {
-                                navBuilder.withAlpha(mNavFadeOut.value);
+                                navBuilder.setAlpha(mNavFadeOut.value);
                             }
-                            handle.getTransformParams().applySurfaceParams(navBuilder.build());
+                            handle.getTransformParams().applySurfaceParams(transaction);
                         }
                     }
                 });
@@ -406,12 +411,12 @@
                 }
             });
 
-            final RemoteAnimationTargetCompat[] appTargets =
+            final RemoteAnimationTarget[] appTargets =
                     RemoteAnimationTargetCompat.wrapApps(transitionInfo, t, null /* leashMap */);
-            final RemoteAnimationTargetCompat[] wallpaperTargets =
+            final RemoteAnimationTarget[] wallpaperTargets =
                     RemoteAnimationTargetCompat.wrapNonApps(
                             transitionInfo, true /* wallpapers */, t, null /* leashMap */);
-            final RemoteAnimationTargetCompat[] nonAppTargets =
+            final RemoteAnimationTarget[] nonAppTargets =
                     RemoteAnimationTargetCompat.wrapNonApps(
                             transitionInfo, false /* wallpapers */, t, null /* leashMap */);
             final RecentsView recentsView = launchingTaskView.getRecentsView();
@@ -480,7 +485,7 @@
      * If {@param launchingTaskView} is not null, then this will play the tasks launch animation
      * from the position of the GroupedTaskView (when user taps on the TaskView to start it).
      * Technically this case should be taken care of by
-     * {@link #composeRecentsSplitLaunchAnimatorLegacy()} below, but the way we launch tasks whether
+     * {@link #composeRecentsSplitLaunchAnimatorLegacy} below, but the way we launch tasks whether
      * it's a single task or multiple tasks results in different entry-points.
      *
      * If it is null, then it will simply fade in the starting apps and fade out launcher (for the
@@ -488,9 +493,9 @@
     public static void composeRecentsSplitLaunchAnimatorLegacy(
             @Nullable GroupedTaskView launchingTaskView, int initialTaskId,
             @Nullable PendingIntent initialTaskPendingIntent, int secondTaskId,
-            @NonNull RemoteAnimationTargetCompat[] appTargets,
-            @NonNull RemoteAnimationTargetCompat[] wallpaperTargets,
-            @NonNull RemoteAnimationTargetCompat[] nonAppTargets,
+            @NonNull RemoteAnimationTarget[] appTargets,
+            @NonNull RemoteAnimationTarget[] wallpaperTargets,
+            @NonNull RemoteAnimationTarget[] nonAppTargets,
             @NonNull StateManager stateManager,
             @Nullable DepthController depthController,
             @NonNull Runnable finishCallback) {
@@ -513,7 +518,7 @@
 
         final ArrayList<SurfaceControl> openingTargets = new ArrayList<>();
         final ArrayList<SurfaceControl> closingTargets = new ArrayList<>();
-        for (RemoteAnimationTargetCompat appTarget : appTargets) {
+        for (RemoteAnimationTarget appTarget : appTargets) {
             final int taskId = appTarget.taskInfo != null ? appTarget.taskInfo.taskId : -1;
             final int mode = appTarget.mode;
             final SurfaceControl leash = appTarget.leash;
@@ -568,9 +573,9 @@
     }
 
     public static void composeRecentsLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
-            @NonNull RemoteAnimationTargetCompat[] appTargets,
-            @NonNull RemoteAnimationTargetCompat[] wallpaperTargets,
-            @NonNull RemoteAnimationTargetCompat[] nonAppTargets, boolean launcherClosing,
+            @NonNull RemoteAnimationTarget[] appTargets,
+            @NonNull RemoteAnimationTarget[] wallpaperTargets,
+            @NonNull RemoteAnimationTarget[] nonAppTargets, boolean launcherClosing,
             @NonNull StateManager stateManager, @NonNull RecentsView recentsView,
             @Nullable DepthController depthController) {
         boolean skipLauncherChanges = !launcherClosing;
@@ -662,7 +667,7 @@
      * @return the animator animating the surfaces
      */
     public static ValueAnimator createSplitAuxiliarySurfacesAnimator(
-            RemoteAnimationTargetCompat[] nonApps, boolean shown,
+            RemoteAnimationTarget[] nonApps, boolean shown,
             Consumer<ValueAnimator> animatorHandler) {
         if (nonApps == null || nonApps.length == 0) {
             return null;
@@ -672,7 +677,7 @@
         List<SurfaceControl> auxiliarySurfaces = new ArrayList<>(nonApps.length);
         boolean hasSurfaceToAnimate = false;
         for (int i = 0; i < nonApps.length; ++i) {
-            final RemoteAnimationTargetCompat targ = nonApps[i];
+            final RemoteAnimationTarget targ = nonApps[i];
             final SurfaceControl leash = targ.leash;
             if (targ.windowType == TYPE_DOCK_DIVIDER && leash != null && leash.isValid()) {
                 auxiliarySurfaces.add(leash);
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 1452c8f..80db362 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -27,6 +27,7 @@
 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TRACING_ENABLED;
 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_BACK_ANIMATION;
@@ -71,6 +72,7 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.provider.RestoreDbTask;
+import com.android.launcher3.statehandlers.DesktopVisibilityController;
 import com.android.launcher3.statemanager.StatefulActivity;
 import com.android.launcher3.taskbar.TaskbarActivityContext;
 import com.android.launcher3.taskbar.TaskbarManager;
@@ -543,6 +545,18 @@
             mOverviewComponentObserver.onSystemUiStateChanged();
             mTaskbarManager.onSystemUiFlagsChanged(systemUiStateFlags);
 
+            boolean wasFreeformActive =
+                    (lastSysUIFlags & SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE) != 0;
+            boolean isFreeformActive =
+                    (systemUiStateFlags & SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE) != 0;
+            if (wasFreeformActive != isFreeformActive) {
+                DesktopVisibilityController controller = mOverviewComponentObserver
+                        .getActivityInterface().getDesktopVisibilityController();
+                if (controller != null) {
+                    controller.setFreeformTasksVisible(isFreeformActive);
+                }
+            }
+
             boolean wasExpanded = (lastSysUIFlags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) != 0;
             boolean isExpanded =
                     (systemUiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) != 0;
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index 67e0bea..bcaae99 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -200,13 +200,12 @@
     }
 
     @Override
-    public void setModalStateEnabled(boolean isModalState) {
-        super.setModalStateEnabled(isModalState);
+    public void setModalStateEnabled(boolean isModalState, boolean animate) {
         if (isModalState) {
-            mActivity.getStateManager().goToState(RecentsState.MODAL_TASK);
+            mActivity.getStateManager().goToState(RecentsState.MODAL_TASK, animate);
         } else {
             if (mActivity.isInState(RecentsState.MODAL_TASK)) {
-                mActivity.getStateManager().goToState(DEFAULT);
+                mActivity.getStateManager().goToState(DEFAULT, animate);
                 resetModalVisuals();
             }
         }
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
index 6bc24f2..8410149 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
@@ -37,6 +37,7 @@
 import android.graphics.Point;
 import android.graphics.PointF;
 import android.view.MotionEvent;
+import android.view.RemoteAnimationTarget;
 import android.view.VelocityTracker;
 
 import com.android.launcher3.R;
@@ -53,13 +54,12 @@
 import com.android.quickstep.RecentsAnimationDeviceState;
 import com.android.quickstep.RecentsAnimationTargets;
 import com.android.quickstep.TaskAnimationManager;
+import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties;
 import com.android.quickstep.util.TransformParams;
 import com.android.quickstep.util.TransformParams.BuilderProxy;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.InputMonitorCompat;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder;
 
 import java.util.HashMap;
 
@@ -290,9 +290,9 @@
 
     @Override
     public void onBuildTargetParams(
-            Builder builder, RemoteAnimationTargetCompat app, TransformParams params) {
+            SurfaceProperties builder, RemoteAnimationTarget app, TransformParams params) {
         mMatrix.setTranslate(0, mProgress.value * mMaxTranslationY);
-        builder.withMatrix(mMatrix);
+        builder.setMatrix(mMatrix);
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index 7ccd8af..60d5ba4 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -39,8 +39,6 @@
 import android.content.Intent;
 import android.graphics.PointF;
 import android.os.Build;
-import android.os.Handler;
-import android.os.Looper;
 import android.util.Log;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
@@ -57,7 +55,6 @@
 import com.android.launcher3.util.TraceHelper;
 import com.android.quickstep.AbsSwipeUpHandler;
 import com.android.quickstep.AbsSwipeUpHandler.Factory;
-import com.android.quickstep.BaseActivityInterface;
 import com.android.quickstep.GestureState;
 import com.android.quickstep.InputConsumer;
 import com.android.quickstep.RecentsAnimationCallbacks;
@@ -70,7 +67,6 @@
 import com.android.quickstep.util.CachedEventDispatcher;
 import com.android.quickstep.util.MotionPauseDetector;
 import com.android.quickstep.util.NavBarPosition;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.InputChannelCompat.InputEventReceiver;
 import com.android.systemui.shared.system.InputMonitorCompat;
 
@@ -101,7 +97,6 @@
     private final CachedEventDispatcher mRecentsViewDispatcher = new CachedEventDispatcher();
     private final InputMonitorCompat mInputMonitorCompat;
     private final InputEventReceiver mInputEventReceiver;
-    private final BaseActivityInterface mActivityInterface;
 
     private final AbsSwipeUpHandler.Factory mHandlerFactory;
 
@@ -136,12 +131,6 @@
     // Might be displacement in X or Y, depending on the direction we are swiping from the nav bar.
     private float mStartDisplacement;
 
-    private Handler mMainThreadHandler;
-    private Runnable mCancelRecentsAnimationRunnable = () -> {
-        ActivityManagerWrapper.getInstance().cancelRecentsAnimation(
-                true /* restoreHomeStackPosition */);
-    };
-
     public OtherActivityInputConsumer(Context base, RecentsAnimationDeviceState deviceState,
             TaskAnimationManager taskAnimationManager, GestureState gestureState,
             boolean isDeferredDownTarget, Consumer<OtherActivityInputConsumer> onCompleteCallback,
@@ -152,9 +141,7 @@
         mNavBarPosition = mDeviceState.getNavBarPosition();
         mTaskAnimationManager = taskAnimationManager;
         mGestureState = gestureState;
-        mMainThreadHandler = new Handler(Looper.getMainLooper());
         mHandlerFactory = handlerFactory;
-        mActivityInterface = mGestureState.getActivityInterface();
 
         mMotionPauseDetector = new MotionPauseDetector(base, false,
                 mNavBarPosition.isLeftEdge() || mNavBarPosition.isRightEdge()
@@ -437,13 +424,6 @@
             }
             onConsumerAboutToBeSwitched();
             onInteractionGestureFinished();
-
-            // Cancel the recents animation if SysUI happens to handle UP before we have a chance
-            // to start the recents animation. In addition, workaround for b/126336729 by delaying
-            // the cancel of the animation for a period, in case SysUI is slow to handle UP and we
-            // handle DOWN & UP and move the home stack before SysUI can start the activity
-            mMainThreadHandler.removeCallbacks(mCancelRecentsAnimationRunnable);
-            mMainThreadHandler.postDelayed(mCancelRecentsAnimationRunnable, 100);
         }
         cleanupAfterGesture();
         TraceHelper.INSTANCE.endSection(traceToken);
@@ -465,7 +445,6 @@
     @Override
     public void onConsumerAboutToBeSwitched() {
         Preconditions.assertUIThread();
-        mMainThreadHandler.removeCallbacks(mCancelRecentsAnimationRunnable);
         if (mInteractionHandler != null) {
             // The consumer is being switched while we are active. Set up the shared state to be
             // used by the next animation
diff --git a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
index fa7bc04..d7ff0be 100644
--- a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
@@ -33,7 +33,6 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.os.Build;
-import android.view.SurfaceControl;
 import android.view.View;
 import android.view.ViewOutlineProvider;
 
@@ -53,9 +52,11 @@
 import com.android.quickstep.RemoteTargetGluer;
 import com.android.quickstep.SwipeUpAnimationLogic;
 import com.android.quickstep.SwipeUpAnimationLogic.RunningWindowAnim;
+import com.android.quickstep.util.RecordingSurfaceTransaction;
 import com.android.quickstep.util.RectFSpringAnim;
+import com.android.quickstep.util.SurfaceTransaction;
+import com.android.quickstep.util.SurfaceTransaction.MockProperties;
 import com.android.quickstep.util.TransformParams;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
 
 @TargetApi(Build.VERSION_CODES.R)
 abstract class SwipeUpGestureTutorialController extends TutorialController {
@@ -415,21 +416,23 @@
     private class FakeTransformParams extends TransformParams {
 
         @Override
-        public SurfaceParams[] createSurfaceParams(BuilderProxy proxy) {
-            SurfaceParams.Builder builder = new SurfaceParams.Builder((SurfaceControl) null);
-            proxy.onBuildTargetParams(builder, null, this);
-            return new SurfaceParams[] {builder.build()};
+        public SurfaceTransaction createSurfaceParams(BuilderProxy proxy) {
+            RecordingSurfaceTransaction transaction = new RecordingSurfaceTransaction();
+            proxy.onBuildTargetParams(transaction.mockProperties, null, this);
+            return transaction;
         }
 
         @Override
-        public void applySurfaceParams(SurfaceParams[] params) {
-            SurfaceParams p = params[0];
-            mFakeTaskView.setAnimationMatrix(p.matrix);
-            mFakePreviousTaskView.setAnimationMatrix(p.matrix);
-            mFakeTaskViewRect.set(p.windowCrop);
-            mFakeTaskViewRadius = p.cornerRadius;
-            mFakeTaskView.invalidateOutline();
-            mFakePreviousTaskView.invalidateOutline();
+        public void applySurfaceParams(SurfaceTransaction params) {
+            if (params instanceof RecordingSurfaceTransaction) {
+                MockProperties p = ((RecordingSurfaceTransaction) params).mockProperties;
+                mFakeTaskView.setAnimationMatrix(p.matrix);
+                mFakePreviousTaskView.setAnimationMatrix(p.matrix);
+                mFakeTaskViewRect.set(p.windowCrop);
+                mFakeTaskViewRadius = p.cornerRadius;
+                mFakeTaskView.invalidateOutline();
+                mFakePreviousTaskView.invalidateOutline();
+            }
         }
     }
 }
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index 79c578d..791f93b 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -191,7 +191,7 @@
                 info.getWidget().getSpanX(), // span_x = 17 [default = 1];
                 info.getWidget().getSpanY(), // span_y = 18 [default = 1];
                 getAttributes(info) /* attributes = 19 [(log_mode) = MODE_BYTES] */,
-                false /* is_kids_mode = 20 */
+                info.getIsKidsMode() /* is_kids_mode = 20 */
         );
     }
 
diff --git a/quickstep/src/com/android/quickstep/util/BaseDepthController.java b/quickstep/src/com/android/quickstep/util/BaseDepthController.java
index 29ae9a1..064e675 100644
--- a/quickstep/src/com/android/quickstep/util/BaseDepthController.java
+++ b/quickstep/src/com/android/quickstep/util/BaseDepthController.java
@@ -71,7 +71,7 @@
      * Ratio from 0 to 1, where 0 is fully zoomed out, and 1 is zoomed in.
      * @see android.service.wallpaper.WallpaperService.Engine#onZoomChanged(float)
      */
-    protected float mDepth;
+    private float mDepth;
 
     protected SurfaceControl mSurface;
 
@@ -143,7 +143,7 @@
         }
     }
 
-    protected void setDepth(float depth) {
+    private void setDepth(float depth) {
         depth = Utilities.boundToRange(depth, 0, 1);
         // Round out the depth to dedupe frequent, non-perceptable updates
         int depthI = (int) (depth * 256);
diff --git a/quickstep/src/com/android/quickstep/util/RecordingSurfaceTransaction.java b/quickstep/src/com/android/quickstep/util/RecordingSurfaceTransaction.java
new file mode 100644
index 0000000..a2f48dd
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/RecordingSurfaceTransaction.java
@@ -0,0 +1,28 @@
+/*
+ * 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.quickstep.util;
+
+/**
+ * Extension for {@link SurfaceTransaction} which records the commands for mocking
+ */
+public class RecordingSurfaceTransaction extends SurfaceTransaction {
+
+    /**
+     * A mock builder which can be used for recording values
+     */
+    public final MockProperties mockProperties = new MockProperties();
+
+}
diff --git a/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java b/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
index ee82ae6..10f2eaa 100644
--- a/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
+++ b/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
@@ -16,23 +16,22 @@
 package com.android.quickstep.util;
 
 import android.animation.AnimatorSet;
-
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import android.view.RemoteAnimationTarget;
 
 public abstract class RemoteAnimationProvider {
 
-    public abstract AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] appTargets,
-            RemoteAnimationTargetCompat[] wallpaperTargets);
+    public abstract AnimatorSet createWindowAnimation(RemoteAnimationTarget[] appTargets,
+            RemoteAnimationTarget[] wallpaperTargets);
 
     /**
      * @return the target with the lowest opaque layer for a certain app animation, or null.
      */
-    public static RemoteAnimationTargetCompat findLowestOpaqueLayerTarget(
-            RemoteAnimationTargetCompat[] appTargets, int mode) {
+    public static RemoteAnimationTarget findLowestOpaqueLayerTarget(
+            RemoteAnimationTarget[] appTargets, int mode) {
         int lowestLayer = Integer.MAX_VALUE;
         int lowestLayerIndex = -1;
         for (int i = appTargets.length - 1; i >= 0; i--) {
-            RemoteAnimationTargetCompat target = appTargets[i];
+            RemoteAnimationTarget target = appTargets[i];
             if (target.mode == mode && !target.isTranslucent) {
                 int layer = target.prefixOrderIndex;
                 if (layer < lowestLayer) {
diff --git a/quickstep/src/com/android/quickstep/util/RemoteFadeOutAnimationListener.java b/quickstep/src/com/android/quickstep/util/RemoteFadeOutAnimationListener.java
index 81c124f..382cf79 100644
--- a/quickstep/src/com/android/quickstep/util/RemoteFadeOutAnimationListener.java
+++ b/quickstep/src/com/android/quickstep/util/RemoteFadeOutAnimationListener.java
@@ -15,14 +15,14 @@
  */
 package com.android.quickstep.util;
 
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
+import static android.view.RemoteAnimationTarget.MODE_CLOSING;
 
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.view.RemoteAnimationTarget;
+import android.view.SurfaceControl.Transaction;
 
 import com.android.quickstep.RemoteAnimationTargets;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.TransactionCompat;
 
 /**
  * Animation listener which fades out the closing targets
@@ -32,24 +32,24 @@
     private final RemoteAnimationTargets mTarget;
     private boolean mFirstFrame = true;
 
-    public RemoteFadeOutAnimationListener(RemoteAnimationTargetCompat[] appTargets,
-            RemoteAnimationTargetCompat[] wallpaperTargets) {
+    public RemoteFadeOutAnimationListener(RemoteAnimationTarget[] appTargets,
+            RemoteAnimationTarget[] wallpaperTargets) {
         mTarget = new RemoteAnimationTargets(appTargets, wallpaperTargets,
-                new RemoteAnimationTargetCompat[0], MODE_CLOSING);
+                new RemoteAnimationTarget[0], MODE_CLOSING);
     }
 
     @Override
     public void onAnimationUpdate(ValueAnimator valueAnimator) {
-        TransactionCompat t = new TransactionCompat();
+        Transaction t = new Transaction();
         if (mFirstFrame) {
-            for (RemoteAnimationTargetCompat target : mTarget.unfilteredApps) {
+            for (RemoteAnimationTarget target : mTarget.unfilteredApps) {
                 t.show(target.leash);
             }
             mFirstFrame = false;
         }
 
         float alpha = 1 - valueAnimator.getAnimatedFraction();
-        for (RemoteAnimationTargetCompat app : mTarget.apps) {
+        for (RemoteAnimationTarget app : mTarget.apps) {
             t.setAlpha(app.leash, alpha);
         }
         t.apply();
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index f07f990..3119a77 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -38,6 +38,7 @@
 import android.util.Log;
 import android.util.Pair;
 import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationTarget;
 import android.view.SurfaceControl;
 import android.window.TransitionInfo;
 
@@ -61,7 +62,6 @@
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
 import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 import com.android.systemui.shared.system.RemoteTransitionCompat;
 import com.android.systemui.shared.system.RemoteTransitionRunner;
 
@@ -359,8 +359,8 @@
         }
 
         @Override
-        public void onAnimationStart(int transit, RemoteAnimationTargetCompat[] apps,
-                RemoteAnimationTargetCompat[] wallpapers, RemoteAnimationTargetCompat[] nonApps,
+        public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
+                RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
                 Runnable finishedCallback) {
             postAsyncCallback(mHandler,
                     () -> TaskViewUtils.composeRecentsSplitLaunchAnimatorLegacy(
diff --git a/quickstep/src/com/android/quickstep/util/SurfaceTransaction.java b/quickstep/src/com/android/quickstep/util/SurfaceTransaction.java
new file mode 100644
index 0000000..7ab285d
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/SurfaceTransaction.java
@@ -0,0 +1,161 @@
+/*
+ * 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.quickstep.util;
+
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
+
+/**
+ * Helper class for building a {@link Transaction}.
+ */
+public class SurfaceTransaction {
+
+    private final Transaction mTransaction = new Transaction();
+    private final float[] mTmpValues = new float[9];
+
+    /**
+     * Creates a new builder for the provided surface
+     */
+    public SurfaceProperties forSurface(SurfaceControl surface) {
+        return surface.isValid() ? new SurfaceProperties(surface) : new MockProperties();
+    }
+
+    /**
+     * Returns the final transaction
+     */
+    public Transaction getTransaction() {
+        return mTransaction;
+    }
+
+    /**
+     * Utility class to update surface params in a transaction
+     */
+    public class SurfaceProperties {
+
+        private final SurfaceControl mSurface;
+
+        SurfaceProperties(SurfaceControl surface) {
+            mSurface = surface;
+        }
+
+        /**
+         * @param alpha The alpha value to apply to the surface.
+         * @return this Builder
+         */
+        public SurfaceProperties setAlpha(float alpha) {
+            mTransaction.setAlpha(mSurface, alpha);
+            return this;
+        }
+
+        /**
+         * @param matrix The matrix to apply to the surface.
+         * @return this Builder
+         */
+        public SurfaceProperties setMatrix(Matrix matrix) {
+            mTransaction.setMatrix(mSurface, matrix, mTmpValues);
+            return this;
+        }
+
+        /**
+         * @param windowCrop The window crop to apply to the surface.
+         * @return this Builder
+         */
+        public SurfaceProperties setWindowCrop(Rect windowCrop) {
+            mTransaction.setWindowCrop(mSurface, windowCrop);
+            return this;
+        }
+
+        /**
+         * @param relativeLayer The relative layer.
+         * @return this Builder
+         */
+        public SurfaceProperties setLayer(int relativeLayer) {
+            mTransaction.setLayer(mSurface, relativeLayer);
+            return this;
+        }
+
+        /**
+         * @param radius the Radius for rounded corners to apply to the surface.
+         * @return this Builder
+         */
+        public SurfaceProperties setCornerRadius(float radius) {
+            mTransaction.setCornerRadius(mSurface, radius);
+            return this;
+        }
+
+        /**
+         * @param radius the Radius for the shadows to apply to the surface.
+         * @return this Builder
+         */
+        public SurfaceProperties setShadowRadius(float radius) {
+            mTransaction.setShadowRadius(mSurface, radius);
+            return this;
+        }
+    }
+
+    /**
+     * Extension of {@link SurfaceProperties} which just stores all the values locally
+     */
+    public class MockProperties extends SurfaceProperties {
+
+        public float alpha = -1;
+        public Matrix matrix = null;
+        public Rect windowCrop = null;
+        public float cornerRadius = 0;
+        public float shadowRadius = 0;
+
+        protected MockProperties() {
+            super(null);
+        }
+
+        @Override
+        public SurfaceProperties setAlpha(float alpha) {
+            this.alpha = alpha;
+            return this;
+        }
+
+        @Override
+        public SurfaceProperties setMatrix(Matrix matrix) {
+            this.matrix = matrix;
+            return this;
+        }
+
+        @Override
+        public SurfaceProperties setWindowCrop(Rect windowCrop) {
+            this.windowCrop = windowCrop;
+            return this;
+        }
+
+        @Override
+        public SurfaceProperties setLayer(int relativeLayer) {
+            return this;
+        }
+
+        @Override
+        public SurfaceProperties setCornerRadius(float radius) {
+            this.cornerRadius = radius;
+            return this;
+        }
+
+        @Override
+        public SurfaceProperties setShadowRadius(float radius) {
+            this.shadowRadius = radius;
+            return this;
+        }
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java b/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java
index 1200208..95473dc 100644
--- a/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java
+++ b/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java
@@ -25,7 +25,6 @@
 import android.view.ViewRootImpl;
 
 import com.android.quickstep.RemoteAnimationTargets.ReleaseCheck;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
 
 import java.util.function.Consumer;
 
@@ -70,18 +69,12 @@
      * @param params The surface parameters to apply. DO NOT MODIFY the list after passing into
      *               this method to avoid synchronization issues.
      */
-    public void scheduleApply(final SurfaceParams... params) {
+    public void scheduleApply(SurfaceTransaction params) {
         View view = mTargetViewRootImpl.getView();
         if (view == null) {
             return;
         }
-        Transaction t = new Transaction();
-        for (int i = params.length - 1; i >= 0; i--) {
-            SurfaceParams surfaceParams = params[i];
-            if (surfaceParams.surface.isValid()) {
-                surfaceParams.applyTo(t);
-            }
-        }
+        Transaction t = params.getTransaction();
 
         mLastSequenceNumber++;
         final int toApplySeqNo = mLastSequenceNumber;
@@ -102,7 +95,7 @@
     }
 
     /**
-     * Creates an instance of SyncRtSurfaceTransactionApplier, deferring until the target view is
+     * Creates an instance of SurfaceTransactionApplier, deferring until the target view is
      * attached if necessary.
      */
     public static void create(
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index c03aa3f..5c37da1 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -35,6 +35,7 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.util.Log;
+import android.view.RemoteAnimationTarget;
 
 import androidx.annotation.NonNull;
 
@@ -46,11 +47,10 @@
 import com.android.quickstep.AnimatedFloat;
 import com.android.quickstep.BaseActivityInterface;
 import com.android.quickstep.TaskAnimationManager;
+import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties;
 import com.android.quickstep.views.TaskView.FullscreenDrawParams;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.recents.utilities.PreviewPositionHelper;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder;
 
 /**
  * A utility class which emulates the layout behavior of TaskView and RecentsView
@@ -171,8 +171,11 @@
     /**
      * Sets the targets which the simulator will control
      */
-    public void setPreview(RemoteAnimationTargetCompat runningTarget) {
-        setPreviewBounds(runningTarget.startScreenSpaceBounds, runningTarget.contentInsets);
+    public void setPreview(RemoteAnimationTarget runningTarget) {
+        setPreviewBounds(
+                runningTarget.startBounds == null
+                        ? runningTarget.screenSpaceBounds : runningTarget.startBounds,
+                runningTarget.contentInsets);
     }
 
     /**
@@ -181,7 +184,7 @@
      *
      * @param splitInfo set to {@code null} when not in staged split mode
      */
-    public void setPreview(RemoteAnimationTargetCompat runningTarget, SplitBounds splitInfo) {
+    public void setPreview(RemoteAnimationTarget runningTarget, SplitBounds splitInfo) {
         setPreview(runningTarget);
         mSplitBounds = splitInfo;
         if (mSplitBounds == null) {
@@ -191,6 +194,7 @@
         mStagePosition = mThumbnailPosition.equals(splitInfo.leftTopBounds) ?
                 STAGE_POSITION_TOP_OR_LEFT :
                 STAGE_POSITION_BOTTOM_OR_RIGHT;
+        mPositionHelper.setSplitBounds(convertSplitBounds(mSplitBounds), mStagePosition);
     }
 
     /**
@@ -386,10 +390,10 @@
 
     @Override
     public void onBuildTargetParams(
-            Builder builder, RemoteAnimationTargetCompat app, TransformParams params) {
-        builder.withMatrix(mMatrix)
-                .withWindowCrop(mTmpCropRect)
-                .withCornerRadius(getCurrentCornerRadius());
+            SurfaceProperties builder, RemoteAnimationTarget app, TransformParams params) {
+        builder.setMatrix(mMatrix)
+                .setWindowCrop(mTmpCropRect)
+                .setCornerRadius(getCurrentCornerRadius());
 
         // If mDrawsBelowRecents is unset, no reordering will be enforced.
         if (mDrawsBelowRecents != null) {
@@ -398,7 +402,7 @@
             // conflict with layers that WM core positions (ie. the input consumers).  For shell
             // transitions, the animation leashes are reparented to an animation container so we
             // can bump layers as needed.
-            builder.withLayer(mDrawsBelowRecents
+            builder.setLayer(mDrawsBelowRecents
                     ? Integer.MIN_VALUE + 1
                     : ENABLE_SHELL_TRANSITIONS ? Integer.MAX_VALUE : 0);
         }
@@ -418,4 +422,15 @@
         return Math.max(Math.abs(mTempPoint[0]), Math.abs(mTempPoint[1]));
     }
 
+    /**
+     * TODO(b/254378592): Remove this after consolidation of classes
+     */
+    public static com.android.wm.shell.util.SplitBounds convertSplitBounds(SplitBounds bounds) {
+        return new com.android.wm.shell.util.SplitBounds(
+                bounds.leftTopBounds,
+                bounds.rightBottomBounds,
+                bounds.leftTopTaskId,
+                bounds.rightBottomTaskId
+        );
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/util/TransformParams.java b/quickstep/src/com/android/quickstep/util/TransformParams.java
index a7f25d4..aa9a45b 100644
--- a/quickstep/src/com/android/quickstep/util/TransformParams.java
+++ b/quickstep/src/com/android/quickstep/util/TransformParams.java
@@ -15,16 +15,18 @@
  */
 package com.android.quickstep.util;
 
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+
 import android.util.FloatProperty;
+import android.view.RemoteAnimationTarget;
 import android.view.SurfaceControl;
 
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.Interpolators;
 import com.android.quickstep.RemoteAnimationTargets;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
-import com.android.systemui.shared.system.TransactionCompat;
+import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties;
 
 public class TransformParams {
 
@@ -113,8 +115,7 @@
      * Sets the SyncRtSurfaceTransactionApplierCompat that will apply the SurfaceParams that
      * are computed based on these TransformParams.
      */
-    public TransformParams setSyncTransactionApplier(
-            SurfaceTransactionApplier applier) {
+    public TransformParams setSyncTransactionApplier(SurfaceTransactionApplier applier) {
         mSyncTransactionApplier = applier;
         return this;
     }
@@ -137,28 +138,26 @@
         return this;
     }
 
-    public SurfaceParams[] createSurfaceParams(BuilderProxy proxy) {
+    public SurfaceTransaction createSurfaceParams(BuilderProxy proxy) {
         RemoteAnimationTargets targets = mTargetSet;
-        final int appLength =  targets.unfilteredApps.length;
-        final int wallpaperLength = targets.wallpapers != null ? targets.wallpapers.length : 0;
-        SurfaceParams[] surfaceParams = new SurfaceParams[appLength + wallpaperLength];
+        SurfaceTransaction transaction = new SurfaceTransaction();
         mRecentsSurface = getRecentsSurface(targets);
 
-        for (int i = 0; i < appLength; i++) {
-            RemoteAnimationTargetCompat app = targets.unfilteredApps[i];
-            SurfaceParams.Builder builder = new SurfaceParams.Builder(app.leash);
+        for (int i = 0; i < targets.unfilteredApps.length; i++) {
+            RemoteAnimationTarget app = targets.unfilteredApps[i];
+            SurfaceProperties builder = transaction.forSurface(app.leash);
 
             if (app.mode == targets.targetMode) {
-                if (app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
+                int activityType = app.windowConfiguration.getActivityType();
+                if (activityType == ACTIVITY_TYPE_HOME) {
                     mHomeBuilderProxy.onBuildTargetParams(builder, app, this);
                 } else {
                     // Fade out Assistant overlay.
-                    if (app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_ASSISTANT
-                            && app.isNotInRecents) {
+                    if (activityType == ACTIVITY_TYPE_ASSISTANT && app.isNotInRecents) {
                         float progress = Utilities.boundToRange(getProgress(), 0, 1);
-                        builder.withAlpha(1 - Interpolators.DEACCEL_2_5.getInterpolation(progress));
+                        builder.setAlpha(1 - Interpolators.DEACCEL_2_5.getInterpolation(progress));
                     } else {
-                        builder.withAlpha(getTargetAlpha());
+                        builder.setAlpha(getTargetAlpha());
                     }
 
                     proxy.onBuildTargetParams(builder, app, this);
@@ -166,22 +165,22 @@
             } else {
                 mBaseBuilderProxy.onBuildTargetParams(builder, app, this);
             }
-            surfaceParams[i] = builder.build();
         }
+
         // always put wallpaper layer to bottom.
+        final int wallpaperLength = targets.wallpapers != null ? targets.wallpapers.length : 0;
         for (int i = 0; i < wallpaperLength; i++) {
-            RemoteAnimationTargetCompat wallpaper = targets.wallpapers[i];
-            surfaceParams[appLength + i] = new SurfaceParams.Builder(wallpaper.leash)
-                    .withLayer(Integer.MIN_VALUE).build();
+            RemoteAnimationTarget wallpaper = targets.wallpapers[i];
+            transaction.forSurface(wallpaper.leash).setLayer(Integer.MIN_VALUE);
         }
-        return surfaceParams;
+        return transaction;
     }
 
     private static SurfaceControl getRecentsSurface(RemoteAnimationTargets targets) {
         for (int i = 0; i < targets.unfilteredApps.length; i++) {
-            RemoteAnimationTargetCompat app = targets.unfilteredApps[i];
+            RemoteAnimationTarget app = targets.unfilteredApps[i];
             if (app.mode == targets.targetMode) {
-                if (app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_RECENTS) {
+                if (app.windowConfiguration.getActivityType() == ACTIVITY_TYPE_RECENTS) {
                     return app.leash;
                 }
             } else {
@@ -213,15 +212,11 @@
         return mTargetSet;
     }
 
-    public void applySurfaceParams(SurfaceParams... params) {
+    public void applySurfaceParams(SurfaceTransaction builder) {
         if (mSyncTransactionApplier != null) {
-            mSyncTransactionApplier.scheduleApply(params);
+            mSyncTransactionApplier.scheduleApply(builder);
         } else {
-            TransactionCompat t = new TransactionCompat();
-            for (SurfaceParams param : params) {
-                SyncRtSurfaceTransactionApplierCompat.applyParams(t, param);
-            }
-            t.apply();
+            builder.getTransaction().apply();
         }
     }
 
@@ -229,9 +224,9 @@
     public interface BuilderProxy {
 
         BuilderProxy NO_OP = (builder, app, params) -> { };
-        BuilderProxy ALWAYS_VISIBLE = (builder, app, params) ->builder.withAlpha(1);
+        BuilderProxy ALWAYS_VISIBLE = (builder, app, params) -> builder.setAlpha(1);
 
-        void onBuildTargetParams(SurfaceParams.Builder builder,
-                RemoteAnimationTargetCompat app, TransformParams params);
+        void onBuildTargetParams(SurfaceProperties builder,
+                RemoteAnimationTarget app, TransformParams params);
     }
 }
diff --git a/quickstep/src/com/android/quickstep/util/ViewCapture.java b/quickstep/src/com/android/quickstep/util/ViewCapture.java
index 6171c5d..ba7d7f5 100644
--- a/quickstep/src/com/android/quickstep/util/ViewCapture.java
+++ b/quickstep/src/com/android/quickstep/util/ViewCapture.java
@@ -23,10 +23,9 @@
 
 import android.content.Context;
 import android.content.res.Resources;
-import android.os.Handler;
 import android.os.Looper;
-import android.os.Message;
 import android.os.Process;
+import android.os.SystemClock;
 import android.os.Trace;
 import android.text.TextUtils;
 import android.util.Base64;
@@ -57,7 +56,9 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.concurrent.Future;
+import java.util.concurrent.Executor;
+import java.util.concurrent.FutureTask;
+import java.util.function.Consumer;
 import java.util.zip.GZIPOutputStream;
 
 /**
@@ -83,7 +84,7 @@
     private final List<WindowListener> mListeners = new ArrayList<>();
 
     private final Context mContext;
-    private final LooperExecutor mExecutor;
+    private final Executor mExecutor;
 
     // Pool used for capturing view tree on the UI thread.
     private ViewRef mPool = new ViewRef();
@@ -156,8 +157,13 @@
         ViewIdProvider idProvider = new ViewIdProvider(mContext.getResources());
 
         // Collect all the tasks first so that all the tasks are posted on the executor
-        List<Pair<String, Future<ExportedData>>> tasks = mListeners.stream()
-                .map(l -> Pair.create(l.name, mExecutor.submit(() -> l.dumpToProto(idProvider))))
+        List<Pair<String, FutureTask<ExportedData>>> tasks = mListeners.stream()
+                .map(l -> {
+                    FutureTask<ExportedData> task =
+                            new FutureTask<ExportedData>(() -> l.dumpToProto(idProvider));
+                    mExecutor.execute(task);
+                    return Pair.create(l.name, task);
+                })
                 .collect(toList());
 
         tasks.forEach(pair -> {
@@ -187,7 +193,6 @@
         private final View mRoot;
         public final String name;
 
-        private final Handler mHandler;
         private final ViewRef mViewRef = new ViewRef();
 
         private int mFrameIndexBg = -1;
@@ -196,20 +201,23 @@
         private final ViewPropertyRef[] mNodesBg = new ViewPropertyRef[MEMORY_SIZE];
 
         private boolean mDestroyed = false;
+        private final Consumer<ViewRef> mCaptureCallback = this::captureViewPropertiesBg;
 
         WindowListener(View view, String name) {
             mRoot = view;
             this.name = name;
-            mHandler = new Handler(mExecutor.getLooper(), this::captureViewPropertiesBg);
         }
 
         @Override
         public void onDraw() {
             Trace.beginSection("view_capture");
             captureViewTree(mRoot, mViewRef);
-            Message m = Message.obtain(mHandler);
-            m.obj = mViewRef.next;
-            mHandler.sendMessage(m);
+            ViewRef captured = mViewRef.next;
+            if (captured != null) {
+                captured.callback = mCaptureCallback;
+                captured.creationTime = SystemClock.uptimeMillis();
+                mExecutor.execute(captured);
+            }
             mIsFirstFrame = false;
             Trace.endSection();
         }
@@ -219,12 +227,8 @@
          * back to the pool
          */
         @WorkerThread
-        private boolean captureViewPropertiesBg(Message msg) {
-            ViewRef viewRefStart = (ViewRef) msg.obj;
-            long time = msg.getWhen();
-            if (viewRefStart == null) {
-                return false;
-            }
+        private void captureViewPropertiesBg(ViewRef viewRefStart) {
+            long time = viewRefStart.creationTime;
             mFrameIndexBg++;
             if (mFrameIndexBg >= MEMORY_SIZE) {
                 mFrameIndexBg = 0;
@@ -292,7 +296,6 @@
                 viewRefEnd = viewRefEnd.next;
             }
             mNodesBg[mFrameIndexBg] = resultStart;
-            return true;
         }
 
         private ViewPropertyRef findInLastFrame(int hashCode) {
@@ -464,11 +467,14 @@
         }
     }
 
-    private static class ViewRef {
+    private static class ViewRef implements Runnable {
         public View view;
         public int childCount = 0;
         public ViewRef next;
 
+        public Consumer<ViewRef> callback = null;
+        public long creationTime = 0;
+
         public void transferTo(ViewPropertyRef out) {
             out.childCount = this.childCount;
 
@@ -495,6 +501,15 @@
             out.visibility = view.getVisibility();
             out.willNotDraw = view.willNotDraw();
         }
+
+        @Override
+        public void run() {
+            Consumer<ViewRef> oldCallback = callback;
+            callback = null;
+            if (oldCallback != null) {
+                oldCallback.accept(this);
+            }
+        }
     }
 
     private static final class ViewIdProvider {
diff --git a/quickstep/src/com/android/quickstep/views/DesktopTaskView.java b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
index 9874f96..8385afe 100644
--- a/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
@@ -284,11 +284,18 @@
         return false;
     }
 
+    @Override
+    public RunnableList launchTasks() {
+        showDesktopApps();
+        getRecentsView().onTaskLaunchedInLiveTileMode();
+        return new RunnableList();
+    }
+
     @Nullable
     @Override
     public RunnableList launchTaskAnimated() {
         RunnableList endCallback = new RunnableList();
-        SystemUiProxy.INSTANCE.get(getContext()).showDesktopApps();
+        showDesktopApps();
         RecentsView<?, ?> recentsView = getRecentsView();
         recentsView.addSideTaskLaunchCallback(endCallback);
         return endCallback;
@@ -296,10 +303,14 @@
 
     @Override
     public void launchTask(@NonNull Consumer<Boolean> callback, boolean freezeTaskList) {
-        SystemUiProxy.INSTANCE.get(getContext()).showDesktopApps();
+        showDesktopApps();
         callback.accept(true);
     }
 
+    private void showDesktopApps() {
+        SystemUiProxy.INSTANCE.get(getContext()).showDesktopApps();
+    }
+
     @Override
     void refreshThumbnails(@Nullable HashMap<Integer, ThumbnailData> thumbnailDatas) {
         // Sets new thumbnails based on the incoming data and refreshes the rest.
diff --git a/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java b/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java
index 8a5f42a..6431bdf 100644
--- a/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java
+++ b/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java
@@ -25,6 +25,7 @@
 import android.util.AttributeSet;
 import android.util.Size;
 import android.view.GhostView;
+import android.view.RemoteAnimationTarget;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver.OnGlobalLayoutListener;
@@ -41,7 +42,6 @@
 import com.android.launcher3.views.ListenerView;
 import com.android.launcher3.widget.LauncherAppWidgetHostView;
 import com.android.launcher3.widget.RoundedCornerEnforcement;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
 /** A view that mimics an App Widget through a launch animation. */
 @TargetApi(Build.VERSION_CODES.S)
@@ -304,7 +304,7 @@
      * context's theme background color.
      */
     public static int getDefaultBackgroundColor(
-            Context context, RemoteAnimationTargetCompat target) {
+            Context context, RemoteAnimationTarget target) {
         return (target != null && target.taskInfo.taskDescription != null)
                 ? target.taskInfo.taskDescription.getBackgroundColor()
                 : Themes.getColorBackground(context);
diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
index 71b0c60..64b5e0b 100644
--- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
@@ -1,6 +1,5 @@
 package com.android.quickstep.views;
 
-import static com.android.launcher3.AbstractFloatingView.getAnyView;
 import static com.android.launcher3.util.SplitConfigurationOptions.DEFAULT_SPLIT_RATIO;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
@@ -14,11 +13,11 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.util.RunnableList;
+import com.android.launcher3.util.SplitConfigurationOptions;
 import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds;
 import com.android.launcher3.util.TransformingTouchDelegate;
 import com.android.quickstep.RecentsModel;
@@ -26,8 +25,10 @@
 import com.android.quickstep.TaskThumbnailCache;
 import com.android.quickstep.util.CancellableTask;
 import com.android.quickstep.util.RecentsOrientedState;
+import com.android.quickstep.util.TaskViewSimulator;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.systemui.shared.recents.utilities.PreviewPositionHelper;
 import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
 
 import java.util.HashMap;
@@ -86,9 +87,19 @@
         mTaskIdContainer[1] = secondary.key.id;
         mTaskIdAttributeContainer[1] = new TaskIdAttributeContainer(secondary, mSnapshotView2,
                 mIconView2, STAGE_POSITION_BOTTOM_OR_RIGHT);
-        mTaskIdAttributeContainer[0].setStagePosition(STAGE_POSITION_TOP_OR_LEFT);
+        mTaskIdAttributeContainer[0].setStagePosition(
+                SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT);
         mSnapshotView2.bind(secondary);
         mSplitBoundsConfig = splitBoundsConfig;
+        if (mSplitBoundsConfig == null) {
+            return;
+        }
+        mSnapshotView.getPreviewPositionHelper().setSplitBounds(TaskViewSimulator
+                        .convertSplitBounds(splitBoundsConfig),
+                PreviewPositionHelper.STAGE_POSITION_TOP_OR_LEFT);
+        mSnapshotView2.getPreviewPositionHelper().setSplitBounds(TaskViewSimulator
+                        .convertSplitBounds(splitBoundsConfig),
+                PreviewPositionHelper.STAGE_POSITION_BOTTOM_OR_RIGHT);
     }
 
     @Override
@@ -164,21 +175,6 @@
         }
     }
 
-    @Override
-    protected boolean showTaskMenuWithContainer(IconView iconView) {
-        boolean showedTaskMenu = super.showTaskMenuWithContainer(iconView);
-        if (iconView == mIconView2 && showedTaskMenu && !mActivity.getDeviceProfile().isTablet) {
-            // Adjust the position of the secondary task's menu view (only on phones)
-            TaskMenuView taskMenuView = getAnyView(mActivity, AbstractFloatingView.TYPE_TASK_MENU);
-            DeviceProfile deviceProfile = mActivity.getDeviceProfile();
-            getRecentsView().getPagedOrientationHandler()
-                    .setSecondaryTaskMenuPosition(mSplitBoundsConfig, this,
-                            deviceProfile, mTaskIdAttributeContainer[0].getThumbnailView(),
-                            taskMenuView);
-        }
-        return showedTaskMenu;
-    }
-
     @Nullable
     @Override
     public RunnableList launchTaskAnimated() {
@@ -207,7 +203,8 @@
     @Override
     public void launchTask(@NonNull Consumer<Boolean> callback, boolean freezeTaskList) {
         getRecentsView().getSplitSelectController().launchTasks(mTask.key.id, mSecondaryTask.key.id,
-                STAGE_POSITION_TOP_OR_LEFT, callback, freezeTaskList, getSplitRatio());
+                SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT, callback, freezeTaskList,
+                getSplitRatio());
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index bb8506d..6c27587 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -147,6 +147,9 @@
                     & CLEAR_ALL_BUTTON) != 0;
             setDisallowScrollToClearAll(!hasClearAllButton);
         }
+        if (mActivity.getDesktopVisibilityController() != null) {
+            mActivity.getDesktopVisibilityController().setOverviewStateEnabled(enabled);
+        }
     }
 
     @Override
@@ -162,13 +165,12 @@
     }
 
     @Override
-    public void setModalStateEnabled(boolean isModalState) {
-        super.setModalStateEnabled(isModalState);
+    public void setModalStateEnabled(boolean isModalState, boolean animate) {
         if (isModalState) {
-            mActivity.getStateManager().goToState(LauncherState.OVERVIEW_MODAL_TASK);
+            mActivity.getStateManager().goToState(LauncherState.OVERVIEW_MODAL_TASK, animate);
         } else {
             if (mActivity.isInState(LauncherState.OVERVIEW_MODAL_TASK)) {
-                mActivity.getStateManager().goToState(LauncherState.OVERVIEW);
+                mActivity.getStateManager().goToState(LauncherState.OVERVIEW, animate);
                 resetModalVisuals();
             }
         }
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index c8ef958..72467e6 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -104,6 +104,7 @@
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
+import android.view.RemoteAnimationTarget;
 import android.view.View;
 import android.view.ViewDebug;
 import android.view.ViewGroup;
@@ -180,6 +181,7 @@
 import com.android.quickstep.util.SplitAnimationTimings;
 import com.android.quickstep.util.SplitScreenBounds;
 import com.android.quickstep.util.SplitSelectStateController;
+import com.android.quickstep.util.SurfaceTransaction;
 import com.android.quickstep.util.SurfaceTransactionApplier;
 import com.android.quickstep.util.TaskViewSimulator;
 import com.android.quickstep.util.TaskVisualsChangeListener;
@@ -191,8 +193,6 @@
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
 import com.android.systemui.shared.system.PackageManagerWrapper;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
 import com.android.systemui.shared.system.TaskStackChangeListener;
 import com.android.systemui.shared.system.TaskStackChangeListeners;
 import com.android.wm.shell.pip.IPipAnimationListener;
@@ -1064,8 +1064,8 @@
         }
     }
 
-    public void launchSideTaskInLiveTileMode(int taskId, RemoteAnimationTargetCompat[] apps,
-            RemoteAnimationTargetCompat[] wallpaper, RemoteAnimationTargetCompat[] nonApps) {
+    public void launchSideTaskInLiveTileMode(int taskId, RemoteAnimationTarget[] apps,
+            RemoteAnimationTarget[] wallpaper, RemoteAnimationTarget[] nonApps) {
         AnimatorSet anim = new AnimatorSet();
         TaskView taskView = getTaskViewByTaskId(taskId);
         if (taskView == null || !isTaskViewVisible(taskView)) {
@@ -1077,14 +1077,15 @@
             appAnimator.setInterpolator(ACCEL_DEACCEL);
             appAnimator.addUpdateListener(valueAnimator -> {
                 float percent = valueAnimator.getAnimatedFraction();
-                SurfaceParams.Builder builder = new SurfaceParams.Builder(
-                        apps[apps.length - 1].leash);
+                SurfaceTransaction transaction = new SurfaceTransaction();
                 Matrix matrix = new Matrix();
                 matrix.postScale(percent, percent);
                 matrix.postTranslate(mActivity.getDeviceProfile().widthPx * (1 - percent) / 2,
                         mActivity.getDeviceProfile().heightPx * (1 - percent) / 2);
-                builder.withAlpha(percent).withMatrix(matrix);
-                surfaceApplier.scheduleApply(builder.build());
+                transaction.forSurface(apps[apps.length - 1].leash)
+                        .setAlpha(percent)
+                        .setMatrix(matrix);
+                surfaceApplier.scheduleApply(transaction);
             });
             anim.play(appAnimator);
             anim.addListener(new AnimatorListenerAdapter() {
@@ -1245,6 +1246,8 @@
         if (!mActivity.getDeviceProfile().isTablet) {
             mActionsView.updateDisabledFlags(OverviewActionsView.DISABLED_SCROLLING, true);
         }
+        InteractionJankMonitorWrapper.begin(/* view= */ this,
+                InteractionJankMonitorWrapper.CUJ_RECENTS_SCROLLING);
     }
 
     @Override
@@ -1258,6 +1261,7 @@
         if (getNextPage() > 0) {
             setSwipeDownShouldLaunchApp(true);
         }
+        InteractionJankMonitorWrapper.end(InteractionJankMonitorWrapper.CUJ_RECENTS_SCROLLING);
     }
 
     @Override
@@ -1760,7 +1764,7 @@
     private void onOrientationChanged() {
         // If overview is in modal state when rotate, reset it to overview state without running
         // animation.
-        setModalStateEnabled(false);
+        setModalStateEnabled(/* isModalState= */ false, /* animate= */ false);
         if (isSplitSelectionActive()) {
             onRotateInSplitSelectionState();
         }
@@ -5198,11 +5202,8 @@
         setInsets(mInsets);
     }
 
-    /**
-     * Enables or disables modal state for RecentsView
-     * @param isModalState
-     */
-    public void setModalStateEnabled(boolean isModalState) { }
+    /** Enables or disables modal state for RecentsView */
+    public abstract void setModalStateEnabled(boolean isModalState, boolean animate);
 
     public TaskOverlayFactory getTaskOverlayFactory() {
         return mTaskOverlayFactory;
diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuView.java b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
index 6815745..2c9afb4 100644
--- a/quickstep/src/com/android/quickstep/views/TaskMenuView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
@@ -23,7 +23,6 @@
 import android.animation.ObjectAnimator;
 import android.content.Context;
 import android.graphics.Outline;
-import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.drawable.ShapeDrawable;
 import android.graphics.drawable.shapes.RectShape;
@@ -32,7 +31,6 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewOutlineProvider;
-import android.view.ViewTreeObserver.OnScrollChangedListener;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
@@ -56,13 +54,12 @@
 /**
  * Contains options for a recent task when long-pressing its icon.
  */
-public class TaskMenuView extends AbstractFloatingView implements OnScrollChangedListener {
+public class TaskMenuView extends AbstractFloatingView {
 
     private static final Rect sTempRect = new Rect();
 
     private static final int REVEAL_OPEN_DURATION = 150;
     private static final int REVEAL_CLOSE_DURATION = 100;
-    private final float mTaskInsetMargin;
 
     private BaseDraggingActivity mActivity;
     private TextView mTaskName;
@@ -81,7 +78,6 @@
 
         mActivity = BaseDraggingActivity.fromContext(context);
         setClipToOutline(true);
-        mTaskInsetMargin = getResources().getDimension(R.dimen.task_card_margin);
     }
 
     @Override
@@ -129,33 +125,6 @@
         };
     }
 
-    private void setPosition(float x, float y, int overscrollShift) {
-        PagedOrientationHandler pagedOrientationHandler = mTaskView.getPagedOrientationHandler();
-        // Inset due to margin
-        PointF additionalInset = pagedOrientationHandler
-                .getAdditionalInsetForTaskMenu(mTaskInsetMargin);
-        DeviceProfile deviceProfile = mActivity.getDeviceProfile();
-        int taskTopMargin = deviceProfile.overviewTaskThumbnailTopMarginPx;
-
-        float adjustedY = y + taskTopMargin - additionalInset.y;
-        float adjustedX = x - additionalInset.x;
-        // Changing pivot to make computations easier
-        // NOTE: Changing the pivots means the rotated view gets rotated about the new pivots set,
-        // which would render the X and Y position set here incorrect
-        setPivotX(0);
-        if (deviceProfile.isTablet) {
-            // In tablet, set pivotY to original position without mThumbnailTopMargin adjustment.
-            setPivotY(-taskTopMargin);
-        } else {
-            setPivotY(0);
-        }
-        setRotation(pagedOrientationHandler.getDegreesRotated());
-        setX(pagedOrientationHandler.getTaskMenuX(adjustedX,
-                mTaskContainer.getThumbnailView(), overscrollShift, deviceProfile));
-        setY(pagedOrientationHandler.getTaskMenuY(
-                adjustedY, mTaskContainer.getThumbnailView(), overscrollShift));
-    }
-
     public void onRotationChanged() {
         if (mOpenCloseAnimator != null && mOpenCloseAnimator.isRunning()) {
             mOpenCloseAnimator.end();
@@ -187,17 +156,9 @@
             return false;
         }
         post(this::animateOpen);
-        ((RecentsView) mActivity.getOverviewPanel()).addOnScrollChangedListener(this);
         return true;
     }
 
-    @Override
-    public void onScrollChanged() {
-        RecentsView rv = mActivity.getOverviewPanel();
-        setPosition(mTaskView.getX() - rv.getScrollX(), mTaskView.getY() - rv.getScrollY(),
-                rv.getOverScrollShift());
-    }
-
     /** @return true if successfully able to populate task view menu, false otherwise */
     private boolean populateAndLayoutMenu() {
         if (mTaskContainer.getTask().icon == null) {
@@ -234,18 +195,18 @@
         RecentsView recentsView = mActivity.getOverviewPanel();
         PagedOrientationHandler orientationHandler = recentsView.getPagedOrientationHandler();
         measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
-        orientationHandler.setTaskMenuAroundTaskView(this, mTaskInsetMargin);
 
         // Get Position
         DeviceProfile deviceProfile = mActivity.getDeviceProfile();
-        mActivity.getDragLayer().getDescendantRectRelativeToSelf(mTaskView, sTempRect);
+        mActivity.getDragLayer().getDescendantRectRelativeToSelf(taskContainer.getThumbnailView(),
+                sTempRect);
         Rect insets = mActivity.getDragLayer().getInsets();
         BaseDragLayer.LayoutParams params = (BaseDragLayer.LayoutParams) getLayoutParams();
         int padding = getResources()
                 .getDimensionPixelSize(R.dimen.task_menu_vertical_padding);
         params.width = orientationHandler
                 .getTaskMenuWidth(taskContainer.getThumbnailView(),
-                        deviceProfile) - (2 * padding);
+                        deviceProfile, taskContainer.getStagePosition()) - (2 * padding);
         // Gravity set to Left instead of Start as sTempRect.left measures Left distance not Start
         params.gravity = Gravity.LEFT;
         setLayoutParams(params);
@@ -260,7 +221,22 @@
 
         orientationHandler.setTaskOptionsMenuLayoutOrientation(
                 deviceProfile, mOptionLayout, dividerSpacing, divider);
-        setPosition(sTempRect.left - insets.left, sTempRect.top - insets.top, 0);
+        float thumbnailAlignedX = sTempRect.left - insets.left;
+        float thumbnailAlignedY = sTempRect.top - insets.top;
+        // Changing pivot to make computations easier
+        // NOTE: Changing the pivots means the rotated view gets rotated about the new pivots set,
+        // which would render the X and Y position set here incorrect
+        setPivotX(0);
+        setPivotY(0);
+        setRotation(orientationHandler.getDegreesRotated());
+
+        // Margin that insets the menuView inside the taskView
+        float taskInsetMargin = getResources().getDimension(R.dimen.task_card_margin);
+        setTranslationX(orientationHandler.getTaskMenuX(thumbnailAlignedX,
+                mTaskContainer.getThumbnailView(), deviceProfile, taskInsetMargin));
+        setTranslationY(orientationHandler.getTaskMenuY(
+                thumbnailAlignedY, mTaskContainer.getThumbnailView(),
+                mTaskContainer.getStagePosition(), this, taskInsetMargin));
     }
 
     private void animateOpen() {
@@ -306,7 +282,6 @@
     private void closeComplete() {
         mIsOpen = false;
         mActivity.getDragLayer().removeView(this);
-        ((RecentsView) mActivity.getOverviewPanel()).removeOnScrollChangedListener(this);
     }
 
     private RoundedRectRevealOutlineProvider createOpenCloseOutlineProvider() {
diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt b/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt
index b586ac3..bdc0585 100644
--- a/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt
@@ -37,7 +37,6 @@
 import com.android.launcher3.popup.RoundedArrowDrawable
 import com.android.launcher3.popup.SystemShortcut
 import com.android.launcher3.util.Themes
-import com.android.quickstep.KtR
 import com.android.quickstep.TaskOverlayFactory
 import com.android.quickstep.views.TaskView.TaskIdAttributeContainer
 
@@ -53,9 +52,9 @@
                 .fromContext<BaseDraggingActivity>(taskContainer.taskView.context)
             val taskMenuViewWithArrow = activity.layoutInflater
                 .inflate(
-                    KtR.layout.task_menu_with_arrow,
-                    activity.dragLayer,
-                    false
+                        R.layout.task_menu_with_arrow,
+                        activity.dragLayer,
+                        false
                 ) as TaskMenuViewWithArrow<*>
 
             return taskMenuViewWithArrow.populateAndShowForTask(taskContainer, alignSecondRow)
@@ -93,7 +92,7 @@
     private var optionMeasuredHeight = 0
     private val arrowHorizontalPadding: Int
         get() = if (taskView.isFocusedTask)
-            resources.getDimensionPixelSize(KtR.dimen.task_menu_horizontal_padding)
+            resources.getDimensionPixelSize(R.dimen.task_menu_horizontal_padding)
         else
             0
 
@@ -119,7 +118,7 @@
 
     override fun onFinishInflate() {
         super.onFinishInflate()
-        optionLayout = findViewById(KtR.id.menu_option_layout)
+        optionLayout = findViewById(R.id.menu_option_layout)
     }
 
     private fun populateAndShowForTask(
@@ -170,7 +169,7 @@
         // Add the spaces between items
         val divider = ShapeDrawable(RectShape())
         divider.paint.color = resources.getColor(android.R.color.transparent)
-        val dividerSpacing = resources.getDimension(KtR.dimen.task_menu_spacing).toInt()
+        val dividerSpacing = resources.getDimension(R.dimen.task_menu_spacing).toInt()
         optionLayout.showDividers = SHOW_DIVIDER_MIDDLE
 
         // Set the orientation, which makes the menu show
@@ -187,7 +186,7 @@
 
     private fun addMenuOption(menuOption: SystemShortcut<*>) {
         val menuOptionView = mActivityContext.layoutInflater.inflate(
-            KtR.layout.task_view_menu_option, this, false
+                R.layout.task_view_menu_option, this, false
         ) as LinearLayout
         menuOption.setIconAndLabelFor(
             menuOptionView.findViewById(R.id.icon),
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 35f0f5d..6eb77a4 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -53,6 +53,7 @@
 import android.util.Log;
 import android.view.Display;
 import android.view.MotionEvent;
+import android.view.RemoteAnimationTarget;
 import android.view.TouchDelegate;
 import android.view.View;
 import android.view.ViewGroup;
@@ -101,7 +102,6 @@
 import com.android.systemui.shared.recents.utilities.PreviewPositionHelper;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
 import java.lang.annotation.Retention;
 import java.util.Arrays;
@@ -739,14 +739,14 @@
             } else {
                 TransformParams topLeftParams = remoteTargetHandles[0].getTransformParams();
                 TransformParams rightBottomParams = remoteTargetHandles[1].getTransformParams();
-                RemoteAnimationTargetCompat[] apps = Stream.concat(
+                RemoteAnimationTarget[] apps = Stream.concat(
                         Arrays.stream(topLeftParams.getTargetSet().apps),
                         Arrays.stream(rightBottomParams.getTargetSet().apps))
-                        .toArray(RemoteAnimationTargetCompat[]::new);
-                RemoteAnimationTargetCompat[] wallpapers = Stream.concat(
+                        .toArray(RemoteAnimationTarget[]::new);
+                RemoteAnimationTarget[] wallpapers = Stream.concat(
                         Arrays.stream(topLeftParams.getTargetSet().wallpapers),
                         Arrays.stream(rightBottomParams.getTargetSet().wallpapers))
-                        .toArray(RemoteAnimationTargetCompat[]::new);
+                        .toArray(RemoteAnimationTarget[]::new);
                 targets = new RemoteAnimationTargets(apps, wallpapers,
                         topLeftParams.getTargetSet().nonApps,
                         topLeftParams.getTargetSet().targetMode);
diff --git a/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java b/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
index c1b3beb..b903691 100644
--- a/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
+++ b/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
@@ -36,7 +36,6 @@
 import android.content.ComponentName;
 import android.os.UserHandle;
 import android.text.TextUtils;
-import android.util.Log;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
@@ -111,7 +110,6 @@
         doReturn(allWidgets).when(manager).getInstalledProvidersForProfile(eq(myUserHandle()));
         doAnswer(i -> {
             String pkg = i.getArgument(0);
-            Log.e("Hello", "Getting v " + pkg);
             return TextUtils.isEmpty(pkg) ? allWidgets : allWidgets.stream()
                     .filter(a -> pkg.equals(a.provider.getPackageName()))
                     .collect(Collectors.toList());
diff --git a/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java b/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
index 190b002..83602be 100644
--- a/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
+++ b/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
@@ -23,8 +23,8 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.util.ArrayMap;
+import android.view.RemoteAnimationTarget;
 import android.view.Surface;
-import android.view.SurfaceControl;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
@@ -42,8 +42,7 @@
 import com.android.launcher3.util.window.WindowManagerProxy;
 import com.android.quickstep.FallbackActivityInterface;
 import com.android.quickstep.SystemUiProxy;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
+import com.android.quickstep.util.SurfaceTransaction.MockProperties;
 
 import org.hamcrest.Description;
 import org.hamcrest.TypeSafeMatcher;
@@ -207,17 +206,21 @@
         }
 
         @Override
-        public SurfaceParams[] createSurfaceParams(BuilderProxy proxy) {
-            SurfaceParams.Builder builder = new SurfaceParams.Builder((SurfaceControl) null);
-            proxy.onBuildTargetParams(builder, mock(RemoteAnimationTargetCompat.class), this);
-            return new SurfaceParams[] {builder.build()};
+        public SurfaceTransaction createSurfaceParams(BuilderProxy proxy) {
+            RecordingSurfaceTransaction transaction = new RecordingSurfaceTransaction();
+            proxy.onBuildTargetParams(
+                    transaction.mockProperties, mock(RemoteAnimationTarget.class), this);
+            return transaction;
         }
 
         @Override
-        public void applySurfaceParams(SurfaceParams[] params) {
+        public void applySurfaceParams(SurfaceTransaction params) {
+            Assert.assertTrue(params instanceof RecordingSurfaceTransaction);
+            MockProperties p = ((RecordingSurfaceTransaction) params).mockProperties;
+
             // Verify that the task position remains the same
             RectF newAppBounds = new RectF(mAppBounds);
-            params[0].matrix.mapRect(newAppBounds);
+            p.matrix.mapRect(newAppBounds);
             Assert.assertThat(newAppBounds, new AlmostSame(mAppBounds));
 
             System.err.println("Bounds mapped: " + mAppBounds + " => " + newAppBounds);
diff --git a/res/drawable/ic_all_apps_button.xml b/res/drawable/ic_all_apps_button.xml
index 5770d3c..7de390a 100644
--- a/res/drawable/ic_all_apps_button.xml
+++ b/res/drawable/ic_all_apps_button.xml
@@ -18,27 +18,29 @@
     android:width="80dp"
     android:height="80dp"
     android:viewportWidth="80"
-    android:viewportHeight="80"
-    android:theme="@style/AllAppsTheme">
-  <path
-      android:pathData="M40,0.5L40,0.5c21.8,0 39.5,17.7 39.5,39.5l0,0c0,21.8 -17.7,39.5 -39.5,39.5l0,0C18.2,79.5 0.5,61.8 0.5,40l0,0C0.5,18.2 18.2,0.5 40,0.5z"
-      android:fillColor="?attr/allAppsButtonBgColor"/>
-  <path
-      android:pathData="M26.8,32.1m-5.3,0a5.3,5.3 0,1 1,10.6 0a5.3,5.3 0,1 1,-10.6 0"
-      android:fillColor="?attr/allAppsButtonColor1"/>
-  <path
-      android:pathData="M26.8,47.9m-5.3,0a5.3,5.3 0,1 1,10.6 0a5.3,5.3 0,1 1,-10.6 0"
-      android:fillColor="?attr/allAppsButtonColor2"/>
-  <path
-      android:pathData="M40,32.1m-5.3,0a5.3,5.3 0,1 1,10.6 0a5.3,5.3 0,1 1,-10.6 0"
-      android:fillColor="?attr/allAppsButtonColor3"/>
-  <path
-      android:pathData="M40,47.9m-5.3,0a5.3,5.3 0,1 1,10.6 0a5.3,5.3 0,1 1,-10.6 0"
-      android:fillColor="?attr/allAppsButtonColor2"/>
-  <path
-      android:pathData="M53.2,32.1m-5.3,0a5.3,5.3 0,1 1,10.6 0a5.3,5.3 0,1 1,-10.6 0"
-      android:fillColor="?attr/allAppsButtonColor4"/>
-  <path
-      android:pathData="M53.2,47.9m-5.3,0a5.3,5.3 0,1 1,10.6 0a5.3,5.3 0,1 1,-10.6 0"
-      android:fillColor="?attr/allAppsButtonColor2"/>
+    android:viewportHeight="80">
+  <group
+      android:pivotY="40"
+      android:pivotX="40"
+      android:scaleX=".88"
+      android:scaleY=".88">
+    <path
+        android:pathData="M26.8,32.1m-5.3,0a5.3,5.3 0,1 1,10.6 0a5.3,5.3 0,1 1,-10.6 0"
+        android:fillColor="@color/all_apps_button_color_1"/>
+    <path
+        android:pathData="M26.8,47.9m-5.3,0a5.3,5.3 0,1 1,10.6 0a5.3,5.3 0,1 1,-10.6 0"
+        android:fillColor="@color/all_apps_button_color_2"/>
+    <path
+        android:pathData="M40,32.1m-5.3,0a5.3,5.3 0,1 1,10.6 0a5.3,5.3 0,1 1,-10.6 0"
+        android:fillColor="@color/all_apps_button_color_3"/>
+    <path
+        android:pathData="M40,47.9m-5.3,0a5.3,5.3 0,1 1,10.6 0a5.3,5.3 0,1 1,-10.6 0"
+        android:fillColor="@color/all_apps_button_color_2"/>
+    <path
+        android:pathData="M53.2,32.1m-5.3,0a5.3,5.3 0,1 1,10.6 0a5.3,5.3 0,1 1,-10.6 0"
+        android:fillColor="@color/all_apps_button_color_4"/>
+    <path
+        android:pathData="M53.2,47.9m-5.3,0a5.3,5.3 0,1 1,10.6 0a5.3,5.3 0,1 1,-10.6 0"
+        android:fillColor="@color/all_apps_button_color_2"/>
+  </group>
 </vector>
diff --git a/res/layout/page_indicator_dots.xml b/res/layout/page_indicator_dots.xml
new file mode 100644
index 0000000..d5fe51e
--- /dev/null
+++ b/res/layout/page_indicator_dots.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<com.android.launcher3.pageindicators.PageIndicatorDots xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/page_indicator"
+    android:layout_width="match_parent"
+    android:layout_height="@dimen/workspace_page_indicator_height"
+    android:layout_gravity="bottom | center_horizontal"
+    android:theme="@style/HomeScreenElementTheme" />
\ No newline at end of file
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 8623414..283c793 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -56,12 +56,6 @@
     <attr name="preloadIconAccentColor" format="color" />
     <attr name="preloadIconBackgroundColor" format="color" />
 
-    <attr name="allAppsButtonBgColor" format="color" />
-    <attr name="allAppsButtonColor1" format="color" />
-    <attr name="allAppsButtonColor2" format="color" />
-    <attr name="allAppsButtonColor3" format="color" />
-    <attr name="allAppsButtonColor4" format="color" />
-
     <!-- BubbleTextView specific attributes. -->
     <declare-styleable name="BubbleTextView">
         <attr name="layoutHorizontal" format="boolean" />
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 90553a1..d0be420 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -207,14 +207,6 @@
         <item name="android:importantForAccessibility">no</item>
     </style>
 
-    <style name="AllAppsButtonTheme">
-        <item name="allAppsButtonBgColor">@color/all_apps_button_bg_color</item>
-        <item name="allAppsButtonColor1">@color/all_apps_button_color_1</item>
-        <item name="allAppsButtonColor2">@color/all_apps_button_color_2</item>
-        <item name="allAppsButtonColor3">@color/all_apps_button_color_3</item>
-        <item name="allAppsButtonColor4">@color/all_apps_button_color_4</item>
-    </style>
-
     <style name="AllAppsTheme">
         <item name="disabledIconAlpha">.54</item>
     </style>
diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java
index 5ee6fce..73acd87 100644
--- a/src/com/android/launcher3/AbstractFloatingView.java
+++ b/src/com/android/launcher3/AbstractFloatingView.java
@@ -94,6 +94,7 @@
     public static final int TYPE_TASKBAR_EDUCATION_DIALOG = 1 << 16;
     public static final int TYPE_TASKBAR_ALL_APPS = 1 << 17;
     public static final int TYPE_ADD_TO_HOME_CONFIRMATION = 1 << 18;
+    public static final int TYPE_TASKBAR_OVERLAY_PROXY = 1 << 19;
 
     public static final int TYPE_ALL = TYPE_FOLDER | TYPE_ACTION_POPUP
             | TYPE_WIDGETS_BOTTOM_SHEET | TYPE_WIDGET_RESIZE_FRAME | TYPE_WIDGETS_FULL_SHEET
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index 83ff084..9bdc822 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -196,8 +196,7 @@
 
     @Override
     protected void onResume() {
-        addActivityFlags(ACTIVITY_STATE_RESUMED | ACTIVITY_STATE_USER_ACTIVE);
-        removeActivityFlags(ACTIVITY_STATE_USER_WILL_BE_ACTIVE);
+        setResumed();
         super.onResume();
     }
 
@@ -228,7 +227,7 @@
 
     @Override
     protected void onPause() {
-        removeActivityFlags(ACTIVITY_STATE_RESUMED | ACTIVITY_STATE_DEFERRED_RESUMED);
+        setPaused();
         super.onPause();
 
         // Reset the overridden sysui flags used for the task-swipe launch animation, we do this
@@ -260,6 +259,21 @@
         return (mActivityFlags & ACTIVITY_STATE_RESUMED) != 0;
     }
 
+    /**
+     * Sets the activity to appear as paused.
+     */
+    public void setPaused() {
+        removeActivityFlags(ACTIVITY_STATE_RESUMED | ACTIVITY_STATE_DEFERRED_RESUMED);
+    }
+
+    /**
+     * Sets the activity to appear as resumed.
+     */
+    public void setResumed() {
+        addActivityFlags(ACTIVITY_STATE_RESUMED | ACTIVITY_STATE_USER_ACTIVE);
+        removeActivityFlags(ACTIVITY_STATE_USER_WILL_BE_ACTIVE);
+    }
+
     public boolean isUserActive() {
         return (mActivityFlags & ACTIVITY_STATE_USER_ACTIVE) != 0;
     }
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index c1df403..75d7b6b 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -19,6 +19,7 @@
 import static android.animation.ValueAnimator.areAnimatorsEnabled;
 
 import static com.android.launcher3.anim.Interpolators.DEACCEL_1_5;
+import static com.android.launcher3.config.FeatureFlags.SHOW_HOME_GARDENING;
 import static com.android.launcher3.dragndrop.DraggableView.DRAGGABLE_ICON;
 import static com.android.launcher3.icons.IconNormalizer.ICON_VISIBLE_AREA_FACTOR;
 
@@ -54,7 +55,6 @@
 import android.view.accessibility.AccessibilityEvent;
 
 import androidx.annotation.IntDef;
-import androidx.annotation.Nullable;
 import androidx.core.graphics.ColorUtils;
 import androidx.core.view.ViewCompat;
 
@@ -552,7 +552,9 @@
     public void setSpringLoadedProgress(float progress) {
         if (Float.compare(progress, mSpringLoadedProgress) != 0) {
             mSpringLoadedProgress = progress;
-            updateBgAlpha();
+            if (!SHOW_HOME_GARDENING.get()) {
+                updateBgAlpha();
+            }
             setGridAlpha(progress);
         }
     }
@@ -577,7 +579,9 @@
     public void setScrollProgress(float progress) {
         if (Float.compare(Math.abs(progress), mScrollProgress) != 0) {
             mScrollProgress = Math.abs(progress);
-            updateBgAlpha();
+            if (!SHOW_HOME_GARDENING.get()) {
+                updateBgAlpha();
+            }
         }
     }
 
@@ -616,7 +620,7 @@
             }
         }
 
-        if (mVisualizeDropLocation) {
+        if (mVisualizeDropLocation && !SHOW_HOME_GARDENING.get()) {
             for (int i = 0; i < mDragOutlines.length; i++) {
                 final float alpha = mDragOutlineAlphas[i];
                 if (alpha <= 0) continue;
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 7881a26..1c26f04 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -218,6 +218,9 @@
     public int overviewRowSpacing;
     public int overviewGridSideMargin;
 
+    // Split staging
+    public int splitPlaceholderInset;
+
     // Widgets
     private final ViewScaleProvider mViewScaleProvider;
 
@@ -459,6 +462,8 @@
         overviewRowSpacing = res.getDimensionPixelSize(R.dimen.overview_grid_row_spacing);
         overviewGridSideMargin = res.getDimensionPixelSize(R.dimen.overview_grid_side_margin);
 
+        splitPlaceholderInset = res.getDimensionPixelSize(R.dimen.split_placeholder_inset);
+
         // Calculate all of the remaining variables.
         extraSpace = updateAvailableDimensions(res);
 
diff --git a/src/com/android/launcher3/FastScrollRecyclerView.java b/src/com/android/launcher3/FastScrollRecyclerView.java
index 747b755..2f927d3 100644
--- a/src/com/android/launcher3/FastScrollRecyclerView.java
+++ b/src/com/android/launcher3/FastScrollRecyclerView.java
@@ -24,7 +24,6 @@
 import android.view.accessibility.AccessibilityNodeInfo;
 
 import androidx.annotation.Nullable;
-import androidx.recyclerview.widget.LinearLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
 
 import com.android.launcher3.compat.AccessibilityManagerCompat;
@@ -92,8 +91,7 @@
     protected int getAvailableScrollHeight() {
         // AvailableScrollHeight = Total height of the all items - first page height
         int firstPageHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();
-        int totalHeightOfAllItems = getItemsHeight(/* untilIndex= */ getAdapter().getItemCount());
-        int availableScrollHeight = totalHeightOfAllItems - firstPageHeight;
+        int availableScrollHeight = computeVerticalScrollRange() - firstPageHeight;
         return Math.max(0, availableScrollHeight);
     }
 
@@ -146,10 +144,7 @@
 
         // IF scroller is at the very top OR there is no scroll bar because there is probably not
         // enough items to scroll, THEN it's okay for the container to be pulled down.
-        if (getCurrentScrollY() == 0) {
-            return true;
-        }
-        return getAdapter() == null || getAdapter().getItemCount() == 0;
+        return computeVerticalScrollOffset() == 0;
     }
 
     /**
@@ -160,53 +155,6 @@
     }
 
     /**
-     * @return the scroll top of this recycler view.
-     */
-    public int getCurrentScrollY() {
-        Adapter adapter = getAdapter();
-        if (adapter == null) {
-            return -1;
-        }
-        if (adapter.getItemCount() == 0 || getChildCount() == 0) {
-            return -1;
-        }
-
-        int itemPosition = NO_POSITION;
-        View child = null;
-
-        LayoutManager layoutManager = getLayoutManager();
-        if (layoutManager instanceof LinearLayoutManager) {
-            // Use the LayoutManager as the source of truth for visible positions. During
-            // animations, the view group child may not correspond to the visible views that appear
-            // at the top.
-            itemPosition = ((LinearLayoutManager) layoutManager).findFirstVisibleItemPosition();
-            child = layoutManager.findViewByPosition(itemPosition);
-        }
-
-        if (child == null) {
-            // If the layout manager returns null for any reason, which can happen before layout
-            // has occurred for the position, then look at the child of this view as a ViewGroup.
-            child = getChildAt(0);
-            itemPosition = getChildAdapterPosition(child);
-        }
-        if (itemPosition == NO_POSITION) {
-            return -1;
-        }
-        return getPaddingTop() + getItemsHeight(itemPosition)
-                - layoutManager.getDecoratedTop(child);
-    }
-
-    /**
-     * Returns the sum of the height, in pixels, of this list adapter's items from index
-     * 0 (inclusive) until {@code untilIndex} (exclusive). If untilIndex is same as the itemCount,
-     * it returns the full height of all the items.
-     *
-     * <p>If the untilIndex is larger than the total number of items in this adapter, returns the
-     * sum of all items' height.
-     */
-    protected abstract int getItemsHeight(int untilIndex);
-
-    /**
      * Maps the touch (from 0..1) to the adapter position that should be visible.
      * <p>Override in each subclass of this base class.
      */
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 5a6b3ec..07d0f55 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -43,6 +43,7 @@
 import static com.android.launcher3.Utilities.postAsyncCallback;
 import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.getSupportedActions;
 import static com.android.launcher3.anim.Interpolators.EMPHASIZED;
+import static com.android.launcher3.config.FeatureFlags.SHOW_DELIGHTFUL_PAGINATION;
 import static com.android.launcher3.logging.StatsLogManager.EventEnum;
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
@@ -97,6 +98,7 @@
 import android.os.UserHandle;
 import android.text.TextUtils;
 import android.text.method.TextKeyListener;
+import android.util.AttributeSet;
 import android.util.FloatProperty;
 import android.util.Log;
 import android.util.SparseArray;
@@ -163,6 +165,7 @@
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.notification.NotificationListener;
+import com.android.launcher3.pageindicators.WorkspacePageIndicator;
 import com.android.launcher3.pm.PinRequestHelper;
 import com.android.launcher3.pm.UserCache;
 import com.android.launcher3.popup.ArrowPopup;
@@ -1291,6 +1294,16 @@
         mAllAppsController.setupViews(mScrimView, mAppsView);
     }
 
+    @Override
+    public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
+        if (SHOW_DELIGHTFUL_PAGINATION.get()
+                && WorkspacePageIndicator.class.getName().equals(name)) {
+            return LayoutInflater.from(context).inflate(R.layout.page_indicator_dots,
+                    (ViewGroup) parent, false);
+        }
+        return super.onCreateView(parent, name, context, attrs);
+    }
+
     /**
      * Creates a view representing a shortcut.
      *
@@ -2790,7 +2803,7 @@
             View v = getFirstMatch(Collections.singletonList(activeRecyclerView),
                     preferredItem, packageAndUserAndApp);
 
-            if (v != null && activeRecyclerView.getCurrentScrollY() > 0) {
+            if (v != null && activeRecyclerView.computeVerticalScrollOffset() > 0) {
                 RectF locationBounds = new RectF();
                 FloatingIconView.getLocationBoundsForView(this, v, false, locationBounds,
                         new Rect());
diff --git a/src/com/android/launcher3/LauncherAnimUtils.java b/src/com/android/launcher3/LauncherAnimUtils.java
index b858d1a..4e80d41 100644
--- a/src/com/android/launcher3/LauncherAnimUtils.java
+++ b/src/com/android/launcher3/LauncherAnimUtils.java
@@ -218,4 +218,32 @@
             }
         };
     }
+
+    /**
+     * A property that updates the specified property within a given range of values (ie. even if
+     * the animator goes beyond 0..1, the interpolated value will still be bounded).
+     * @param <T> the specified property
+     */
+    public static class ClampedProperty<T> extends FloatProperty<T> {
+        private final FloatProperty<T> mProperty;
+        private final float mMinValue;
+        private final float mMaxValue;
+
+        public ClampedProperty(FloatProperty<T> property, float minValue, float maxValue) {
+            super(property.getName() + "Clamped");
+            mProperty = property;
+            mMinValue = minValue;
+            mMaxValue = maxValue;
+        }
+
+        @Override
+        public void setValue(T t, float v) {
+            mProperty.set(t, Utilities.boundToRange(v, mMinValue, mMaxValue));
+        }
+
+        @Override
+        public Float get(T t) {
+            return mProperty.get(t);
+        }
+    }
 }
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 6aff83e..fe8b364 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -53,12 +53,14 @@
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.SparseArray;
+import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
 import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.FrameLayout;
 import android.widget.Toast;
 
 import androidx.annotation.Nullable;
@@ -107,7 +109,6 @@
 import com.android.launcher3.util.RunnableList;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.util.WallpaperOffsetInterpolator;
-import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.widget.LauncherAppWidgetHost;
 import com.android.launcher3.widget.LauncherAppWidgetHost.ProviderChangedListener;
 import com.android.launcher3.widget.LauncherAppWidgetHostView;
@@ -223,8 +224,8 @@
     // Variables relating to touch disambiguation (scrolling workspace vs. scrolling a widget)
     private float mXDown;
     private float mYDown;
-    private View mQsb;
-    private boolean mIsEventOverQsb;
+    private View mFirstPagePinnedItem;
+    private boolean mIsEventOverFirstPagePinnedItem;
 
     final static float START_DAMPING_TOUCH_SLOP_ANGLE = (float) Math.PI / 6;
     final static float MAX_SWIPE_ANGLE = (float) Math.PI / 3;
@@ -326,6 +327,26 @@
 
         updateCellLayoutPadding();
         updateWorkspaceWidgetsSizes();
+        setPageIndicatorInset();
+    }
+
+    private void setPageIndicatorInset() {
+        DeviceProfile grid = mLauncher.getDeviceProfile();
+
+        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mPageIndicator.getLayoutParams();
+
+        // Set insets for page indicator
+        Rect padding = grid.workspacePadding;
+        if (grid.isVerticalBarLayout()) {
+            lp.leftMargin = padding.left + grid.workspaceCellPaddingXPx;
+            lp.rightMargin = padding.right + grid.workspaceCellPaddingXPx;
+            lp.bottomMargin = padding.bottom;
+        } else {
+            lp.leftMargin = lp.rightMargin = 0;
+            lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
+            lp.bottomMargin = grid.hotseatBarSizePx;
+        }
+        mPageIndicator.setLayoutParams(lp);
     }
 
     private void updateCellLayoutPadding() {
@@ -554,20 +575,22 @@
 
         // Add the first page
         CellLayout firstPage = insertNewWorkspaceScreen(Workspace.FIRST_SCREEN_ID, getChildCount());
-        // Always add a QSB on the first screen.
-        if (mQsb == null) {
-            // In transposed layout, we add the QSB in the Grid. As workspace does not touch the
-            // edges, we do not need a full width QSB.
-            mQsb = LayoutInflater.from(getContext())
+        // Always add a first page pinned widget on the first screen.
+        if (mFirstPagePinnedItem == null) {
+            // In transposed layout, we add the first page pinned widget in the Grid.
+            // As workspace does not touch the edges, we do not need a full
+            // width first page pinned widget.
+            mFirstPagePinnedItem = LayoutInflater.from(getContext())
                     .inflate(R.layout.search_container_workspace, firstPage, false);
         }
 
         int cellHSpan = mLauncher.getDeviceProfile().inv.numSearchContainerColumns;
         CellLayoutLayoutParams lp = new CellLayoutLayoutParams(0, 0, cellHSpan, 1);
         lp.canReorder = false;
-        if (!firstPage.addViewToCellLayout(mQsb, 0, R.id.search_container_workspace, lp, true)) {
+        if (!firstPage.addViewToCellLayout(
+                mFirstPagePinnedItem, 0, R.id.search_container_workspace, lp, true)) {
             Log.e(TAG, "Failed to add to item at (0, 0) to CellLayout");
-            mQsb = null;
+            mFirstPagePinnedItem = null;
         }
     }
 
@@ -576,9 +599,9 @@
         // transition animations competing with us changing the scroll when we add pages
         disableLayoutTransitions();
 
-        // Recycle the QSB widget
-        if (mQsb != null) {
-            ((ViewGroup) mQsb.getParent()).removeView(mQsb);
+        // Recycle the first page pinned widget
+        if (mFirstPagePinnedItem != null) {
+            ((ViewGroup) mFirstPagePinnedItem.getParent()).removeView(mFirstPagePinnedItem);
         }
 
         // Remove the pages and clear the screen models
@@ -898,6 +921,10 @@
         return mScreenOrder;
     }
 
+    protected View getFirstPagePinnedItem() {
+        return mFirstPagePinnedItem;
+    }
+
     /**
      * Returns the screen ID of a page that is shown together with the given page screen ID when the
      * two panel UI is enabled.
@@ -1049,20 +1076,22 @@
 
         mXDown = ev.getX();
         mYDown = ev.getY();
-        if (mQsb != null) {
+        if (mFirstPagePinnedItem != null) {
             mTempFXY[0] = mXDown + getScrollX();
             mTempFXY[1] = mYDown + getScrollY();
-            Utilities.mapCoordInSelfToDescendant(mQsb, this, mTempFXY);
-            mIsEventOverQsb = mQsb.getLeft() <= mTempFXY[0] && mQsb.getRight() >= mTempFXY[0]
-                    && mQsb.getTop() <= mTempFXY[1] && mQsb.getBottom() >= mTempFXY[1];
+            Utilities.mapCoordInSelfToDescendant(mFirstPagePinnedItem, this, mTempFXY);
+            mIsEventOverFirstPagePinnedItem = mFirstPagePinnedItem.getLeft() <= mTempFXY[0]
+                    && mFirstPagePinnedItem.getRight() >= mTempFXY[0]
+                    && mFirstPagePinnedItem.getTop() <= mTempFXY[1]
+                    && mFirstPagePinnedItem.getBottom() >= mTempFXY[1];
         } else {
-            mIsEventOverQsb = false;
+            mIsEventOverFirstPagePinnedItem = false;
         }
     }
 
     @Override
     protected void determineScrollingStart(MotionEvent ev) {
-        if (!isFinishedSwitchingState() || mIsEventOverQsb) return;
+        if (!isFinishedSwitchingState() || mIsEventOverFirstPagePinnedItem) return;
 
         float deltaX = ev.getX() - mXDown;
         float absDeltaX = Math.abs(deltaX);
@@ -2513,10 +2542,10 @@
     }
 
     private boolean isDragObjectOverSmartSpace(DragObject dragObject) {
-        if (mQsb == null) {
+        if (mFirstPagePinnedItem == null) {
             return false;
         }
-        getViewBoundsRelativeToWorkspace(mQsb, mTempRect);
+        getViewBoundsRelativeToWorkspace(mFirstPagePinnedItem, mTempRect);
         return mTempRect.contains(dragObject.x, dragObject.y);
     }
 
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index a991c2f..264c0a7 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -29,11 +29,13 @@
 import static com.android.launcher3.LauncherState.HINT_STATE;
 import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
 import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.LauncherState.SPRING_LOADED;
 import static com.android.launcher3.LauncherState.WORKSPACE_PAGE_INDICATOR;
 import static com.android.launcher3.anim.Interpolators.ACCEL_2;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.anim.Interpolators.ZOOM_OUT;
 import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
+import static com.android.launcher3.config.FeatureFlags.SHOW_HOME_GARDENING;
 import static com.android.launcher3.graphics.Scrim.SCRIM_PROGRESS;
 import static com.android.launcher3.graphics.SysUiScrim.SYSUI_PROGRESS;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_FADE;
@@ -69,6 +71,8 @@
  */
 public class WorkspaceStateTransitionAnimation {
 
+    private static final float FIRST_PAGE_PINNED_WIDGET_DISABLED_ALPHA = 0.3f;
+
     private static final FloatProperty<Workspace<?>> WORKSPACE_SCALE_PROPERTY =
             WORKSPACE_SCALE_PROPERTY_FACTORY.get(SCALE_INDEX_WORKSPACE_STATE);
 
@@ -155,6 +159,18 @@
         float hotseatIconsAlpha = (elements & HOTSEAT_ICONS) != 0 ? 1 : 0;
         propertySetter.setViewAlpha(hotseat, hotseatIconsAlpha, hotseatFadeInterpolator);
 
+        if (SHOW_HOME_GARDENING.get()) {
+            propertySetter.setViewAlpha(
+                    mWorkspace.getFirstPagePinnedItem(),
+                    state == SPRING_LOADED ? FIRST_PAGE_PINNED_WIDGET_DISABLED_ALPHA : 1,
+                    workspaceFadeInterpolator);
+            propertySetter.addEndListener(success -> {
+                if (success) {
+                    mWorkspace.getFirstPagePinnedItem().setClickable(state != SPRING_LOADED);
+                }
+            });
+        }
+
         // Update the accessibility flags for hotseat based on launcher state.
         hotseat.setImportantForAccessibility(
                 state.hasFlag(FLAG_HOTSEAT_INACCESSIBLE)
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index fe0230a..368a373 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -26,7 +26,9 @@
 import androidx.core.view.accessibility.AccessibilityRecordCompat;
 import androidx.recyclerview.widget.GridLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.RecyclerView.Adapter;
 
+import com.android.launcher3.util.ScrollableLayoutManager;
 import com.android.launcher3.views.ActivityContext;
 
 import java.util.List;
@@ -66,10 +68,10 @@
     /**
      * A subclass of GridLayoutManager that overrides accessibility values during app search.
      */
-    public class AppsGridLayoutManager extends GridLayoutManager {
+    public class AppsGridLayoutManager extends ScrollableLayoutManager {
 
         public AppsGridLayoutManager(Context context) {
-            super(context, 1, GridLayoutManager.VERTICAL, false);
+            super(context);
         }
 
         @Override
@@ -129,6 +131,15 @@
             }
             return extraRows;
         }
+
+        @Override
+        protected int incrementTotalHeight(Adapter adapter, int position, int heightUntilLastPos) {
+            AllAppsGridAdapter.AdapterItem item = mApps.getAdapterItems().get(position);
+            // only account for the first icon in the row since they are the same size within a row
+            return (isIconViewType(item.viewType) && item.rowAppIndex != 0)
+                    ? heightUntilLastPos
+                    : (heightUntilLastPos + mCachedSizes.get(item.viewType));
+        }
     }
 
     @Override
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index 21a7dfb..ac10892 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -15,9 +15,11 @@
  */
 package com.android.launcher3.allapps;
 
-import static android.view.View.MeasureSpec.UNSPECIFIED;
-
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_SCROLLED;
+import static com.android.launcher3.logger.LauncherAtom.ContainerInfo;
+import static com.android.launcher3.logger.LauncherAtom.SearchResultContainer;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_SCROLLED_DOWN;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_SCROLLED_UNKNOWN_DIRECTION;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_SCROLLED_UP;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_VERTICAL_SWIPE_BEGIN;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_VERTICAL_SWIPE_END;
 import static com.android.launcher3.util.LogConfig.SEARCH_LOGGING;
@@ -26,11 +28,11 @@
 import android.graphics.Canvas;
 import android.util.AttributeSet;
 import android.util.Log;
-import android.util.SparseIntArray;
 
 import androidx.recyclerview.widget.RecyclerView;
 
 import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.ExtendedEditText;
 import com.android.launcher3.FastScrollRecyclerView;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
@@ -49,40 +51,11 @@
     private static final boolean DEBUG = false;
     private static final boolean DEBUG_LATENCY = Utilities.isPropertyEnabled(SEARCH_LOGGING);
 
-    protected AlphabeticalAppsList<?> mApps;
     protected final int mNumAppsPerRow;
-
-    // The specific view heights that we use to calculate scroll
-    private final SparseIntArray mViewHeights = new SparseIntArray();
-    private final SparseIntArray mCachedScrollPositions = new SparseIntArray();
     private final AllAppsFastScrollHelper mFastScrollHelper;
+    private int mCumulativeVerticalScroll;
 
-
-    private final AdapterDataObserver mObserver = new RecyclerView.AdapterDataObserver() {
-        public void onChanged() {
-            mCachedScrollPositions.clear();
-        }
-
-        @Override
-        public void onItemRangeChanged(int positionStart, int itemCount) {
-            onChanged();
-        }
-
-        @Override
-        public void onItemRangeInserted(int positionStart, int itemCount) {
-            onChanged();
-        }
-
-        @Override
-        public void onItemRangeRemoved(int positionStart, int itemCount) {
-            onChanged();
-        }
-
-        @Override
-        public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
-            onChanged();
-        }
-    };
+    protected AlphabeticalAppsList<?> mApps;
 
     public AllAppsRecyclerView(Context context) {
         this(context, null);
@@ -122,12 +95,8 @@
         pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_ALL_APPS_DIVIDER, 1);
         pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_ICON, approxRows
                 * (mNumAppsPerRow + 1));
-
-        mViewHeights.clear();
-        mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_ICON, grid.allAppsCellHeightPx);
     }
 
-
     @Override
     public void onDraw(Canvas c) {
         if (DEBUG) {
@@ -157,7 +126,7 @@
         StatsLogManager mgr = ActivityContext.lookupContext(getContext()).getStatsLogManager();
         switch (state) {
             case SCROLL_STATE_DRAGGING:
-                mgr.logger().log(LAUNCHER_ALLAPPS_SCROLLED);
+                mCumulativeVerticalScroll = 0;
                 requestFocus();
                 mgr.logger().sendToInteractionJankMonitor(
                         LAUNCHER_ALLAPPS_VERTICAL_SWIPE_BEGIN, this);
@@ -166,10 +135,17 @@
             case SCROLL_STATE_IDLE:
                 mgr.logger().sendToInteractionJankMonitor(
                         LAUNCHER_ALLAPPS_VERTICAL_SWIPE_END, this);
+                logCumulativeVerticalScroll();
                 break;
         }
     }
 
+    @Override
+    public void onScrolled(int dx, int dy) {
+        super.onScrolled(dx, dy);
+        mCumulativeVerticalScroll += dy;
+    }
+
     /**
      * Maps the touch (from 0..1) to the adapter position that should be visible.
      */
@@ -200,17 +176,6 @@
     }
 
     @Override
-    public void setAdapter(Adapter adapter) {
-        if (getAdapter() != null) {
-            getAdapter().unregisterAdapterDataObserver(mObserver);
-        }
-        super.setAdapter(adapter);
-        if (adapter != null) {
-            adapter.registerAdapterDataObserver(mObserver);
-        }
-    }
-
-    @Override
     protected boolean isPaddingOffsetRequired() {
         return true;
     }
@@ -231,13 +196,13 @@
         List<AllAppsGridAdapter.AdapterItem> items = mApps.getAdapterItems();
 
         // Skip early if there are no items or we haven't been measured
-        if (items.isEmpty() || mNumAppsPerRow == 0) {
+        if (items.isEmpty() || mNumAppsPerRow == 0 || getChildCount() == 0) {
             mScrollbar.setThumbOffsetY(-1);
             return;
         }
 
         // Skip early if, there no child laid out in the container.
-        int scrollY = getCurrentScrollY();
+        int scrollY = computeVerticalScrollOffset();
         if (scrollY < 0) {
             mScrollbar.setThumbOffsetY(-1);
             return;
@@ -292,51 +257,6 @@
         }
     }
 
-    @Override
-    protected int getItemsHeight(int position) {
-        List<AllAppsGridAdapter.AdapterItem> items = mApps.getAdapterItems();
-        AllAppsGridAdapter.AdapterItem posItem = position < items.size()
-                ? items.get(position) : null;
-        int y = mCachedScrollPositions.get(position, -1);
-        if (y < 0) {
-            y = 0;
-            for (int i = 0; i < position; i++) {
-                AllAppsGridAdapter.AdapterItem item = items.get(i);
-                if (AllAppsGridAdapter.isIconViewType(item.viewType)) {
-                    // Break once we reach the desired row
-                    if (posItem != null && posItem.viewType == item.viewType &&
-                            posItem.rowIndex == item.rowIndex) {
-                        break;
-                    }
-                    // Otherwise, only account for the first icon in the row since they are the same
-                    // size within a row
-                    if (item.rowAppIndex == 0) {
-                        y += mViewHeights.get(item.viewType, 0);
-                    }
-                } else {
-                    // Rest of the views span the full width
-                    int elHeight = mViewHeights.get(item.viewType);
-                    if (elHeight == 0) {
-                        ViewHolder holder = findViewHolderForAdapterPosition(i);
-                        if (holder == null) {
-                            holder = getAdapter().createViewHolder(this, item.viewType);
-                            getAdapter().onBindViewHolder(holder, i);
-                            holder.itemView.measure(UNSPECIFIED, UNSPECIFIED);
-                            elHeight = holder.itemView.getMeasuredHeight();
-
-                            getRecycledViewPool().putRecycledView(holder);
-                        } else {
-                            elHeight = holder.itemView.getMeasuredHeight();
-                        }
-                    }
-                    y += elHeight;
-                }
-            }
-            mCachedScrollPositions.put(position, y);
-        }
-        return y;
-    }
-
     public int getScrollBarTop() {
         return getResources().getDimensionPixelOffset(R.dimen.all_apps_header_top_padding);
     }
@@ -349,4 +269,21 @@
     public boolean hasOverlappingRendering() {
         return false;
     }
+
+    private void logCumulativeVerticalScroll() {
+        ActivityContext context = ActivityContext.lookupContext(getContext());
+        StatsLogManager mgr = context.getStatsLogManager();
+        ExtendedEditText editText = context.getAppsView().getSearchUiManager().getEditText();
+        ContainerInfo containerInfo = ContainerInfo.newBuilder().setSearchResultContainer(
+                SearchResultContainer
+                        .newBuilder()
+                        .setQueryLength((editText == null) ? -1 : editText.length())).build();
+
+        // mCumulativeVerticalScroll == 0 when user comes back to original position, we don't
+        // know the direction of scrolling.
+        mgr.logger().withContainerInfo(containerInfo).log(
+                mCumulativeVerticalScroll == 0 ? LAUNCHER_ALLAPPS_SCROLLED_UNKNOWN_DIRECTION
+                        : (mCumulativeVerticalScroll > 0) ? LAUNCHER_ALLAPPS_SCROLLED_DOWN
+                                : LAUNCHER_ALLAPPS_SCROLLED_UP);
+    }
 }
diff --git a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
index 11df81a..2ff992e 100644
--- a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
@@ -107,7 +107,8 @@
             new RecyclerView.OnScrollListener() {
                 @Override
                 public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
-                    updateHeaderScroll(((AllAppsRecyclerView) recyclerView).getCurrentScrollY());
+                    updateHeaderScroll(
+                            ((AllAppsRecyclerView) recyclerView).computeVerticalScrollOffset());
                 }
             };
 
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderView.java b/src/com/android/launcher3/allapps/FloatingHeaderView.java
index f31379e..1cbb0f9 100644
--- a/src/com/android/launcher3/allapps/FloatingHeaderView.java
+++ b/src/com/android/launcher3/allapps/FloatingHeaderView.java
@@ -68,7 +68,7 @@
                         mAnimator.cancel();
                     }
 
-                    int current = -mCurrentRV.getCurrentScrollY();
+                    int current = -mCurrentRV.computeVerticalScrollOffset();
                     boolean headerCollapsed = mHeaderCollapsed;
                     moved(current);
                     applyVerticalMove();
diff --git a/src/com/android/launcher3/allapps/SearchTransitionController.java b/src/com/android/launcher3/allapps/SearchTransitionController.java
index bde4fa6..9c3dab4 100644
--- a/src/com/android/launcher3/allapps/SearchTransitionController.java
+++ b/src/com/android/launcher3/allapps/SearchTransitionController.java
@@ -170,12 +170,13 @@
     /**
      * Updates the children views of SearchRecyclerView based on the current animation progress.
      *
-     * @return the total height of animating views (excluding any app icons).
+     * @return the total height of animating views (excluding at most one row of app icons).
      */
     private int updateSearchRecyclerViewProgress() {
         int numSearchResultsAnimated = 0;
         int totalHeight = 0;
         int appRowHeight = 0;
+        boolean appRowComplete = false;
         Integer top = null;
         SearchRecyclerView searchRecyclerView = getSearchRecyclerView();
 
@@ -189,67 +190,72 @@
                 top = searchResultView.getTop();
             }
 
-            if (searchResultView instanceof BubbleTextView
-                    && searchResultView.getTag() instanceof ItemInfo
-                    && ((ItemInfo) searchResultView.getTag()).itemType == ITEM_TYPE_APPLICATION) {
-                // The first app icon will set appRowHeight, which will also contribute to
-                // totalHeight. Additional app icons should remove the appRowHeight to remain in
-                // the same row as the first app.
-                searchResultView.setY(top + totalHeight - appRowHeight);
-                if (appRowHeight == 0) {
-                    appRowHeight = searchResultView.getHeight();
-                    totalHeight += appRowHeight;
-                }
-                // Don't scale/fade app row.
-                searchResultView.setScaleY(1);
-                searchResultView.setAlpha(1);
-                continue;
-            }
-
-            // Adjust content alpha based on start progress and stagger.
-            float startContentFadeProgress = Math.max(0,
-                    TOP_CONTENT_FADE_PROGRESS_START - CONTENT_STAGGER * numSearchResultsAnimated);
-            float endContentFadeProgress = Math.min(1,
-                    startContentFadeProgress + CONTENT_FADE_PROGRESS_DURATION);
-            searchResultView.setAlpha(1 - clampToProgress(mSearchToAzProgress,
-                    startContentFadeProgress, endContentFadeProgress));
-
-            // Adjust background (or decorator) alpha based on start progress and stagger.
-            float startBackgroundFadeProgress = Math.max(0,
-                    TOP_BACKGROUND_FADE_PROGRESS_START
-                            - CONTENT_STAGGER * numSearchResultsAnimated);
-            float endBackgroundFadeProgress = Math.min(1,
-                    startBackgroundFadeProgress + BACKGROUND_FADE_PROGRESS_DURATION);
-            float backgroundAlpha = 1 - clampToProgress(mSearchToAzProgress,
-                    startBackgroundFadeProgress, endBackgroundFadeProgress);
             int adapterPosition = searchRecyclerView.getChildAdapterPosition(searchResultView);
-            boolean decoratorFilled =
-                    adapterPosition != NO_POSITION
-                            && searchRecyclerView.getApps().getAdapterItems().get(adapterPosition)
-                            .setDecorationFillAlpha((int) (255 * backgroundAlpha));
-            if (!decoratorFilled) {
-                // Try to adjust background alpha instead (e.g. for Search Edu card).
-                Drawable background = searchResultView.getBackground();
-                if (background != null) {
-                    background.setAlpha((int) (255 * backgroundAlpha));
+            int spanIndex = getSpanIndex(searchRecyclerView, adapterPosition);
+            appRowComplete |= appRowHeight > 0 && spanIndex == 0;
+            // We don't animate the first (currently only) app row we see, as that is assumed to be
+            // predicted/prefix-matched apps.
+            boolean shouldAnimate = !isAppIcon(searchResultView) || appRowComplete;
+
+            float contentAlpha = 1f;
+            float backgroundAlpha = 1f;
+            if (shouldAnimate) {
+                if (spanIndex > 0) {
+                    // Animate this item with the previous item on the same row.
+                    numSearchResultsAnimated--;
                 }
+
+                // Adjust content alpha based on start progress and stagger.
+                float startContentFadeProgress = Math.max(0,
+                        TOP_CONTENT_FADE_PROGRESS_START
+                                - CONTENT_STAGGER * numSearchResultsAnimated);
+                float endContentFadeProgress = Math.min(1,
+                        startContentFadeProgress + CONTENT_FADE_PROGRESS_DURATION);
+                contentAlpha = 1 - clampToProgress(mSearchToAzProgress,
+                        startContentFadeProgress, endContentFadeProgress);
+
+                // Adjust background (or decorator) alpha based on start progress and stagger.
+                float startBackgroundFadeProgress = Math.max(0,
+                        TOP_BACKGROUND_FADE_PROGRESS_START
+                                - CONTENT_STAGGER * numSearchResultsAnimated);
+                float endBackgroundFadeProgress = Math.min(1,
+                        startBackgroundFadeProgress + BACKGROUND_FADE_PROGRESS_DURATION);
+                backgroundAlpha = 1 - clampToProgress(mSearchToAzProgress,
+                        startBackgroundFadeProgress, endBackgroundFadeProgress);
+
+                numSearchResultsAnimated++;
+            }
+            searchResultView.setAlpha(contentAlpha);
+            // Apply background alpha to decorator if possible.
+            if (adapterPosition != NO_POSITION) {
+                searchRecyclerView.getApps().getAdapterItems()
+                        .get(adapterPosition).setDecorationFillAlpha((int) (255 * backgroundAlpha));
+            }
+            // Apply background alpha to view's background (e.g. for Search Edu card).
+            Drawable background = searchResultView.getBackground();
+            if (background != null) {
+                background.setAlpha((int) (255 * backgroundAlpha));
             }
 
-            float scaleY = 1 - mSearchToAzProgress;
+            float scaleY = 1;
+            if (shouldAnimate) {
+                scaleY = 1 - mSearchToAzProgress;
+            }
             int scaledHeight = (int) (searchResultView.getHeight() * scaleY);
             searchResultView.setScaleY(scaleY);
 
             // For rows with multiple elements, only count the height once and translate elements to
             // the same y position.
             int y = top + totalHeight;
-            int spanIndex = getSpanIndex(searchRecyclerView, adapterPosition);
             if (spanIndex > 0) {
                 // Continuation of an existing row; move this item into the row.
                 y -= scaledHeight;
             } else {
-                // Start of a new row contributes to total height and animation stagger.
-                numSearchResultsAnimated++;
+                // Start of a new row contributes to total height.
                 totalHeight += scaledHeight;
+                if (!shouldAnimate) {
+                    appRowHeight = scaledHeight;
+                }
             }
             searchResultView.setY(y);
         }
@@ -275,6 +281,11 @@
         return adapter.getSpanIndex(adapterPosition);
     }
 
+    private boolean isAppIcon(View item) {
+        return item instanceof BubbleTextView && item.getTag() instanceof ItemInfo
+                && ((ItemInfo) item.getTag()).itemType == ITEM_TYPE_APPLICATION;
+    }
+
     /** Called just before a child is attached to the SearchRecyclerView. */
     private void onSearchChildAttached(View child) {
         // Avoid allocating hardware layers for alpha changes.
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 3a530af..75c28f9 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -283,13 +283,12 @@
             "In foldables, when reordering the icons and widgets, is now going to use both sides");
 
     public static final BooleanFlag ENABLE_WIDGET_PICKER_DEPTH = new DeviceFlag(
-            "ENABLE_WIDGET_PICKER_DEPTH", false, "Enable changing depth in widget picker.");
+            "ENABLE_WIDGET_PICKER_DEPTH", true, "Enable changing depth in widget picker.");
 
-    public static final BooleanFlag SHOW_DELIGHTFUL_PAGINATION_FOLDER = new DeviceFlag(
-            "SHOW_DELIGHTFUL_PAGINATION_FOLDER", false,
-            "Enable showing the new 'delightful pagination'"
-                    + " which is a brand new animation for folder pagination");
-
+    public static final BooleanFlag SHOW_DELIGHTFUL_PAGINATION = new DeviceFlag(
+            "SHOW_DELIGHTFUL_PAGINATION", false,
+            "Enable showing the new 'delightful pagination' which is a brand"
+                    + " new animation for folder pagination and workspace pagination");
     public static final BooleanFlag POPUP_MATERIAL_U = new DeviceFlag(
             "POPUP_MATERIAL_U", false, "Switch popup UX to use material U");
 
@@ -300,6 +299,10 @@
     public static final BooleanFlag ENABLE_TRANSIENT_TASKBAR = getDebugFlag(
             "ENABLE_TRANSIENT_TASKBAR", false, "Enables transient taskbar.");
 
+    public static final BooleanFlag SECONDARY_DRAG_N_DROP_TO_PIN = getDebugFlag(
+            "SECONDARY_DRAG_N_DROP_TO_PIN", false,
+            "Enable dragging and dropping to pin apps within secondary display");
+
     public static void initialize(Context context) {
         synchronized (sDebugFlags) {
             for (DebugFlag flag : sDebugFlags) {
diff --git a/src/com/android/launcher3/icons/ShortcutCachingLogic.java b/src/com/android/launcher3/icons/ShortcutCachingLogic.java
index d5a79dd..26ddc0b 100644
--- a/src/com/android/launcher3/icons/ShortcutCachingLogic.java
+++ b/src/com/android/launcher3/icons/ShortcutCachingLogic.java
@@ -76,7 +76,8 @@
             Drawable unbadgedDrawable = ShortcutCachingLogic.getIcon(
                     context, info, LauncherAppState.getIDP(context).fillResIconDpi);
             if (unbadgedDrawable == null) return BitmapInfo.LOW_RES_INFO;
-            return new BitmapInfo(li.createScaledBitmapWithoutShadow(unbadgedDrawable),
+            return new BitmapInfo(
+                    li.createScaledBitmap(unbadgedDrawable, BaseIconFactory.MODE_WITH_SHADOW),
                     Themes.getColorAccent(context));
         }
     }
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index 6a28990..05f53fd 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -555,6 +555,20 @@
                 + "result page etc.")
         LAUNCHER_ALLAPPS_SCROLLED(985),
 
+        @UiEvent(doc = "User scrolled up on one of the all apps surfaces such as A-Z list, search "
+                + "result page etc.")
+        LAUNCHER_ALLAPPS_SCROLLED_UP(1229),
+
+        @UiEvent(doc =
+                "User scrolled down on one of the all apps surfaces such as A-Z list, search "
+                        + "result page etc.")
+        LAUNCHER_ALLAPPS_SCROLLED_DOWN(1230),
+
+        @UiEvent(doc = "User scrolled on one of the all apps surfaces such as A-Z list, search "
+                + "result page etc and we don't know the direction since user came back to "
+                + "original position from which they scrolled.")
+        LAUNCHER_ALLAPPS_SCROLLED_UNKNOWN_DIRECTION(1231),
+
         @UiEvent(doc = "User tapped taskbar home button")
         LAUNCHER_TASKBAR_HOME_BUTTON_TAP(1003),
 
diff --git a/src/com/android/launcher3/model/data/ItemInfo.java b/src/com/android/launcher3/model/data/ItemInfo.java
index a8e9eb5..159af60 100644
--- a/src/com/android/launcher3/model/data/ItemInfo.java
+++ b/src/com/android/launcher3/model/data/ItemInfo.java
@@ -39,8 +39,10 @@
 import android.content.ComponentName;
 import android.content.ContentValues;
 import android.content.Intent;
+import android.net.Uri;
 import android.os.Process;
 import android.os.UserHandle;
+import android.provider.Settings;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -61,6 +63,7 @@
 import com.android.launcher3.logger.LauncherAtomExtensions.ExtendedContainers;
 import com.android.launcher3.model.ModelWriter;
 import com.android.launcher3.util.ContentWriter;
+import com.android.launcher3.util.SettingsCache;
 
 import java.util.Optional;
 
@@ -74,6 +77,9 @@
     // An id that doesn't match any item, including predicted apps with have an id=NO_ID
     public static final int NO_MATCHING_ID = Integer.MIN_VALUE;
 
+    /** Hidden field Settings.Secure.NAV_BAR_KIDS_MODE */
+    private static final Uri NAV_BAR_KIDS_MODE = Settings.Secure.getUriFor("nav_bar_kids_mode");
+
     /**
      * The id in the settings database for this item
      */
@@ -390,6 +396,9 @@
     protected LauncherAtom.ItemInfo.Builder getDefaultItemInfoBuilder() {
         LauncherAtom.ItemInfo.Builder itemBuilder = LauncherAtom.ItemInfo.newBuilder();
         itemBuilder.setIsWork(!Process.myUserHandle().equals(user));
+        SettingsCache settingsCache = SettingsCache.INSTANCE.getNoCreate();
+        boolean isKidsMode = settingsCache != null && settingsCache.getValue(NAV_BAR_KIDS_MODE, 0);
+        itemBuilder.setIsKidsMode(isKidsMode);
         itemBuilder.setRank(rank);
         return itemBuilder;
     }
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
index b4cb0ee..98ce951 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
@@ -16,7 +16,7 @@
 
 package com.android.launcher3.pageindicators;
 
-import static com.android.launcher3.config.FeatureFlags.SHOW_DELIGHTFUL_PAGINATION_FOLDER;
+import static com.android.launcher3.config.FeatureFlags.SHOW_DELIGHTFUL_PAGINATION;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -29,6 +29,7 @@
 import android.graphics.Outline;
 import android.graphics.Paint;
 import android.graphics.Paint.Style;
+import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
@@ -38,6 +39,7 @@
 import android.view.animation.Interpolator;
 import android.view.animation.OvershootInterpolator;
 
+import com.android.launcher3.Insettable;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.Interpolators;
@@ -47,7 +49,7 @@
  * {@link PageIndicator} which shows dots per page. The active page is shown with the current
  * accent color.
  */
-public class PageIndicatorDots extends View implements PageIndicator {
+public class PageIndicatorDots extends View implements Insettable, PageIndicator {
 
     private static final float SHIFT_PER_ANIMATION = 0.5f;
     private static final float SHIFT_THRESHOLD = 0.1f;
@@ -128,8 +130,7 @@
         mPaginationPaint.setColor(Themes.getAttrColor(context, R.attr.folderPaginationColor));
         mDotRadius = getResources().getDimension(R.dimen.page_indicator_dot_size) / 2;
 
-
-        if (SHOW_DELIGHTFUL_PAGINATION_FOLDER.get()) {
+        if (SHOW_DELIGHTFUL_PAGINATION.get()) {
             mPageIndicatorSize = getResources().getDimension(
                     R.dimen.page_indicator_size);
             mPageIndicatorRadius = mPageIndicatorSize / 2;
@@ -144,7 +145,7 @@
             mPageIndicatorDrawable = null;
             mCircleGap = DOT_GAP_FACTOR * mDotRadius;
         }
-        if (!SHOW_DELIGHTFUL_PAGINATION_FOLDER.get()) {
+        if (!SHOW_DELIGHTFUL_PAGINATION.get()) {
             setOutlineProvider(new MyOutlineProver());
         }
         mIsRtl = Utilities.isRtl(getResources());
@@ -161,7 +162,7 @@
             currentScroll = totalScroll - currentScroll;
         }
 
-        if (SHOW_DELIGHTFUL_PAGINATION_FOLDER.get()) {
+        if (SHOW_DELIGHTFUL_PAGINATION.get()) {
             mCurrentScroll = currentScroll;
             mTotalScroll = totalScroll;
             invalidate();
@@ -296,7 +297,7 @@
             }
             for (int i = 0; i < mEntryAnimationRadiusFactors.length; i++) {
                 mPaginationPaint.setAlpha(i == mActivePage ? PAGE_INDICATOR_ALPHA : DOT_ALPHA);
-                if (SHOW_DELIGHTFUL_PAGINATION_FOLDER.get()) {
+                if (SHOW_DELIGHTFUL_PAGINATION.get()) {
                     if (i != mActivePage) {
                         canvas.drawCircle(x, y, mDotRadius * mEntryAnimationRadiusFactors[i],
                                 mPaginationPaint);
@@ -313,7 +314,7 @@
             // Here we draw the dots
             mPaginationPaint.setAlpha(DOT_ALPHA);
             for (int i = 0; i < mNumPages; i++) {
-                if (SHOW_DELIGHTFUL_PAGINATION_FOLDER.get()) {
+                if (SHOW_DELIGHTFUL_PAGINATION.get()) {
                     canvas.drawCircle(x, y, getRadius(x), mPaginationPaint);
                 } else {
                     canvas.drawCircle(x, y, mDotRadius, mPaginationPaint);
@@ -323,7 +324,7 @@
 
             // Here we draw the current page indicator
             mPaginationPaint.setAlpha(PAGE_INDICATOR_ALPHA);
-            if (SHOW_DELIGHTFUL_PAGINATION_FOLDER.get()) {
+            if (SHOW_DELIGHTFUL_PAGINATION.get()) {
                 drawPageIndicator(canvas, 1);
             } else {
                 canvas.drawRoundRect(getActiveRect(), mDotRadius, mDotRadius, mPaginationPaint);
@@ -389,7 +390,7 @@
         float diameter = 2 * mDotRadius;
         float startX;
 
-        if (SHOW_DELIGHTFUL_PAGINATION_FOLDER.get()) {
+        if (SHOW_DELIGHTFUL_PAGINATION.get()) {
             startX = ((getWidth() - (mNumPages * mCircleGap) + mDotRadius) / 2) - getOffset();
             sTempRect.top = (getHeight() - mPageIndicatorSize) * 0.5f;
             sTempRect.bottom = (getHeight() + mPageIndicatorSize) * 0.5f;
@@ -483,4 +484,12 @@
             }
         }
     }
+
+    /**
+     * We need to override setInsets to prevent InsettableFrameLayout from applying different
+     * margins on the pagination.
+     */
+    @Override
+    public void setInsets(Rect insets) {
+    }
 }
diff --git a/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java b/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
index 87ae890..bde4e52 100644
--- a/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
+++ b/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
@@ -14,12 +14,9 @@
 import android.os.Looper;
 import android.util.AttributeSet;
 import android.util.Property;
-import android.view.Gravity;
 import android.view.View;
 import android.view.ViewConfiguration;
-import android.widget.FrameLayout;
 
-import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Insettable;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
@@ -258,21 +255,11 @@
         }
     }
 
+    /**
+     * We need to override setInsets to prevent InsettableFrameLayout from applying different
+     * margins on the page indicator.
+     */
     @Override
     public void setInsets(Rect insets) {
-        DeviceProfile grid = mLauncher.getDeviceProfile();
-        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
-
-        if (grid.isVerticalBarLayout()) {
-            Rect padding = grid.workspacePadding;
-            lp.leftMargin = padding.left + grid.workspaceCellPaddingXPx;
-            lp.rightMargin = padding.right + grid.workspaceCellPaddingXPx;
-            lp.bottomMargin = padding.bottom;
-        } else {
-            lp.leftMargin = lp.rightMargin = 0;
-            lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
-            lp.bottomMargin = grid.hotseatBarSizePx;
-        }
-        setLayoutParams(lp);
     }
 }
diff --git a/src/com/android/launcher3/secondarydisplay/PinnedAppsAdapter.java b/src/com/android/launcher3/secondarydisplay/PinnedAppsAdapter.java
index a0ed77e..f03c62a 100644
--- a/src/com/android/launcher3/secondarydisplay/PinnedAppsAdapter.java
+++ b/src/com/android/launcher3/secondarydisplay/PinnedAppsAdapter.java
@@ -168,15 +168,18 @@
         mPrefs.unregisterOnSharedPreferenceChangeListener(this);
     }
 
-    private void update(ItemInfo info, Function<ComponentKey, Boolean> op) {
+    /**
+     * Pins or unpins apps from home screen
+     */
+    public void update(ItemInfo info, Function<ComponentKey, Boolean> op) {
         ComponentKey key = new ComponentKey(info.getTargetComponent(), info.user);
         if (op.apply(key)) {
             createFilteredAppsList();
             Set<ComponentKey> copy = new HashSet<>(mPinnedApps);
             Executors.MODEL_EXECUTOR.submit(() ->
                     mPrefs.edit().putStringSet(PINNED_APPS_KEY,
-                        copy.stream().map(this::encode).collect(Collectors.toSet()))
-                        .apply());
+                                    copy.stream().map(this::encode).collect(Collectors.toSet()))
+                            .apply());
         }
     }
 
@@ -210,6 +213,13 @@
                 mPinnedApps.contains(new ComponentKey(info.getTargetComponent(), info.user)));
     }
 
+    /**
+     * Pins app to home screen
+     */
+    public void addPinnedApp(ItemInfo info) {
+        update(info, mPinnedApps::add);
+    }
+
     private class PinUnPinShortcut extends SystemShortcut<SecondaryDisplayLauncher> {
 
         private final boolean mIsPinned;
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
index a55f7e3..7b32d8b 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
@@ -18,6 +18,9 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.content.Intent;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.view.View;
 import android.view.View.OnClickListener;
@@ -26,6 +29,9 @@
 
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.DragSource;
+import com.android.launcher3.DropTarget;
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherModel;
@@ -33,6 +39,11 @@
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.allapps.ActivityAllAppsContainerView;
+import com.android.launcher3.dragndrop.DragController;
+import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.dragndrop.DraggableView;
+import com.android.launcher3.graphics.DragPreviewProvider;
+import com.android.launcher3.icons.FastBitmapDrawable;
 import com.android.launcher3.model.BgDataModel;
 import com.android.launcher3.model.StringCache;
 import com.android.launcher3.model.data.AppInfo;
@@ -52,11 +63,11 @@
  * Launcher activity for secondary displays
  */
 public class SecondaryDisplayLauncher extends BaseDraggingActivity
-        implements BgDataModel.Callbacks {
+        implements BgDataModel.Callbacks, DragController.DragListener {
 
     private LauncherModel mModel;
-
     private BaseDragLayer mDragLayer;
+    private SecondaryDragController mDragController;
     private ActivityAllAppsContainerView<SecondaryDisplayLauncher> mAppsView;
     private View mAppsButton;
 
@@ -69,10 +80,13 @@
     private boolean mBindingItems = false;
     private SecondaryDisplayPredictions mSecondaryDisplayPredictions;
 
+    private final int[] mTempXY = new int[2];
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         mModel = LauncherAppState.getInstance(this).getModel();
+        mDragController = new SecondaryDragController(this);
         mOnboardingPrefs = new OnboardingPrefs<>(this, Utilities.getPrefs(this));
         mSecondaryDisplayPredictions = SecondaryDisplayPredictions.newInstance(this);
         if (getWindow().getDecorView().isAttachedToWindow()) {
@@ -86,6 +100,12 @@
         initUi();
     }
 
+    @Override
+    public void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        this.getDragController().removeDragListener(this);
+    }
+
     private void initUi() {
         if (mDragLayer != null) {
             return;
@@ -106,6 +126,7 @@
         mAppsView = findViewById(R.id.apps_view);
         mAppsButton = findViewById(R.id.all_apps_button);
 
+        mDragController.addDragListener(this);
         mPopupDataProvider = new PopupDataProvider(
                 mAppsView.getAppsStore()::updateNotificationDots);
 
@@ -113,6 +134,12 @@
     }
 
     @Override
+    protected void onPause() {
+        super.onPause();
+        mDragController.cancelDrag();
+    }
+
+    @Override
     public void onNewIntent(Intent intent) {
         super.onNewIntent(intent);
 
@@ -129,12 +156,21 @@
         showAppDrawer(false);
     }
 
+    public DragController getDragController() {
+        return mDragController;
+    }
+
     @Override
     public void onBackPressed() {
         if (finishAutoCancelActionMode()) {
             return;
         }
 
+        if (mDragController.isDragging()) {
+            mDragController.cancelDrag();
+            return;
+        }
+
         // Note: There should be at most one log per method call. This is enforced implicitly
         // by using if-else statements.
         AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(this);
@@ -202,7 +238,7 @@
         float closeR = Themes.getDialogCornerRadius(this);
         float startR = mAppsButton.getWidth() / 2f;
 
-        float[] buttonPos = new float[] { startR, startR};
+        float[] buttonPos = new float[]{startR, startR};
         mDragLayer.getDescendantCoordRelativeToSelf(mAppsButton, buttonPos);
         mDragLayer.mapCoordInSelfToDescendant(mAppsView, buttonPos);
         final Animator animator = ViewAnimationUtils.createCircularReveal(mAppsView,
@@ -236,6 +272,7 @@
     @Override
     public void startBinding() {
         mBindingItems = true;
+        mDragController.cancelDrag();
     }
 
     @Override
@@ -266,6 +303,10 @@
         }
     }
 
+    public SecondaryDisplayPredictions getSecondaryDisplayPredictions() {
+        return mSecondaryDisplayPredictions;
+    }
+
     @Override
     public StringCache getStringCache() {
         return mStringCache;
@@ -308,4 +349,101 @@
             startActivitySafely(v, intent, item);
         }
     }
+
+    /**
+     * Core functionality for beginning a drag operation for an item that will be dropped within
+     * the secondary display grid home screen
+     */
+    public void beginDragShared(View child, DragSource source, DragOptions options) {
+        Object dragObject = child.getTag();
+        if (!(dragObject instanceof ItemInfo)) {
+            String msg = "Drag started with a view that has no tag set. This "
+                    + "will cause a crash (issue 11627249) down the line. "
+                    + "View: " + child + "  tag: " + child.getTag();
+            throw new IllegalStateException(msg);
+        }
+        beginDragShared(child, source, (ItemInfo) dragObject,
+                new DragPreviewProvider(child), options);
+    }
+
+    private void beginDragShared(View child, DragSource source,
+            ItemInfo dragObject, DragPreviewProvider previewProvider, DragOptions options) {
+
+        float iconScale = 1f;
+        if (child instanceof BubbleTextView) {
+            FastBitmapDrawable icon = ((BubbleTextView) child).getIcon();
+            if (icon != null) {
+                iconScale = icon.getAnimatedScale();
+            }
+        }
+
+        // clear pressed state if necessary
+        child.clearFocus();
+        child.setPressed(false);
+        if (child instanceof BubbleTextView) {
+            BubbleTextView icon = (BubbleTextView) child;
+            icon.clearPressedBackground();
+        }
+
+        DraggableView draggableView = null;
+        if (child instanceof DraggableView) {
+            draggableView = (DraggableView) child;
+        }
+
+        final View contentView = previewProvider.getContentView();
+        final float scale;
+        // The draggable drawable follows the touch point around on the screen
+        final Drawable drawable;
+        if (contentView == null) {
+            drawable = previewProvider.createDrawable();
+            scale = previewProvider.getScaleAndPosition(drawable, mTempXY);
+        } else {
+            drawable = null;
+            scale = previewProvider.getScaleAndPosition(contentView, mTempXY);
+        }
+        int halfPadding = previewProvider.previewPadding / 2;
+        int dragLayerX = mTempXY[0];
+        int dragLayerY = mTempXY[1];
+
+        Point dragVisualizeOffset = null;
+        Rect dragRect = new Rect();
+        if (draggableView != null) {
+            draggableView.getSourceVisualDragBounds(dragRect);
+            dragLayerY += dragRect.top;
+            dragVisualizeOffset = new Point(-halfPadding, halfPadding);
+        }
+        if (contentView != null) {
+            mDragController.startDrag(
+                    contentView,
+                    draggableView,
+                    dragLayerX,
+                    dragLayerY,
+                    source,
+                    dragObject,
+                    dragVisualizeOffset,
+                    dragRect,
+                    scale * iconScale,
+                    scale,
+                    options);
+        } else {
+            mDragController.startDrag(
+                    drawable,
+                    draggableView,
+                    dragLayerX,
+                    dragLayerY,
+                    source,
+                    dragObject,
+                    dragVisualizeOffset,
+                    dragRect,
+                    scale * iconScale,
+                    scale,
+                    options);
+        }
+    }
+
+    @Override
+    public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) { }
+
+    @Override
+    public void onDragEnd() { }
 }
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayPredictions.java b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayPredictions.java
index a58916a..21c50d3 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayPredictions.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayPredictions.java
@@ -16,8 +16,10 @@
 package com.android.launcher3.secondarydisplay;
 
 import android.content.Context;
+import android.view.View;
 
 import com.android.launcher3.R;
+import com.android.launcher3.allapps.ActivityAllAppsContainerView;
 import com.android.launcher3.model.BgDataModel;
 import com.android.launcher3.util.ResourceBasedOverride;
 
@@ -45,4 +47,12 @@
      */
     public void setPredictedApps(BgDataModel.FixedContainerItems item) {
     }
+
+    /**
+     * Set long click listener for predicted apps in top of app drawer.
+     */
+    public void setLongClickListener(
+            ActivityAllAppsContainerView<?> appsView,
+            View.OnLongClickListener onIconLongClickListener) {
+    }
 }
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDragController.java b/src/com/android/launcher3/secondarydisplay/SecondaryDragController.java
new file mode 100644
index 0000000..9bf2764
--- /dev/null
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDragController.java
@@ -0,0 +1,194 @@
+/*
+ * 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.secondarydisplay;
+
+import android.content.res.Resources;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.util.Log;
+import android.view.HapticFeedbackConstants;
+import android.view.View;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.DragSource;
+import com.android.launcher3.DropTarget;
+import com.android.launcher3.R;
+import com.android.launcher3.accessibility.DragViewStateAnnouncer;
+import com.android.launcher3.dragndrop.DragController;
+import com.android.launcher3.dragndrop.DragDriver;
+import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.dragndrop.DragView;
+import com.android.launcher3.dragndrop.DraggableView;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.testing.shared.TestProtocol;
+
+/**
+ * Drag controller for Secondary Launcher activity
+ */
+public class SecondaryDragController extends DragController<SecondaryDisplayLauncher> {
+
+    private static final boolean PROFILE_DRAWING_DURING_DRAG = false;
+
+    public SecondaryDragController(SecondaryDisplayLauncher secondaryLauncher) {
+        super(secondaryLauncher);
+    }
+
+    @Override
+    protected DragView startDrag(@Nullable Drawable drawable, @Nullable View view,
+            DraggableView originalView, int dragLayerX, int dragLayerY, DragSource source,
+            ItemInfo dragInfo, Point dragOffset, Rect dragRegion, float initialDragViewScale,
+            float dragViewScaleOnDrop, DragOptions options) {
+
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.NO_DROP_TARGET, "5");
+        }
+
+        if (PROFILE_DRAWING_DURING_DRAG) {
+            android.os.Debug.startMethodTracing("Launcher");
+        }
+        mActivity.hideKeyboard();
+
+        mOptions = options;
+        if (mOptions.simulatedDndStartPoint != null) {
+            mLastTouch.x = mMotionDown.x = mOptions.simulatedDndStartPoint.x;
+            mLastTouch.y = mMotionDown.y = mOptions.simulatedDndStartPoint.y;
+        }
+
+        final int registrationX = mMotionDown.x - dragLayerX;
+        final int registrationY = mMotionDown.y - dragLayerY;
+
+        final int dragRegionLeft = dragRegion == null ? 0 : dragRegion.left;
+        final int dragRegionTop = dragRegion == null ? 0 : dragRegion.top;
+
+        mLastDropTarget = null;
+
+        mDragObject = new DropTarget.DragObject(mActivity.getApplicationContext());
+        mDragObject.originalView = originalView;
+
+        mIsInPreDrag = mOptions.preDragCondition != null
+                && !mOptions.preDragCondition.shouldStartDrag(0);
+
+        final Resources res = mActivity.getResources();
+        final float scaleDps = mIsInPreDrag
+                ? res.getDimensionPixelSize(R.dimen.pre_drag_view_scale) : 0f;
+
+        final DragView dragView = mDragObject.dragView = drawable != null
+                ? new SecondaryDragView(
+                mActivity,
+                drawable,
+                registrationX,
+                registrationY,
+                initialDragViewScale,
+                dragViewScaleOnDrop,
+                scaleDps)
+                : new SecondaryDragView(
+                        mActivity,
+                        view,
+                        view.getMeasuredWidth(),
+                        view.getMeasuredHeight(),
+                        registrationX,
+                        registrationY,
+                        initialDragViewScale,
+                        dragViewScaleOnDrop,
+                        scaleDps);
+        dragView.setItemInfo(dragInfo);
+        mDragObject.dragComplete = false;
+
+        mDragObject.xOffset = mMotionDown.x - (dragLayerX + dragRegionLeft);
+        mDragObject.yOffset = mMotionDown.y - (dragLayerY + dragRegionTop);
+
+        mDragDriver = DragDriver.create(this, mOptions, ev -> {
+        });
+        if (!mOptions.isAccessibleDrag) {
+            mDragObject.stateAnnouncer = DragViewStateAnnouncer.createFor(dragView);
+        }
+
+        mDragObject.dragSource = source;
+        mDragObject.dragInfo = dragInfo;
+        mDragObject.originalDragInfo = mDragObject.dragInfo.makeShallowCopy();
+
+        if (dragOffset != null) {
+            dragView.setDragVisualizeOffset(new Point(dragOffset));
+        }
+        if (dragRegion != null) {
+            dragView.setDragRegion(new Rect(dragRegion));
+        }
+
+        mActivity.getDragLayer().performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+        dragView.show(mLastTouch.x, mLastTouch.y);
+        mDistanceSinceScroll = 0;
+
+        if (!mIsInPreDrag) {
+            callOnDragStart();
+        } else if (mOptions.preDragCondition != null) {
+            mOptions.preDragCondition.onPreDragStart(mDragObject);
+        }
+
+        handleMoveEvent(mLastTouch.x, mLastTouch.y);
+        return dragView;
+    }
+
+    @Override
+    protected void exitDrag() { }
+
+    @Override
+    protected DropTarget getDefaultDropTarget(int[] dropCoordinates) {
+        DropTarget target = new DropTarget() {
+            @Override
+            public boolean isDropEnabled() {
+                return true;
+            }
+
+            @Override
+            public void onDrop(DragObject dragObject, DragOptions options) {
+                ((SecondaryDragLayer) mActivity.getDragLayer()).getPinnedAppsAdapter().addPinnedApp(
+                        dragObject.dragInfo);
+                dragObject.dragView.remove();
+            }
+
+            @Override
+            public void onDragEnter(DragObject dragObject) {
+                if (getDistanceDragged() > mActivity.getResources().getDimensionPixelSize(
+                        R.dimen.drag_distanceThreshold)) {
+                    mActivity.showAppDrawer(false);
+                    AbstractFloatingView.closeAllOpenViews(mActivity);
+                }
+            }
+
+            @Override
+            public void onDragOver(DragObject dragObject) { }
+
+            @Override
+            public void onDragExit(DragObject dragObject) { }
+
+            @Override
+            public boolean acceptDrop(DragObject dragObject) {
+                return true;
+            }
+
+            @Override
+            public void prepareAccessibilityDrop() { }
+
+            @Override
+            public void getHitRectRelativeToDragLayer(Rect outRect) { }
+        };
+        return target;
+    }
+}
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java b/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
index c79d70d..5eac01e 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
@@ -29,8 +29,12 @@
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.DropTarget;
 import com.android.launcher3.R;
 import com.android.launcher3.allapps.ActivityAllAppsContainerView;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.dragndrop.DragView;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.popup.PopupContainerWithArrow;
 import com.android.launcher3.popup.PopupDataProvider;
@@ -59,7 +63,8 @@
 
     @Override
     public void recreateControllers() {
-        mControllers = new TouchController[] {new CloseAllAppsTouchController()};
+        mControllers = new TouchController[]{new CloseAllAppsTouchController(),
+                mActivity.getDragController()};
     }
 
     /**
@@ -72,7 +77,8 @@
 
         mAppsView = findViewById(R.id.apps_view);
         mAppsView.setOnIconLongClickListener(this::onIconLongClicked);
-
+        mActivity.getSecondaryDisplayPredictions()
+                .setLongClickListener(mAppsView, this::onIconLongClicked);
         // Setup workspace
         mWorkspace = findViewById(R.id.workspace_grid);
         mPinnedAppsAdapter = new PinnedAppsAdapter(mActivity, mAppsView.getAppsStore(),
@@ -166,6 +172,10 @@
         }
     }
 
+    public PinnedAppsAdapter getPinnedAppsAdapter() {
+        return mPinnedAppsAdapter;
+    }
+
     private boolean onIconLongClicked(View v) {
         if (!(v instanceof BubbleTextView)) {
             return false;
@@ -183,6 +193,7 @@
         if (popupDataProvider == null) {
             return false;
         }
+
         final PopupContainerWithArrow container =
                 (PopupContainerWithArrow) mActivity.getLayoutInflater().inflate(
                         R.layout.popup_container, mActivity.getDragLayer(), false);
@@ -192,7 +203,42 @@
                 Collections.emptyList(),
                 Arrays.asList(mPinnedAppsAdapter.getSystemShortcut(item, v),
                         APP_INFO.getShortcut(mActivity, item, v)));
-        v.getParent().requestDisallowInterceptTouchEvent(true);
+        container.requestFocus();
+
+        if (!FeatureFlags.SECONDARY_DRAG_N_DROP_TO_PIN.get() || !mActivity.isAppDrawerShown()) {
+            return true;
+        }
+
+        DragOptions options = new DragOptions();
+        DeviceProfile grid = mActivity.getDeviceProfile();
+        options.intrinsicIconScaleFactor = (float) grid.allAppsIconSizePx / grid.iconSizePx;
+        options.preDragCondition = container.createPreDragCondition(false);
+        if (options.preDragCondition == null) {
+            options.preDragCondition = new DragOptions.PreDragCondition() {
+                private DragView<SecondaryDisplayLauncher> mDragView;
+
+                @Override
+                public boolean shouldStartDrag(double distanceDragged) {
+                    return mDragView != null && mDragView.isAnimationFinished();
+                }
+
+                @Override
+                public void onPreDragStart(DropTarget.DragObject dragObject) {
+                    mDragView = dragObject.dragView;
+                    if (!shouldStartDrag(0)) {
+                        mDragView.setOnAnimationEndCallback(() -> {
+                            mActivity.beginDragShared(v, mActivity.getAppsView(), options);
+                        });
+                    }
+                }
+
+                @Override
+                public void onPreDragEnd(DropTarget.DragObject dragObject, boolean dragStarted) {
+                    mDragView = null;
+                }
+            };
+        }
+        mActivity.beginDragShared(v, mActivity.getAppsView(), options);
         return true;
     }
 }
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDragView.java b/src/com/android/launcher3/secondarydisplay/SecondaryDragView.java
new file mode 100644
index 0000000..0168b8f
--- /dev/null
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDragView.java
@@ -0,0 +1,66 @@
+/*
+ * 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.secondarydisplay;
+
+import android.graphics.drawable.Drawable;
+import android.view.View;
+
+import com.android.launcher3.R;
+import com.android.launcher3.dragndrop.DragView;
+
+/**
+ * A DragView drawn/used by the Secondary Launcher activity.
+ */
+public class SecondaryDragView extends DragView<SecondaryDisplayLauncher> {
+
+    public SecondaryDragView(SecondaryDisplayLauncher launcher,
+            Drawable drawable,
+            int registrationX, int registrationY, float initialScale, float scaleOnDrop,
+            float finalScaleDps) {
+        super(launcher, drawable, registrationX, registrationY, initialScale, scaleOnDrop,
+                finalScaleDps);
+    }
+
+    public SecondaryDragView(SecondaryDisplayLauncher launcher, View content, int width, int height,
+            int registrationX, int registrationY, float initialScale, float scaleOnDrop,
+            float finalScaleDps) {
+        super(launcher, content, width, height, registrationX, registrationY, initialScale,
+                scaleOnDrop, finalScaleDps);
+    }
+
+    @Override
+    public void animateTo(int toTouchX, int toTouchY, Runnable onCompleteRunnable, int duration) {
+        Runnable onAnimationEnd = () -> {
+            if (onCompleteRunnable != null) {
+                onCompleteRunnable.run();
+            }
+            mActivity.getDragLayer().removeView(this);
+        };
+
+        duration = Math.max(duration,
+                getResources().getInteger(R.integer.config_dropAnimMinDuration));
+
+        animate()
+                .translationX(toTouchX - mRegistrationX)
+                .translationY(toTouchY - mRegistrationY)
+                .scaleX(mScaleOnDrop)
+                .scaleY(mScaleOnDrop)
+                .withEndAction(onAnimationEnd)
+                .setDuration(duration)
+                .start();
+    }
+}
diff --git a/src/com/android/launcher3/states/SpringLoadedState.java b/src/com/android/launcher3/states/SpringLoadedState.java
index 3286afb..f5d511c 100644
--- a/src/com/android/launcher3/states/SpringLoadedState.java
+++ b/src/com/android/launcher3/states/SpringLoadedState.java
@@ -15,6 +15,7 @@
  */
 package com.android.launcher3.states;
 
+import static com.android.launcher3.config.FeatureFlags.SHOW_HOME_GARDENING;
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
 
 import android.content.Context;
@@ -44,6 +45,11 @@
 
     @Override
     public ScaleAndTranslation getWorkspaceScaleAndTranslation(Launcher launcher) {
+
+        if (SHOW_HOME_GARDENING.get()) {
+            return super.getWorkspaceScaleAndTranslation(launcher);
+        }
+
         DeviceProfile grid = launcher.getDeviceProfile();
         Workspace<?> ws = launcher.getWorkspace();
         if (ws.getChildCount() == 0) {
@@ -62,6 +68,9 @@
 
     @Override
     protected float getDepthUnchecked(Context context) {
+        if (SHOW_HOME_GARDENING.get()) {
+            return 0;
+        }
         return 0.5f;
     }
 
@@ -72,6 +81,10 @@
 
     @Override
     public float getWorkspaceBackgroundAlpha(Launcher launcher) {
+        if (SHOW_HOME_GARDENING.get()) {
+            return 0;
+        }
+
         return 0.2f;
     }
 }
diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java
index d3c9bc9..269baf0 100644
--- a/src/com/android/launcher3/testing/TestInformationHandler.java
+++ b/src/com/android/launcher3/testing/TestInformationHandler.java
@@ -112,12 +112,12 @@
 
             case TestProtocol.REQUEST_APPS_LIST_SCROLL_Y: {
                 return getLauncherUIProperty(Bundle::putInt,
-                        l -> l.getAppsView().getActiveRecyclerView().getCurrentScrollY());
+                        l -> l.getAppsView().getActiveRecyclerView().computeVerticalScrollOffset());
             }
 
             case TestProtocol.REQUEST_WIDGETS_SCROLL_Y: {
                 return getLauncherUIProperty(Bundle::putInt,
-                        l -> WidgetsFullSheet.getWidgetsView(l).getCurrentScrollY());
+                        l -> WidgetsFullSheet.getWidgetsView(l).computeVerticalScrollOffset());
             }
 
             case TestProtocol.REQUEST_TARGET_INSETS: {
diff --git a/src/com/android/launcher3/testing/shared/TestProtocol.java b/src/com/android/launcher3/testing/shared/TestProtocol.java
index 5116b01..91b7b2d 100644
--- a/src/com/android/launcher3/testing/shared/TestProtocol.java
+++ b/src/com/android/launcher3/testing/shared/TestProtocol.java
@@ -124,6 +124,7 @@
             "get-grid-task-size-rect-for-tablet";
     public static final String REQUEST_GET_OVERVIEW_PAGE_SPACING = "get-overview-page-spacing";
     public static final String REQUEST_ENABLE_ROTATION = "enable_rotation";
+    public static final String REQUEST_ENABLE_SUGGESTION = "enable-suggestion";
 
     public static boolean sDebugTracing = false;
     public static final String REQUEST_ENABLE_DEBUG_TRACING = "enable-debug-tracing";
diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
index ceebc2e..820162c 100644
--- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
@@ -30,6 +30,7 @@
 import static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_TYPE_MAIN;
 
 import android.content.res.Resources;
@@ -265,20 +266,32 @@
     }
 
     @Override
-    public float getTaskMenuX(float x, View thumbnailView, int overScroll,
-            DeviceProfile deviceProfile) {
-        return thumbnailView.getMeasuredWidth() + x;
+    public float getTaskMenuX(float x, View thumbnailView,
+            DeviceProfile deviceProfile, float taskInsetMargin) {
+        return thumbnailView.getMeasuredWidth() + x - taskInsetMargin;
     }
 
     @Override
-    public float getTaskMenuY(float y, View thumbnailView, int overScroll) {
-        return y + overScroll +
-                (thumbnailView.getMeasuredHeight() - thumbnailView.getMeasuredWidth()) / 2f;
+    public float getTaskMenuY(float y, View thumbnailView, int stagePosition,
+            View taskMenuView, float taskInsetMargin) {
+        BaseDragLayer.LayoutParams lp = (BaseDragLayer.LayoutParams) taskMenuView.getLayoutParams();
+        int taskMenuWidth = lp.width;
+        if (stagePosition == STAGE_POSITION_UNDEFINED) {
+            return y + taskInsetMargin
+                    + (thumbnailView.getMeasuredHeight() - taskMenuWidth) / 2f;
+        } else {
+            return y + taskInsetMargin;
+        }
     }
 
     @Override
-    public int getTaskMenuWidth(View view, DeviceProfile deviceProfile) {
-        return view.getMeasuredWidth();
+    public int getTaskMenuWidth(View thumbnailView, DeviceProfile deviceProfile,
+            @StagePosition int stagePosition) {
+        if (stagePosition == SplitConfigurationOptions.STAGE_POSITION_UNDEFINED) {
+            return thumbnailView.getMeasuredWidth();
+        } else {
+            return thumbnailView.getMeasuredHeight();
+        }
     }
 
     @Override
@@ -300,17 +313,6 @@
     }
 
     @Override
-    public void setTaskMenuAroundTaskView(LinearLayout taskView, float margin) {
-        BaseDragLayer.LayoutParams lp = (BaseDragLayer.LayoutParams) taskView.getLayoutParams();
-        lp.topMargin += margin;
-    }
-
-    @Override
-    public PointF getAdditionalInsetForTaskMenu(float margin) {
-        return new PointF(margin, 0);
-    }
-
-    @Override
     public Pair<Float, Float> getDwbLayoutTranslations(int taskViewWidth,
             int taskViewHeight, SplitBounds splitBounds, DeviceProfile deviceProfile,
             View[] thumbnailViews, int desiredTaskId, View banner) {
@@ -376,19 +378,6 @@
         return isRtl ? 1 : -1;
     }
 
-    @Override
-    public void setSecondaryTaskMenuPosition(SplitBounds splitBounds, View taskView,
-            DeviceProfile deviceProfile, View primarySnaphotView, View taskMenuView) {
-        float topLeftTaskPlusDividerPercent = splitBounds.appsStackedVertically
-                ? (splitBounds.topTaskPercent + splitBounds.dividerHeightPercent)
-                : (splitBounds.leftTaskPercent + splitBounds.dividerWidthPercent);
-        FrameLayout.LayoutParams snapshotParams =
-                (FrameLayout.LayoutParams) primarySnaphotView.getLayoutParams();
-        float additionalOffset = (taskView.getHeight() - snapshotParams.topMargin)
-                * topLeftTaskPlusDividerPercent;
-        taskMenuView.setY(taskMenuView.getY() + additionalOffset);
-    }
-
     /* -------------------- */
 
     @Override
@@ -424,8 +413,8 @@
         // In fake land/seascape, the placeholder always needs to go to the "top" of the device,
         // which is the same bounds as 0 rotation.
         int width = dp.widthPx;
-        int insetThickness = dp.getInsets().top;
-        out.set(0, 0, width, placeholderHeight + insetThickness);
+        int insetSizeAdjustment = getPlaceholderSizeAdjustment(dp);
+        out.set(0, 0, width, placeholderHeight + insetSizeAdjustment);
         out.inset(placeholderInset, 0);
 
         // Adjust the top to account for content off screen. This will help to animate the view in
@@ -442,13 +431,21 @@
             float onScreenRectCenterY, float fullscreenScaleX, float fullscreenScaleY,
             int drawableWidth, int drawableHeight, DeviceProfile dp,
             @StagePosition int stagePosition) {
-        float inset = dp.getInsets().top;
+        float insetAdjustment = getPlaceholderSizeAdjustment(dp) / 2f;
         out.setX(Math.round(onScreenRectCenterX / fullscreenScaleX
                 - 1.0f * drawableWidth / 2));
-        out.setY(Math.round((onScreenRectCenterY + (inset / 2f)) / fullscreenScaleY
+        out.setY(Math.round((onScreenRectCenterY + insetAdjustment) / fullscreenScaleY
                 - 1.0f * drawableHeight / 2));
     }
 
+    /**
+     * The split placeholder comes with a default inset to buffer the icon from the top of the
+     * screen. But if the device already has a large inset (from cutouts etc), use that instead.
+     */
+    private int getPlaceholderSizeAdjustment(DeviceProfile dp) {
+        return Math.max(dp.getInsets().top - dp.splitPlaceholderInset, 0);
+    }
+
     @Override
     public void setSplitInstructionsParams(View out, DeviceProfile dp, int splitInstructionsHeight,
             int splitInstructionsWidth, int threeButtonNavShift) {
diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java
index cbcb700..6234462 100644
--- a/src/com/android/launcher3/touch/PagedOrientationHandler.java
+++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java
@@ -184,9 +184,12 @@
      * taskMenu width is the same size as the thumbnail width (what got set below in
      * getTaskMenuWidth()), so we directly use that in the calculations.
      */
-    float getTaskMenuX(float x, View thumbnailView, int overScroll, DeviceProfile deviceProfile);
-    float getTaskMenuY(float y, View thumbnailView, int overScroll);
-    int getTaskMenuWidth(View view, DeviceProfile deviceProfile);
+    float getTaskMenuX(float x, View thumbnailView, DeviceProfile deviceProfile,
+            float taskInsetMargin);
+    float getTaskMenuY(float y, View thumbnailView, int stagePosition,
+            View taskMenuView, float taskInsetMargin);
+    int getTaskMenuWidth(View thumbnailView, DeviceProfile deviceProfile,
+            @StagePosition int stagePosition);
     /**
      * Sets linear layout orientation for {@link com.android.launcher3.popup.SystemShortcut} items
      * inside task menu view.
@@ -200,16 +203,6 @@
      */
     void setLayoutParamsForTaskMenuOptionItem(LinearLayout.LayoutParams lp,
             LinearLayout viewGroup, DeviceProfile deviceProfile);
-    /**
-     * Adjusts margins for the entire task menu view itself, which comprises of both app title and
-     * shortcut options.
-     */
-    void setTaskMenuAroundTaskView(LinearLayout taskView, float margin);
-    /**
-     * Since the task menu layout is manually positioned on top of recents view, this method returns
-     * additional adjustments to the positioning based on fake land/seascape
-     */
-    PointF getAdditionalInsetForTaskMenu(float margin);
 
     /**
      * Calculates the position where a Digital Wellbeing Banner should be placed on its parent
@@ -231,14 +224,6 @@
     int getTaskDragDisplacementFactor(boolean isRtl);
 
     /**
-     * Calls the corresponding {@link View#setX(float)} or {@link View#setY(float)}
-     * on {@param taskMenuView} by taking the space needed by {@param primarySnapshotView} into
-     * account.
-     * This is expected to only be called for secondary (bottom/right) tasks.
-     */
-    void setSecondaryTaskMenuPosition(SplitBounds splitBounds, View taskView,
-            DeviceProfile deviceProfile, View primarySnaphotView, View taskMenuView);
-    /**
      * Maps the velocity from the coordinate plane of the foreground app to that
      * of Launcher's (which now will always be portrait)
      */
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index 5efebaa..af689dc 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -56,7 +56,6 @@
 import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds;
 import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
 import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
-import com.android.launcher3.views.BaseDragLayer;
 
 import java.util.List;
 
@@ -265,26 +264,28 @@
     }
 
     @Override
-    public float getTaskMenuX(float x, View thumbnailView, int overScroll,
-            DeviceProfile deviceProfile) {
+    public float getTaskMenuX(float x, View thumbnailView,
+            DeviceProfile deviceProfile, float taskInsetMargin) {
         if (deviceProfile.isLandscape) {
-            return x + overScroll
+            return x + taskInsetMargin
                     + (thumbnailView.getMeasuredWidth() - thumbnailView.getMeasuredHeight()) / 2f;
         } else {
-            return x + overScroll;
+            return x + taskInsetMargin;
         }
     }
 
     @Override
-    public float getTaskMenuY(float y, View thumbnailView, int overScroll) {
-        return y;
+    public float getTaskMenuY(float y, View thumbnailView, int stagePosition,
+            View taskMenuView, float taskInsetMargin) {
+        return y + taskInsetMargin;
     }
 
     @Override
-    public int getTaskMenuWidth(View view, DeviceProfile deviceProfile) {
+    public int getTaskMenuWidth(View thumbnailView, DeviceProfile deviceProfile,
+            @StagePosition int stagePosition) {
         return deviceProfile.isLandscape && !deviceProfile.isTablet
-                ? view.getMeasuredHeight()
-                : view.getMeasuredWidth();
+                ? thumbnailView.getMeasuredHeight()
+                : thumbnailView.getMeasuredWidth();
     }
 
     @Override
@@ -305,38 +306,6 @@
     }
 
     @Override
-    public void setTaskMenuAroundTaskView(LinearLayout taskView, float margin) {
-        BaseDragLayer.LayoutParams lp = (BaseDragLayer.LayoutParams) taskView.getLayoutParams();
-        lp.topMargin += margin;
-        lp.leftMargin += margin;
-    }
-
-    @Override
-    public PointF getAdditionalInsetForTaskMenu(float margin) {
-        return new PointF(0, 0);
-    }
-
-    @Override
-    public void setSecondaryTaskMenuPosition(SplitBounds splitBounds, View taskView,
-            DeviceProfile deviceProfile, View primarySnaphotView, View taskMenuView) {
-        float topLeftTaskPlusDividerPercent = splitBounds.appsStackedVertically
-                ? (splitBounds.topTaskPercent + splitBounds.dividerHeightPercent)
-                : (splitBounds.leftTaskPercent + splitBounds.dividerWidthPercent);
-        FrameLayout.LayoutParams snapshotParams =
-                (FrameLayout.LayoutParams) primarySnaphotView.getLayoutParams();
-        float additionalOffset;
-        if (deviceProfile.isLandscape) {
-            additionalOffset = (taskView.getWidth() - snapshotParams.leftMargin)
-                    * topLeftTaskPlusDividerPercent;
-            taskMenuView.setX(taskMenuView.getX() + additionalOffset);
-        } else {
-            additionalOffset = (taskView.getHeight() - snapshotParams.topMargin)
-                    * topLeftTaskPlusDividerPercent;
-            taskMenuView.setY(taskMenuView.getY() + additionalOffset);
-        }
-    }
-
-    @Override
     public Pair<Float, Float> getDwbLayoutTranslations(int taskViewWidth,
             int taskViewHeight, SplitBounds splitBounds, DeviceProfile deviceProfile,
             View[] thumbnailViews, int desiredTaskId, View banner) {
@@ -445,13 +414,9 @@
         int screenWidth = dp.widthPx;
         int screenHeight = dp.heightPx;
         boolean pinToRight = stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT;
-        int insetThickness;
-        if (!dp.isLandscape) {
-            insetThickness = dp.getInsets().top;
-        } else {
-            insetThickness = pinToRight ? dp.getInsets().right : dp.getInsets().left;
-        }
-        out.set(0, 0, screenWidth, placeholderHeight + insetThickness);
+        int insetSizeAdjustment = getPlaceholderSizeAdjustment(dp, pinToRight);
+
+        out.set(0, 0, screenWidth, placeholderHeight + insetSizeAdjustment);
         if (!dp.isLandscape) {
             // portrait, phone or tablet - spans width of screen, nothing else to do
             out.inset(placeholderInset, 0);
@@ -496,20 +461,18 @@
             int drawableWidth, int drawableHeight, DeviceProfile dp,
             @StagePosition int stagePosition) {
         boolean pinToRight = stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT;
+        float insetAdjustment = getPlaceholderSizeAdjustment(dp, pinToRight) / 2f;
         if (!dp.isLandscape) {
-            float inset = dp.getInsets().top;
             out.setX(Math.round(onScreenRectCenterX / fullscreenScaleX
                     - 1.0f * drawableWidth / 2));
-            out.setY(Math.round((onScreenRectCenterY + (inset / 2f)) / fullscreenScaleY
+            out.setY(Math.round((onScreenRectCenterY + insetAdjustment) / fullscreenScaleY
                     - 1.0f * drawableHeight / 2));
         } else {
             if (pinToRight) {
-                float inset = dp.getInsets().right;
-                out.setX(Math.round((onScreenRectCenterX - (inset / 2f)) / fullscreenScaleX
+                out.setX(Math.round((onScreenRectCenterX - insetAdjustment) / fullscreenScaleX
                         - 1.0f * drawableWidth / 2));
             } else {
-                float inset = dp.getInsets().left;
-                out.setX(Math.round((onScreenRectCenterX + (inset / 2f)) / fullscreenScaleX
+                out.setX(Math.round((onScreenRectCenterX + insetAdjustment) / fullscreenScaleX
                         - 1.0f * drawableWidth / 2));
             }
             out.setY(Math.round(onScreenRectCenterY / fullscreenScaleY
@@ -517,6 +480,20 @@
         }
     }
 
+    /**
+     * The split placeholder comes with a default inset to buffer the icon from the top of the
+     * screen. But if the device already has a large inset (from cutouts etc), use that instead.
+     */
+    private int getPlaceholderSizeAdjustment(DeviceProfile dp, boolean pinToRight) {
+        int insetThickness;
+        if (!dp.isLandscape) {
+            insetThickness = dp.getInsets().top;
+        } else {
+            insetThickness = pinToRight ? dp.getInsets().right : dp.getInsets().left;
+        }
+        return Math.max(insetThickness - dp.splitPlaceholderInset, 0);
+    }
+
     @Override
     public void setSplitInstructionsParams(View out, DeviceProfile dp, int splitInstructionsHeight,
             int splitInstructionsWidth, int threeButtonNavShift) {
diff --git a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
index a616a8b..05683bd 100644
--- a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
@@ -24,6 +24,7 @@
 
 import static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_TYPE_MAIN;
 
 import android.content.res.Resources;
@@ -33,7 +34,6 @@
 import android.view.Surface;
 import android.view.View;
 import android.widget.FrameLayout;
-import android.widget.LinearLayout;
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
@@ -85,26 +85,22 @@
     }
 
     @Override
-    public float getTaskMenuX(float x, View thumbnailView, int overScroll,
-            DeviceProfile deviceProfile) {
-        return x;
+    public float getTaskMenuX(float x, View thumbnailView,
+            DeviceProfile deviceProfile, float taskInsetMargin) {
+        return x + taskInsetMargin;
     }
 
     @Override
-    public float getTaskMenuY(float y, View thumbnailView, int overScroll) {
-        return y + overScroll +
-                (thumbnailView.getMeasuredHeight() + thumbnailView.getMeasuredWidth()) / 2f;
-    }
-
-    @Override
-    public void setTaskMenuAroundTaskView(LinearLayout taskView, float margin) {
-        BaseDragLayer.LayoutParams lp = (BaseDragLayer.LayoutParams) taskView.getLayoutParams();
-        lp.bottomMargin += margin;
-    }
-
-    @Override
-    public PointF getAdditionalInsetForTaskMenu(float margin) {
-        return new PointF(-margin, margin);
+    public float getTaskMenuY(float y, View thumbnailView, int stagePosition,
+            View taskMenuView, float taskInsetMargin) {
+        BaseDragLayer.LayoutParams lp = (BaseDragLayer.LayoutParams) taskMenuView.getLayoutParams();
+        int taskMenuWidth = lp.width;
+        if (stagePosition == STAGE_POSITION_UNDEFINED) {
+            return y + taskInsetMargin
+                    + (thumbnailView.getMeasuredHeight() + taskMenuWidth) / 2f;
+        } else {
+            return y + taskMenuWidth + taskInsetMargin;
+        }
     }
 
     @Override
diff --git a/src/com/android/launcher3/util/MultiPropertyFactory.java b/src/com/android/launcher3/util/MultiPropertyFactory.java
index e7a7785..43daf08 100644
--- a/src/com/android/launcher3/util/MultiPropertyFactory.java
+++ b/src/com/android/launcher3/util/MultiPropertyFactory.java
@@ -107,12 +107,9 @@
 
         @Override
         public Float get(T object) {
-            // The scale of the view should match mLastAggregatedValue. Still, if it has been
-            // changed without using this property, it can differ. As this get method is usually
-            // used to set the starting point on an animation, this would result in some jumps
-            // when the view scale is different than the last aggregated value. To stay on the
-            // safe side, let's return the real view scale.
-            return mProperty.get(object);
+            // Callers of MultiProperty should only care about the sub-property that it sets. If
+            // the overall value is needed, mProperty.get should be called directly.
+            return mValue;
         }
 
         @Override
diff --git a/src/com/android/launcher3/util/ScrollableLayoutManager.java b/src/com/android/launcher3/util/ScrollableLayoutManager.java
new file mode 100644
index 0000000..17eaefd
--- /dev/null
+++ b/src/com/android/launcher3/util/ScrollableLayoutManager.java
@@ -0,0 +1,183 @@
+/*
+ * 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;
+
+import android.content.Context;
+import android.util.SparseIntArray;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.RecyclerView.Adapter;
+import androidx.recyclerview.widget.RecyclerView.State;
+import androidx.recyclerview.widget.RecyclerView.ViewHolder;
+
+/**
+ * Extension of {@link GridLayoutManager} with support for smooth scrolling
+ */
+public class ScrollableLayoutManager extends GridLayoutManager {
+
+    // keyed on item type
+    protected final SparseIntArray mCachedSizes = new SparseIntArray();
+
+    private RecyclerView mRv;
+
+    /**
+     * Precalculated total height keyed on the item position. This is always incremental.
+     * Subclass can override {@link #incrementTotalHeight} to incorporate the layout logic.
+     * For example all-apps should have same values for items in same row,
+     *     sample values: 0, 10, 10, 10, 10, 20, 20, 20, 20
+     * whereas widgets will have strictly increasing values
+     *     sample values: 0, 10, 50, 60, 110
+     */
+
+    //
+    private int[] mTotalHeightCache = new int[1];
+    private int mLastValidHeightIndex = 0;
+
+    public ScrollableLayoutManager(Context context) {
+        super(context, 1, GridLayoutManager.VERTICAL, false);
+    }
+
+    @Override
+    public void onAttachedToWindow(RecyclerView view) {
+        super.onAttachedToWindow(view);
+        mRv = view;
+    }
+
+    @Override
+    public void layoutDecorated(@NonNull View child, int left, int top, int right, int bottom) {
+        super.layoutDecorated(child, left, top, right, bottom);
+        mCachedSizes.put(
+                mRv.getChildViewHolder(child).getItemViewType(), child.getMeasuredHeight());
+    }
+
+    @Override
+    public void layoutDecoratedWithMargins(@NonNull View child, int left, int top, int right,
+            int bottom) {
+        super.layoutDecoratedWithMargins(child, left, top, right, bottom);
+        mCachedSizes.put(
+                mRv.getChildViewHolder(child).getItemViewType(), child.getMeasuredHeight());
+    }
+
+    @Override
+    public int computeVerticalScrollExtent(State state) {
+        return mRv == null ? 0 : mRv.getHeight();
+    }
+
+    @Override
+    public int computeVerticalScrollOffset(State state) {
+        Adapter adapter = mRv == null ? null : mRv.getAdapter();
+        if (adapter == null) {
+            return 0;
+        }
+        if (adapter.getItemCount() == 0 || getChildCount() == 0) {
+            return 0;
+        }
+        View child = getChildAt(0);
+        ViewHolder holder = mRv.findContainingViewHolder(child);
+        if (holder == null) {
+            return 0;
+        }
+        int itemPosition = holder.getLayoutPosition();
+        if (itemPosition < 0) {
+            return 0;
+        }
+        return getPaddingTop() + getItemsHeight(adapter, itemPosition) - getDecoratedTop(child);
+    }
+
+    @Override
+    public int computeVerticalScrollRange(State state) {
+        Adapter adapter = mRv == null ? null : mRv.getAdapter();
+        return adapter == null ? 0 : getItemsHeight(adapter, adapter.getItemCount());
+    }
+
+    /**
+     * Returns the sum of the height, in pixels, of this list adapter's items from index
+     * 0 (inclusive) until {@code untilIndex} (exclusive). If untilIndex is same as the itemCount,
+     * it returns the full height of all the items.
+     *
+     * <p>If the untilIndex is larger than the total number of items in this adapter, returns the
+     * sum of all items' height.
+     */
+    private int getItemsHeight(Adapter adapter, int untilIndex) {
+        final int totalItems = adapter.getItemCount();
+        if (mTotalHeightCache.length < (totalItems + 1)) {
+            mTotalHeightCache = new int[totalItems + 1];
+            mLastValidHeightIndex = 0;
+        }
+        if (untilIndex > totalItems) {
+            untilIndex = totalItems;
+        } else if (untilIndex < 0) {
+            untilIndex = 0;
+        }
+        if (untilIndex <= mLastValidHeightIndex) {
+            return mTotalHeightCache[untilIndex];
+        }
+
+        int totalItemsHeight = mTotalHeightCache[mLastValidHeightIndex];
+        for (int i = mLastValidHeightIndex; i < untilIndex; i++) {
+            totalItemsHeight = incrementTotalHeight(adapter, i, totalItemsHeight);
+            mTotalHeightCache[i + 1] = totalItemsHeight;
+        }
+        mLastValidHeightIndex = untilIndex;
+        return totalItemsHeight;
+    }
+
+    /**
+     * The current implementation assumes a linear list with every item taking up the whole row.
+     * Subclasses should override this method to account for any spanning logic
+     */
+    protected int incrementTotalHeight(Adapter adapter, int position, int heightUntilLastPos) {
+        return heightUntilLastPos + mCachedSizes.get(adapter.getItemViewType(position));
+    }
+
+    private void invalidateScrollCache() {
+        mLastValidHeightIndex = 0;
+    }
+
+    @Override
+    public void onItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount) {
+        super.onItemsAdded(recyclerView, positionStart, itemCount);
+        invalidateScrollCache();
+    }
+
+    @Override
+    public void onItemsChanged(RecyclerView recyclerView) {
+        super.onItemsChanged(recyclerView);
+        invalidateScrollCache();
+    }
+
+    @Override
+    public void onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount) {
+        super.onItemsRemoved(recyclerView, positionStart, itemCount);
+        invalidateScrollCache();
+    }
+
+    @Override
+    public void onItemsMoved(RecyclerView recyclerView, int from, int to, int itemCount) {
+        super.onItemsMoved(recyclerView, from, to, itemCount);
+        invalidateScrollCache();
+    }
+
+    @Override
+    public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount,
+            Object payload) {
+        super.onItemsUpdated(recyclerView, positionStart, itemCount, payload);
+        invalidateScrollCache();
+    }
+}
diff --git a/src/com/android/launcher3/util/SplitConfigurationOptions.java b/src/com/android/launcher3/util/SplitConfigurationOptions.java
index 88e1b22..3eff783 100644
--- a/src/com/android/launcher3/util/SplitConfigurationOptions.java
+++ b/src/com/android/launcher3/util/SplitConfigurationOptions.java
@@ -100,6 +100,7 @@
      * with the same name/functionality in wm.shell.util (which launcher3 cannot be built against)
      *
      * If you make changes here, consider making the same changes there
+     * TODO(b/254378592): We really need to consolidate this
      */
     public static class SplitBounds {
         public final Rect leftTopBounds;
diff --git a/src/com/android/launcher3/views/AllAppsButton.java b/src/com/android/launcher3/views/AllAppsButton.java
deleted file mode 100644
index ab8e5db..0000000
--- a/src/com/android/launcher3/views/AllAppsButton.java
+++ /dev/null
@@ -1,50 +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.views;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.util.AttributeSet;
-import android.view.ContextThemeWrapper;
-
-import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.R;
-import com.android.launcher3.icons.FastBitmapDrawable;
-
-/**
- * Button in Taskbar that opens All Apps.
- */
-public class AllAppsButton extends BubbleTextView {
-
-    public AllAppsButton(Context context) {
-        this(context, null);
-    }
-
-    public AllAppsButton(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public AllAppsButton(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-
-        Context theme = new ContextThemeWrapper(context, R.style.AllAppsButtonTheme);
-        Bitmap bitmap = LauncherAppState.getInstance(context).getIconCache().getIconFactory()
-                .createScaledBitmapWithShadow(theme.getDrawable(R.drawable.ic_all_apps_button));
-        setIcon(new FastBitmapDrawable(bitmap));
-        setContentDescription(context.getString(R.string.all_apps_button_label));
-    }
-}
diff --git a/src/com/android/launcher3/views/IconButtonView.java b/src/com/android/launcher3/views/IconButtonView.java
new file mode 100644
index 0000000..dd48c99
--- /dev/null
+++ b/src/com/android/launcher3/views/IconButtonView.java
@@ -0,0 +1,94 @@
+/*
+ * 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.views;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.BlendMode;
+import android.graphics.BlendModeColorFilter;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.util.AttributeSet;
+
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.icons.BaseIconFactory;
+import com.android.launcher3.icons.FastBitmapDrawable;
+import com.android.launcher3.icons.LauncherIcons;
+
+/**
+ * Button in Taskbar that shows a tinted background and foreground.
+ */
+public class IconButtonView extends BubbleTextView {
+
+    private static final int[] ATTRS = {android.R.attr.icon};
+
+    public IconButtonView(Context context) {
+        this(context, null);
+    }
+
+    public IconButtonView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public IconButtonView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+
+        TypedArray a = context.obtainStyledAttributes(attrs, ATTRS, defStyle, 0);
+        Drawable fg = a.getDrawable(0);
+        a.recycle();
+
+        ColorStateList tintList = getBackgroundTintList();
+        int tint = tintList == null ? Color.WHITE : tintList.getDefaultColor();
+
+        if (fg == null) {
+            fg = new ColorDrawable(Color.TRANSPARENT);
+        }
+        try (BaseIconFactory factory = LauncherIcons.obtain(context)) {
+            setIcon(new IconDrawable(factory.getWhiteShadowLayer(), tint, fg));
+        }
+    }
+
+    private static class IconDrawable extends FastBitmapDrawable {
+
+        private final Drawable mFg;
+
+        @TargetApi(Build.VERSION_CODES.TIRAMISU)
+        IconDrawable(Bitmap b, int colorBg, Drawable fg) {
+            super(b);
+            mPaint.setColorFilter(new BlendModeColorFilter(colorBg, BlendMode.SRC_IN));
+            mFg = fg;
+        }
+
+        @Override
+        protected void drawInternal(Canvas canvas, Rect bounds) {
+            super.drawInternal(canvas, bounds);
+            mFg.draw(canvas);
+        }
+
+        @Override
+        protected void onBoundsChange(Rect bounds) {
+            super.onBoundsChange(bounds);
+            mFg.setBounds(bounds);
+        }
+    }
+}
diff --git a/src/com/android/launcher3/views/RecyclerViewFastScroller.java b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
index 40e4ce1..3af2e3c 100644
--- a/src/com/android/launcher3/views/RecyclerViewFastScroller.java
+++ b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
@@ -119,7 +119,6 @@
     // prevent jumping, this offset is applied as the user scrolls.
     protected int mTouchOffsetY;
     protected int mThumbOffsetY;
-    protected int mRvOffsetY;
 
     // Fast scroller popup
     private TextView mPopupView;
@@ -207,16 +206,11 @@
 
     public void setThumbOffsetY(int y) {
         if (mThumbOffsetY == y) {
-            int rvCurrentOffsetY = mRv.getCurrentScrollY();
-            if (mRvOffsetY != rvCurrentOffsetY) {
-                mRvOffsetY = mRv.getCurrentScrollY();
-            }
             return;
         }
         updatePopupY(y);
         mThumbOffsetY = y;
         invalidate();
-        mRvOffsetY = mRv.getCurrentScrollY();
     }
 
     public int getThumbOffsetY() {
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHost.java b/src/com/android/launcher3/widget/LauncherAppWidgetHost.java
index 3e80699..fff8fbb 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetHost.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetHost.java
@@ -18,6 +18,7 @@
 
 import static android.app.Activity.RESULT_CANCELED;
 
+import android.app.PendingIntent;
 import android.appwidget.AppWidgetHost;
 import android.appwidget.AppWidgetHostView;
 import android.appwidget.AppWidgetManager;
@@ -28,6 +29,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.util.SparseArray;
+import android.view.View;
 import android.widget.RemoteViews;
 import android.widget.Toast;
 
@@ -80,6 +82,24 @@
 
     private IntConsumer mAppWidgetRemovedCallback = null;
 
+    /**
+     * This serves for the purpose of getting rid of the hidden API calling of InteractionHandler
+     */
+    public interface LauncherWidgetInteractionHandler {
+        /**
+         * Invoked when the user performs an interaction on the View.
+         *
+         * @param view the View with which the user interacted
+         * @param pendingIntent the base PendingIntent associated with the view
+         * @param response the response to the interaction, which knows how to fill in the
+         *                 attached PendingIntent
+         */
+        boolean onInteraction(
+                View view,
+                PendingIntent pendingIntent,
+                RemoteViews.RemoteResponse response);
+    }
+
     public LauncherAppWidgetHost(Context context) {
         this(context, null);
     }
diff --git a/src/com/android/launcher3/widget/PendingItemDragHelper.java b/src/com/android/launcher3/widget/PendingItemDragHelper.java
index c8d528b..bbbc329 100644
--- a/src/com/android/launcher3/widget/PendingItemDragHelper.java
+++ b/src/com/android/launcher3/widget/PendingItemDragHelper.java
@@ -39,6 +39,7 @@
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.dragndrop.DraggableView;
 import com.android.launcher3.graphics.DragPreviewProvider;
+import com.android.launcher3.icons.BaseIconFactory;
 import com.android.launcher3.icons.FastBitmapDrawable;
 import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.icons.RoundDrawableWrapper;
@@ -181,7 +182,8 @@
             PendingAddShortcutInfo createShortcutInfo = (PendingAddShortcutInfo) mAddInfo;
             Drawable icon = createShortcutInfo.activityInfo.getFullResIcon(app.getIconCache());
             LauncherIcons li = LauncherIcons.obtain(launcher);
-            preview = new FastBitmapDrawable(li.createScaledBitmapWithoutShadow(icon));
+            preview = new FastBitmapDrawable(
+                    li.createScaledBitmap(icon, BaseIconFactory.MODE_DEFAULT));
             previewWidth = preview.getIntrinsicWidth();
             previewHeight = preview.getIntrinsicHeight();
             li.recycle();
diff --git a/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
index 35fa7a4..5969e3e 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
@@ -19,7 +19,6 @@
 import android.content.Context;
 import android.graphics.Point;
 import android.util.AttributeSet;
-import android.util.SparseIntArray;
 import android.view.MotionEvent;
 
 import androidx.recyclerview.widget.LinearLayoutManager;
@@ -28,6 +27,7 @@
 
 import com.android.launcher3.FastScrollRecyclerView;
 import com.android.launcher3.R;
+import com.android.launcher3.util.ScrollableLayoutManager;
 
 /**
  * The widgets recycler view.
@@ -42,14 +42,6 @@
     private boolean mTouchDownOnScroller;
     private HeaderViewDimensionsProvider mHeaderViewDimensionsProvider;
 
-    /**
-     * There is always 1 or 0 item of VIEW_TYPE_WIDGETS_LIST. Other types have fixes sizes, so the
-     * the size can be used for all other items of same type. Caching the last know size for
-     * VIEW_TYPE_WIDGETS_LIST allows us to use it to estimate full size even when
-     * VIEW_TYPE_WIDGETS_LIST is not visible on the screen.
-     */
-    private final SparseIntArray mCachedSizes = new SparseIntArray();
-
     public WidgetsRecyclerView(Context context) {
         this(context, null);
     }
@@ -68,9 +60,7 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        // create a layout manager with Launcher's context so that scroll position
-        // can be preserved during screen rotation.
-        setLayoutManager(new LinearLayoutManager(getContext()));
+        setLayoutManager(new ScrollableLayoutManager(getContext()));
     }
 
     @Override
@@ -114,7 +104,7 @@
         }
 
         // Skip early if, there no child laid out in the container.
-        int scrollY = getCurrentScrollY();
+        int scrollY = computeVerticalScrollOffset();
         if (scrollY < 0) {
             mScrollbar.setThumbOffsetY(-1);
             return;
@@ -164,39 +154,6 @@
     }
 
     /**
-     * Returns the sum of the height, in pixels, of this list adapter's items from index 0 until
-     * {@code untilIndex}.
-     *
-     * <p>If the untilIndex is larger than the total number of items in this adapter, returns the
-     * sum of all items' height.
-     */
-    @Override
-    protected int getItemsHeight(int untilIndex) {
-        // Initialize cache
-        int childCount = getChildCount();
-        int startPosition;
-        if (childCount > 0
-                && ((startPosition = getChildAdapterPosition(getChildAt(0))) != NO_POSITION)) {
-            int loopCount = Math.min(getChildCount(), getAdapter().getItemCount() - startPosition);
-            for (int i = 0; i < loopCount; i++) {
-                mCachedSizes.put(
-                        mAdapter.getItemViewType(startPosition + i),
-                        getChildAt(i).getMeasuredHeight());
-            }
-        }
-
-        if (untilIndex > mAdapter.getItems().size()) {
-            untilIndex = mAdapter.getItems().size();
-        }
-        int totalItemsHeight = 0;
-        for (int i = 0; i < untilIndex; i++) {
-            int type = mAdapter.getItemViewType(i);
-            totalItemsHeight += mCachedSizes.get(type);
-        }
-        return totalItemsHeight;
-    }
-
-    /**
      * Provides dimensions of the header view that is shown at the top of a
      * {@link WidgetsRecyclerView}.
      */
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java b/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java
index c8b5e2f..ea0f5a3 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java
@@ -17,10 +17,18 @@
 package com.android.launcher3.uioverrides;
 
 import android.app.Person;
+import android.appwidget.AppWidgetHost;
 import android.content.pm.ShortcutInfo;
 
-import com.android.launcher3.Utilities;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
+import com.android.launcher3.Utilities;
+import com.android.launcher3.widget.LauncherAppWidgetHost;
+
+/**
+ * A wrapper for the hidden API calls
+ */
 public class ApiWrapper {
 
     public static final boolean TASKBAR_DRAWN_IN_PROCESS = false;
@@ -28,4 +36,14 @@
     public static Person[] getPersons(ShortcutInfo si) {
         return Utilities.EMPTY_PERSON_ARRAY;
     }
+
+    /**
+     * Set the interaction handler for the host
+     * @param host AppWidgetHost that needs the interaction handler
+     * @param handler InteractionHandler for the views in the host
+     */
+    public static void setHostInteractionHandler(@NonNull AppWidgetHost host,
+            @Nullable LauncherAppWidgetHost.LauncherWidgetInteractionHandler handler) {
+        // No-op
+    }
 }
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 32b62fb..70d122b 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -537,7 +537,7 @@
     }
 
     protected int getAllAppsScroll(Launcher launcher) {
-        return launcher.getAppsView().getActiveRecyclerView().getCurrentScrollY();
+        return launcher.getAppsView().getActiveRecyclerView().computeVerticalScrollOffset();
     }
 
     private void checkLauncherIntegrity(
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index a3eee00..cf5f5fc 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -302,7 +302,7 @@
     }
 
     private int getWidgetsScroll(Launcher launcher) {
-        return getWidgetsView(launcher).getCurrentScrollY();
+        return getWidgetsView(launcher).computeVerticalScrollOffset();
     }
 
     private boolean isOptionsPopupVisible(Launcher launcher) {
diff --git a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
index 561f3cc..1f5590e 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
@@ -54,6 +54,8 @@
         clearHomescreen();
         mDevice.pressHome();
 
+        waitForLauncherCondition("Workspace didn't finish loading", l -> !l.isWorkspaceLoading());
+
         final LauncherAppWidgetProviderInfo widgetInfo =
                 TestViewHelpers.findWidgetProvider(this, false /* hasConfigureScreen */);
 
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 41bb7f3..449b7b7 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -391,6 +391,10 @@
         getTestInfo(TestProtocol.REQUEST_ENABLE_ROTATION, Boolean.toString(on));
     }
 
+    public void setEnableSuggestion(boolean enableSuggestion) {
+        getTestInfo(TestProtocol.REQUEST_ENABLE_SUGGESTION, Boolean.toString(enableSuggestion));
+    }
+
     public boolean hadNontestEvents() {
         return getTestInfo(TestProtocol.REQUEST_GET_HAD_NONTEST_EVENTS)
                 .getBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD);