Merge "Fixs NPE when start back-to-home animation."
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 a87eba2..c4fb1f5 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;
 
@@ -50,8 +52,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;
@@ -82,6 +82,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;
@@ -136,7 +137,6 @@
 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.wm.shell.startingsurface.IStartingWindowListener;
 
@@ -313,7 +313,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;
     }
@@ -327,18 +327,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;
             }
@@ -356,9 +356,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.
@@ -400,9 +400,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));
@@ -425,10 +425,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;
@@ -650,9 +650,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,
@@ -665,7 +665,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);
@@ -820,7 +820,7 @@
                 SurfaceTransaction transaction = new SurfaceTransaction();
 
                 for (int i = appTargets.length - 1; i >= 0; i--) {
-                    RemoteAnimationTargetCompat target = appTargets[i];
+                    RemoteAnimationTarget target = appTargets[i];
                     SurfaceProperties builder = transaction.forSurface(target.leash);
 
                     if (target.mode == MODE_OPENING) {
@@ -903,9 +903,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();
@@ -913,7 +913,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)
@@ -937,7 +937,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);
@@ -1004,7 +1004,7 @@
                 SurfaceTransaction transaction = new SurfaceTransaction();
                 float floatingViewAlpha = appTargetsAreTranslucent ? 1 - mPreviewAlpha.value : 1;
                 for (int i = appTargets.length - 1; i >= 0; i--) {
-                    RemoteAnimationTargetCompat target = appTargets[i];
+                    RemoteAnimationTarget target = appTargets[i];
                     SurfaceProperties builder = transaction.forSurface(target.leash);
                     if (target.mode == MODE_OPENING) {
                         floatingView.update(widgetBackgroundBounds, floatingViewAlpha,
@@ -1181,8 +1181,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.
@@ -1194,9 +1194,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++;
             }
@@ -1218,8 +1218,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);
@@ -1230,7 +1230,7 @@
             public void onAnimationStart(Animator animation) {
                 SurfaceTransaction transaction = new SurfaceTransaction();
                 for (int i = appTargets.length - 1; i >= 0; i--) {
-                    RemoteAnimationTargetCompat target = appTargets[i];
+                    RemoteAnimationTarget target = appTargets[i];
                     transaction.forSurface(target.leash)
                             .setAlpha(1f)
                             .setWindowCrop(target.screenSpaceBounds)
@@ -1242,9 +1242,9 @@
         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;
             }
@@ -1255,8 +1255,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) {
@@ -1270,7 +1270,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;
         }
@@ -1331,15 +1331,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;
@@ -1433,7 +1433,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();
@@ -1456,7 +1456,7 @@
             public void onUpdate(float percent, boolean initOnly) {
                 SurfaceTransaction transaction = new SurfaceTransaction();
                 for (int i = appTargets.length - 1; i >= 0; i--) {
-                    RemoteAnimationTargetCompat target = appTargets[i];
+                    RemoteAnimationTarget target = appTargets[i];
                     SurfaceProperties builder = transaction.forSurface(target.leash);
 
                     if (target.localBounds != null) {
@@ -1555,8 +1555,8 @@
      * the transition.
      */
     public Pair<RectFSpringAnim, AnimatorSet> createWallpaperOpenAnimations(
-            RemoteAnimationTargetCompat[] appTargets,
-            RemoteAnimationTargetCompat[] wallpaperTargets,
+            RemoteAnimationTarget[] appTargets,
+            RemoteAnimationTarget[] wallpaperTargets,
             boolean fromUnlock,
             RectF startRect,
             float startWindowCornerRadius) {
@@ -1665,9 +1665,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();
@@ -1706,9 +1706,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 =
@@ -1835,7 +1835,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();
@@ -1846,7 +1846,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;
@@ -1863,7 +1863,7 @@
         public void onUpdate(RectF currentRectF, float progress) {
             SurfaceTransaction transaction = new SurfaceTransaction();
             for (int i = mAppTargets.length - 1; i >= 0; i--) {
-                RemoteAnimationTargetCompat target = mAppTargets[i];
+                RemoteAnimationTarget target = mAppTargets[i];
                 SurfaceProperties builder = transaction.forSurface(target.leash);
 
                 if (target.localBounds != null) {
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/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 3203f44..2bf6f12 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;
@@ -207,6 +209,7 @@
 
         mTISBindHelper = new TISBindHelper(this, this::onTISConnected);
         mDepthController = new DepthController(this);
+        mDesktopVisibilityController = new DesktopVisibilityController(this);
         mHotseatPredictionController = new HotseatPredictionController(this);
 
         mEnableWidgetDepth = ENABLE_WIDGET_PICKER_DEPTH.get()
@@ -732,6 +735,10 @@
         return mDepthController;
     }
 
+    public DesktopVisibilityController getDesktopVisibilityController() {
+        return mDesktopVisibilityController;
+    }
+
     @Nullable
     public UnfoldTransitionProgressProvider getUnfoldTransitionProgressProvider() {
         return mUnfoldTransitionProgressProvider;
@@ -761,8 +768,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/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index d728b75..267593a 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;
@@ -128,7 +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.TaskStackChangeListener;
 import com.android.systemui.shared.system.TaskStackChangeListeners;
 import com.android.wm.shell.common.TransactionPool;
@@ -683,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;
@@ -867,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
@@ -1076,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;
         }
@@ -1315,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
@@ -1362,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
@@ -1472,7 +1472,7 @@
         }
     }
 
-    private int calculateWindowRotation(RemoteAnimationTargetCompat runningTaskTarget,
+    private int calculateWindowRotation(RemoteAnimationTarget runningTaskTarget,
             RecentsOrientedState orientationState) {
         if (runningTaskTarget.rotationChange != 0
                 && TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) {
@@ -1485,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()
@@ -1881,10 +1881,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",
@@ -1959,9 +1965,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;
     }
 
     /**
@@ -2084,10 +2090,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())
@@ -2096,7 +2102,7 @@
                     finishRecentsAnimationOnTasksAppeared();
                     return;
                 }
-                RemoteAnimationTargetCompat taskTarget = taskTargetOptional.get();
+                RemoteAnimationTarget taskTarget = taskTargetOptional.get();
                 TaskView taskView = mRecentsView.getTaskViewByTaskId(taskTarget.taskId);
                 if (taskView == null || !taskView.getThumbnail().shouldShowSplashView()) {
                     finishRecentsAnimationOnTasksAppeared();
@@ -2110,7 +2116,7 @@
                 SurfaceTransactionApplier surfaceApplier =
                         new SurfaceTransactionApplier(splashView);
                 SurfaceTransaction transaction = new SurfaceTransaction();
-                for (RemoteAnimationTargetCompat target : appearedTaskTargets) {
+                for (RemoteAnimationTarget target : appearedTaskTargets) {
                     transaction.forSurface(target.leash).setAlpha(1).setLayer(-1);
                 }
                 surfaceApplier.scheduleApply(transaction);
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index d432004..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();
 
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 e55e966..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;
@@ -70,7 +71,6 @@
 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 java.lang.ref.WeakReference;
 import java.util.ArrayList;
@@ -126,13 +126,13 @@
     }
 
     private void updateHomeActivityTransformDuringSwipeUp(SurfaceProperties builder,
-            RemoteAnimationTargetCompat app, TransformParams params) {
+            RemoteAnimationTarget app, TransformParams params) {
         setHomeScaleAndAlpha(builder, app, mCurrentShift.value,
                 Utilities.boundToRange(1 - mCurrentShift.value, 0, 1));
     }
 
     private void setHomeScaleAndAlpha(SurfaceProperties builder,
-            RemoteAnimationTargetCompat app, float verticalShift, float alpha) {
+            RemoteAnimationTarget app, float verticalShift, float alpha) {
         float scale = Utilities.mapRange(verticalShift, 1, mMaxLauncherScale);
         mTmpMatrix.setScale(scale, scale,
                 app.localBounds.exactCenterX(), app.localBounds.exactCenterY());
@@ -142,7 +142,7 @@
     @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;
@@ -280,12 +280,12 @@
         }
 
         private void updateRecentsActivityTransformDuringHomeAnim(SurfaceProperties builder,
-                RemoteAnimationTargetCompat app, TransformParams params) {
+                RemoteAnimationTarget app, TransformParams params) {
             builder.setAlpha(mRecentsAlpha.value);
         }
 
         private void updateHomeActivityTransformDuringHomeAnim(SurfaceProperties builder,
-                RemoteAnimationTargetCompat app, TransformParams params) {
+                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/LauncherActivityInterface.java b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
index 8522a87..0a7d226 100644
--- a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
@@ -28,6 +28,7 @@
 import android.content.Context;
 import android.graphics.Rect;
 import android.view.MotionEvent;
+import android.view.RemoteAnimationTarget;
 
 import androidx.annotation.Nullable;
 import androidx.annotation.UiThread;
@@ -39,6 +40,7 @@
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.statehandlers.DepthController;
+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;
@@ -175,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) {
@@ -252,7 +263,7 @@
     }
 
     @Override
-    public Rect getOverviewWindowBounds(Rect homeBounds, RemoteAnimationTargetCompat target) {
+    public Rect getOverviewWindowBounds(Rect homeBounds, RemoteAnimationTarget target) {
         return homeBounds;
     }
 
diff --git a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
index ea685e2..64df9d8 100644
--- a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
+++ b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
@@ -52,7 +52,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;
 
 /**
  * Controls the animation of swiping back and returning to launcher.
@@ -90,7 +89,7 @@
     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;
@@ -171,7 +170,7 @@
                     IRemoteAnimationFinishedCallback finishedCallback) {
                 for (final RemoteAnimationTarget target : apps) {
                     if (MODE_CLOSING == target.mode) {
-                        mBackTarget = new RemoteAnimationTargetCompat(target);
+                        mBackTarget = target;
                         break;
                     }
                 }
@@ -311,8 +310,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/RotationTouchHelper.java b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
index f8b6966..65a1e56 100644
--- a/quickstep/src/com/android/quickstep/RotationTouchHelper.java
+++ b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
@@ -16,8 +16,10 @@
 package com.android.quickstep;
 
 import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.InputDevice.SOURCE_TOUCHSCREEN;
 import static android.view.Surface.ROTATION_0;
 
+import static com.android.launcher3.config.FeatureFlags.ENABLE_TRACKPAD_GESTURE;
 import static com.android.launcher3.util.DisplayController.CHANGE_ACTIVE_SCREEN;
 import static com.android.launcher3.util.DisplayController.CHANGE_ALL;
 import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
@@ -232,6 +234,9 @@
      * @return whether the coordinates of the {@param event} is in the swipe up gesture region.
      */
     public boolean isInSwipeUpTouchRegion(MotionEvent event) {
+        if (isTrackpadMotionEvent(event)) {
+            return true;
+        }
         return mOrientationTouchTransformer.touchInValidSwipeRegions(event.getX(), event.getY());
     }
 
@@ -240,10 +245,20 @@
      *         is in the swipe up gesture region.
      */
     public boolean isInSwipeUpTouchRegion(MotionEvent event, int pointerIndex) {
+        if (isTrackpadMotionEvent(event)) {
+            return true;
+        }
         return mOrientationTouchTransformer.touchInValidSwipeRegions(event.getX(pointerIndex),
                 event.getY(pointerIndex));
     }
 
+    private boolean isTrackpadMotionEvent(MotionEvent event) {
+        // TODO: ideally should use event.getClassification(), but currently only the move
+        // events get assigned the correct classification.
+        return ENABLE_TRACKPAD_GESTURE.get()
+                && (event.getSource() & SOURCE_TOUCHSCREEN) != SOURCE_TOUCHSCREEN;
+    }
+
     @Override
     public void onDisplayInfoChanged(Context context, Info info, int flags) {
         onDisplayInfoChangedInternal(info, flags, false);
diff --git a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
index 71e8a77..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;
@@ -41,7 +42,6 @@
 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 java.util.Arrays;
 import java.util.function.Consumer;
@@ -335,7 +335,7 @@
         }
 
         @Override
-        public void onBuildTargetParams(SurfaceProperties builder, RemoteAnimationTargetCompat app,
+        public void onBuildTargetParams(SurfaceProperties builder, RemoteAnimationTarget app,
                 TransformParams params) {
             builder.setMatrix(mMatrix)
                     .setWindowCrop(mCropRect)
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index 87d2109..fd97ce9 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -675,6 +675,7 @@
      *
      * @return RemoteAnimationTargets of windows that need to animate but only exist in shell.
      */
+    @Nullable
     public RemoteAnimationTarget[] onGoingToRecentsLegacy(RemoteAnimationTarget[] apps) {
         if (mSplitScreen != null) {
             try {
@@ -686,6 +687,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 bfebbbc..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;
@@ -103,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;
@@ -133,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;
@@ -156,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();
@@ -168,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);
@@ -410,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();
@@ -492,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) {
@@ -517,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;
@@ -572,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;
@@ -666,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;
@@ -676,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/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
index c131c05..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;
@@ -59,7 +60,6 @@
 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 java.util.HashMap;
 
@@ -290,7 +290,7 @@
 
     @Override
     public void onBuildTargetParams(
-            SurfaceProperties builder, RemoteAnimationTargetCompat app, TransformParams params) {
+            SurfaceProperties builder, RemoteAnimationTarget app, TransformParams params) {
         mMatrix.setTranslate(0, mProgress.value * mMaxTranslationY);
         builder.setMatrix(mMatrix);
     }
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 b2e159e..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;
 
 /**
  * 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) {
         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/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index a2e88f9..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;
 
@@ -50,7 +51,6 @@
 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;
 
 /**
  * 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) {
@@ -387,7 +390,7 @@
 
     @Override
     public void onBuildTargetParams(
-            SurfaceProperties builder, RemoteAnimationTargetCompat app, TransformParams params) {
+            SurfaceProperties builder, RemoteAnimationTarget app, TransformParams params) {
         builder.setMatrix(mMatrix)
                 .setWindowCrop(mTmpCropRect)
                 .setCornerRadius(getCurrentCornerRadius());
diff --git a/quickstep/src/com/android/quickstep/util/TransformParams.java b/quickstep/src/com/android/quickstep/util/TransformParams.java
index 3d505c6..aa9a45b 100644
--- a/quickstep/src/com/android/quickstep/util/TransformParams.java
+++ b/quickstep/src/com/android/quickstep/util/TransformParams.java
@@ -15,14 +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.quickstep.util.SurfaceTransaction.SurfaceProperties;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
 public class TransformParams {
 
@@ -140,16 +144,16 @@
         mRecentsSurface = getRecentsSurface(targets);
 
         for (int i = 0; i < targets.unfilteredApps.length; i++) {
-            RemoteAnimationTargetCompat app = targets.unfilteredApps[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.setAlpha(1 - Interpolators.DEACCEL_2_5.getInterpolation(progress));
                     } else {
@@ -166,7 +170,7 @@
         // 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];
+            RemoteAnimationTarget wallpaper = targets.wallpapers[i];
             transaction.forSurface(wallpaper.leash).setLayer(Integer.MIN_VALUE);
         }
         return transaction;
@@ -174,9 +178,9 @@
 
     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 {
@@ -223,6 +227,6 @@
         BuilderProxy ALWAYS_VISIBLE = (builder, app, params) -> builder.setAlpha(1);
 
         void onBuildTargetParams(SurfaceProperties builder,
-                RemoteAnimationTargetCompat app, TransformParams params);
+                RemoteAnimationTarget app, TransformParams params);
     }
 }
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/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index 2ae7136..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
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index f60e08d..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;
@@ -192,7 +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.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)) {
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/quickstep/util/TaskViewSimulatorTest.java b/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
index 97eee1f..83602be 100644
--- a/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
+++ b/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
@@ -23,6 +23,7 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.util.ArrayMap;
+import android.view.RemoteAnimationTarget;
 import android.view.Surface;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -42,7 +43,6 @@
 import com.android.quickstep.FallbackActivityInterface;
 import com.android.quickstep.SystemUiProxy;
 import com.android.quickstep.util.SurfaceTransaction.MockProperties;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
 import org.hamcrest.Description;
 import org.hamcrest.TypeSafeMatcher;
@@ -209,7 +209,7 @@
         public SurfaceTransaction createSurfaceParams(BuilderProxy proxy) {
             RecordingSurfaceTransaction transaction = new RecordingSurfaceTransaction();
             proxy.onBuildTargetParams(
-                    transaction.mockProperties, mock(RemoteAnimationTargetCompat.class), this);
+                    transaction.mockProperties, mock(RemoteAnimationTarget.class), this);
             return transaction;
         }
 
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 cdd8f5a..75d7b6b 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -55,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;
 
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 3abefe0..07d0f55 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -2803,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/Workspace.java b/src/com/android/launcher3/Workspace.java
index dd70ad0..fe8b364 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -224,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;
@@ -575,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;
         }
     }
 
@@ -597,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
@@ -919,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.
@@ -1070,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);
@@ -2534,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 0efa7c4..ac10892 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -15,8 +15,6 @@
  */
 package com.android.launcher3.allapps;
 
-import static android.view.View.MeasureSpec.UNSPECIFIED;
-
 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;
@@ -30,7 +28,6 @@
 import android.graphics.Canvas;
 import android.util.AttributeSet;
 import android.util.Log;
-import android.util.SparseIntArray;
 
 import androidx.recyclerview.widget.RecyclerView;
 
@@ -54,41 +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);
@@ -128,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) {
@@ -213,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;
     }
@@ -244,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;
@@ -305,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);
     }
diff --git a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
index 70c1e18..7f6247e 100644
--- a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
@@ -106,7 +106,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/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 05ba32a..4170e97 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -308,6 +308,9 @@
     public static final BooleanFlag ENABLE_TRANSIENT_TASKBAR = getDebugFlag(
             "ENABLE_TRANSIENT_TASKBAR", false, "Enables transient taskbar.");
 
+    public static final BooleanFlag ENABLE_TRACKPAD_GESTURE = getDebugFlag(
+            "ENABLE_TRACKPAD_GESTURE", false, "Enables trackpad gesture.");
+
     public static void initialize(Context context) {
         synchronized (sDebugFlags) {
             for (DebugFlag flag : sDebugFlags) {
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/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/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/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/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 d6510ad..366b868 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 */);