Updating swipe up on home animations

> Scaling down home activity on swipe-up from home and spring back to place on cancel
> Fading out recents on swipe-up to home, so ensure that adjecent tiles also dissapear
> Instead of controlling alpha, controlling full transform for base and home task. This allows adding custom animation for home task on swipe up.
> Removing alpha hanging for fallback activity in baseSwipeHandler. Instead controlling full transform for home activity
> Adding feature flag to use QuickstepLauncher as 3P Launcher to make development easier.

Bug: 156398988
Change-Id: I69cc59f2c9c95d15df6926ba635e7d6c251dceb6
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
index f4d1629..c18a0fd 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
@@ -136,10 +136,13 @@
                         new SyncRtSurfaceTransactionApplierCompat(mActivity.getRootView()));
 
         AnimatedFloat recentsAlpha = new AnimatedFloat(() -> { });
-        params.setBaseAlphaCallback((t, a) -> recentsAlpha.value);
+        params.setBaseBuilderProxy((builder, app, p)
+                -> builder.withAlpha(recentsAlpha.value));
 
         Interpolator taskInterpolator;
         if (targets.isAnimatingHome()) {
+            params.setHomeBuilderProxy((builder, app, p) -> builder.withAlpha(1 - p.getProgress()));
+
             taskInterpolator = TOUCH_RESPONSE_INTERPOLATOR;
             pa.addFloat(recentsAlpha, AnimatedFloat.VALUE, 0, 1, TOUCH_RESPONSE_INTERPOLATOR);
         } else {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java
index 23cc6d5..46799ff 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java
@@ -73,7 +73,6 @@
 import com.android.quickstep.util.RectFSpringAnim;
 import com.android.quickstep.util.ShelfPeekAnim;
 import com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState;
-import com.android.quickstep.util.TransformParams.TargetAlphaProvider;
 import com.android.quickstep.views.LiveTileOverlay;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskView;
@@ -671,10 +670,6 @@
     protected InputConsumer createNewInputProxyHandler() {
         endRunningWindowAnim(mGestureState.getEndTarget() == HOME /* cancel */);
         endLauncherTransitionController();
-        if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) {
-            // Hide the task view, if not already hidden
-            setTargetAlphaProvider(BaseSwipeUpHandlerV2::getHiddenTargetAlpha);
-        }
 
         StatefulActivity activity = mActivityInterface.getCreatedActivity();
         return activity == null ? InputConsumer.NO_OP
@@ -1257,11 +1252,6 @@
         reset();
     }
 
-    private void setTargetAlphaProvider(TargetAlphaProvider provider) {
-        mTransformParams.setTaskAlphaCallback(provider);
-        updateFinalShift();
-    }
-
     private void addLiveTileOverlay() {
         if (LiveTileOverlay.INSTANCE.attach(mActivity.getRootView().getOverlay())) {
             mRecentsView.setLiveTileOverlayAttached(true);
@@ -1273,13 +1263,6 @@
         mRecentsView.setLiveTileOverlayAttached(false);
     }
 
-    public static float getHiddenTargetAlpha(RemoteAnimationTargetCompat app, float expectedAlpha) {
-        if (!isNotInRecents(app)) {
-            return 0;
-        }
-        return expectedAlpha;
-    }
-
     private static boolean isNotInRecents(RemoteAnimationTargetCompat app) {
         return app.isNotInRecents
                 || app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
index 7b614c2..96913c6 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -15,23 +15,29 @@
  */
 package com.android.quickstep;
 
-import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.anim.Interpolators.ACCEL;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
 
+import android.animation.ObjectAnimator;
 import android.app.ActivityOptions;
 import android.content.Context;
 import android.content.Intent;
+import android.graphics.Matrix;
 
 import androidx.annotation.NonNull;
 
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.anim.SpringAnimationBuilder;
 import com.android.quickstep.fallback.FallbackRecentsView;
 import com.android.quickstep.util.TransformParams;
+import com.android.quickstep.util.TransformParams.BuilderProxy;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.InputConsumerController;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
 
 /**
  * Handles the navigation gestures when a 3rd party launcher is the default home activity.
@@ -42,6 +48,9 @@
     private FallbackHomeAnimationFactory mActiveAnimationFactory;
     private final boolean mRunningOverHome;
 
+    private final Matrix mTmpMatrix = new Matrix();
+    private float mMaxLauncherScale = 1;
+
     public FallbackSwipeHandler(Context context, RecentsAnimationDeviceState deviceState,
             TaskAnimationManager taskAnimationManager, GestureState gestureState, long touchTimeMs,
             boolean continuingLastGesture, InputConsumerController inputConsumer) {
@@ -49,6 +58,31 @@
                 continuingLastGesture, inputConsumer);
 
         mRunningOverHome = ActivityManagerWrapper.isHomeTask(mGestureState.getRunningTask());
+        if (mRunningOverHome) {
+            mTransformParams.setHomeBuilderProxy(this::updateHomeActivityTransformDuringSwipeUp);
+        }
+    }
+
+    @Override
+    protected void initTransitionEndpoints(DeviceProfile dp) {
+        super.initTransitionEndpoints(dp);
+        if (mRunningOverHome) {
+            mMaxLauncherScale = 1 / mTaskViewSimulator.getFullScreenScale();
+        }
+    }
+
+    private void updateHomeActivityTransformDuringSwipeUp(SurfaceParams.Builder builder,
+            RemoteAnimationTargetCompat app, TransformParams params) {
+        setHomeScaleAndAlpha(builder, app, mCurrentShift.value,
+                Utilities.boundToRange(1 - mCurrentShift.value, 0, 1));
+    }
+
+    private void setHomeScaleAndAlpha(SurfaceParams.Builder builder,
+            RemoteAnimationTargetCompat app, float verticalShift, float alpha) {
+        float scale = Utilities.mapRange(verticalShift, 1, mMaxLauncherScale);
+        mTmpMatrix.setScale(scale, scale,
+                app.localBounds.exactCenterX(), app.localBounds.exactCenterY());
+        builder.withMatrix(mTmpMatrix).withAlpha(alpha);
     }
 
     @Override
@@ -85,30 +119,61 @@
         }
     }
 
-    private class FallbackHomeAnimationFactory extends HomeAnimationFactory
-            implements TransformParams.BuilderProxy {
+    private class FallbackHomeAnimationFactory extends HomeAnimationFactory {
 
         private final TransformParams mHomeAlphaParams = new TransformParams();
-        private final AnimatedFloat mHomeAlpha = new AnimatedFloat(this::updateHomeAlpha);
+        private final AnimatedFloat mHomeAlpha;
+
+        private final AnimatedFloat mVerticalShiftForScale = new AnimatedFloat();
+
+        private final AnimatedFloat mRecentsAlpha = new AnimatedFloat();
 
         private final long mDuration;
         FallbackHomeAnimationFactory(long duration) {
             super(null);
             mDuration = duration;
+
+            if (mRunningOverHome) {
+                mHomeAlpha = new AnimatedFloat();
+                mHomeAlpha.value = Utilities.boundToRange(1 - mCurrentShift.value, 0, 1);
+                mVerticalShiftForScale.value = mCurrentShift.value;
+                mTransformParams.setHomeBuilderProxy(
+                        this::updateHomeActivityTransformDuringHomeAnim);
+            } else {
+                mHomeAlpha = new AnimatedFloat(this::updateHomeAlpha);
+                mHomeAlpha.value = 0;
+
+                mHomeAlphaParams.setHomeBuilderProxy(
+                        this::updateHomeActivityTransformDuringHomeAnim);
+            }
+
+            mRecentsAlpha.value = 1;
+            mTransformParams.setBaseBuilderProxy(
+                    this::updateRecentsActivityTransformDuringHomeAnim);
+        }
+
+        private void updateRecentsActivityTransformDuringHomeAnim(SurfaceParams.Builder builder,
+                RemoteAnimationTargetCompat app, TransformParams params) {
+            builder.withAlpha(mRecentsAlpha.value);
+        }
+
+        private void updateHomeActivityTransformDuringHomeAnim(SurfaceParams.Builder builder,
+                RemoteAnimationTargetCompat app, TransformParams params) {
+            setHomeScaleAndAlpha(builder, app, mVerticalShiftForScale.value, mHomeAlpha.value);
         }
 
         @NonNull
         @Override
         public AnimatorPlaybackController createActivityAnimationToHome() {
             PendingAnimation pa = new PendingAnimation(mDuration);
-            pa.setFloat(mHomeAlpha, AnimatedFloat.VALUE, 1, LINEAR);
+            pa.setFloat(mRecentsAlpha, AnimatedFloat.VALUE, 0, ACCEL);
             return pa.createPlaybackController();
         }
 
         private void updateHomeAlpha() {
-            mHomeAlphaParams.setProgress(mHomeAlpha.value);
             if (mHomeAlphaParams.getTargetSet() != null) {
-                mHomeAlphaParams.applySurfaceParams(mHomeAlphaParams.createSurfaceParams(this));
+                mHomeAlphaParams.applySurfaceParams(
+                        mHomeAlphaParams.createSurfaceParams(BuilderProxy.NO_OP));
             }
         }
 
@@ -125,7 +190,23 @@
         }
 
         @Override
-        public void onBuildParams(Builder builder, RemoteAnimationTargetCompat app, int targetMode,
-                TransformParams params) { }
+        public void playAtomicAnimation(float velocity) {
+            ObjectAnimator alphaAnim = mHomeAlpha.animateToValue(mHomeAlpha.value, 1);
+            alphaAnim.setDuration(mDuration).setInterpolator(ACCEL);
+            alphaAnim.start();
+
+            if (mRunningOverHome) {
+                // Spring back launcher scale
+                new SpringAnimationBuilder(mContext)
+                        .setStartValue(mVerticalShiftForScale.value)
+                        .setEndValue(0)
+                        .setStartVelocity(-velocity / mTransitionDragLength)
+                        .setMinimumVisibleChange(1f / mDp.heightPx)
+                        .setDampingRatio(0.6f)
+                        .setStiffness(800)
+                        .build(mVerticalShiftForScale, AnimatedFloat.VALUE)
+                        .start();
+            }
+        }
     }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
index b441490..852a51a 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
@@ -34,7 +34,6 @@
 import android.content.res.Configuration;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.IBinder;
 import android.os.Looper;
 import android.view.View;
 
@@ -51,7 +50,6 @@
 import com.android.launcher3.statemanager.StateManager.StateHandler;
 import com.android.launcher3.statemanager.StatefulActivity;
 import com.android.launcher3.util.ActivityTracker;
-import com.android.launcher3.util.ObjectWrapper;
 import com.android.launcher3.util.SystemUiController;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.views.BaseDragLayer;
@@ -62,7 +60,6 @@
 import com.android.quickstep.util.RecentsAtomicAnimationFactory;
 import com.android.quickstep.views.OverviewActionsView;
 import com.android.quickstep.views.TaskView;
-import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.ActivityOptionsCompat;
 import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
 import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
@@ -77,8 +74,6 @@
  */
 public final class RecentsActivity extends StatefulActivity<RecentsState> {
 
-    public static final String EXTRA_THUMBNAIL = "thumbnailData";
-    public static final String EXTRA_TASK_ID = "taskID";
     public static final ActivityTracker<RecentsActivity> ACTIVITY_TRACKER =
             new ActivityTracker<>();
 
@@ -118,21 +113,6 @@
 
     @Override
     protected void onNewIntent(Intent intent) {
-        if (intent.getExtras() != null) {
-            int taskID = intent.getIntExtra(EXTRA_TASK_ID, 0);
-            IBinder thumbnail = intent.getExtras().getBinder(EXTRA_THUMBNAIL);
-            if (taskID != 0 && thumbnail instanceof ObjectWrapper) {
-                ObjectWrapper<ThumbnailData> obj = (ObjectWrapper<ThumbnailData>) thumbnail;
-                ThumbnailData thumbnailData = obj.get();
-                mFallbackRecentsView.showCurrentTask(taskID);
-                mFallbackRecentsView.updateThumbnail(taskID, thumbnailData);
-                // Clear the ref since any reference to the extras on the system side will still
-                // hold a reference to the wrapper
-                obj.clear();
-            }
-        }
-        intent.removeExtra(EXTRA_TASK_ID);
-        intent.removeExtra(EXTRA_THUMBNAIL);
         super.onNewIntent(intent);
         ACTIVITY_TRACKER.handleNewIntent(this, intent);
     }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java
index c81afac..c68d6e2 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java
@@ -143,9 +143,6 @@
         boolean parallaxCenterAndAdjacentTask = taskIndex != recentsView.getCurrentPage();
         int startScroll = recentsView.getScrollOffset(taskIndex);
 
-        params.setProgress(1);
-        out.setFloat(params, TransformParams.PROGRESS, 0, TOUCH_RESPONSE_INTERPOLATOR);
-
         Context context = v.getContext();
         DeviceProfile dp = BaseActivity.fromContext(context).getDeviceProfile();
         // RecentsView never updates the display rotation until swipe-up so the value may be stale.
@@ -173,9 +170,7 @@
         }
 
         // Fade in the task during the initial 20% of the animation
-        AnimatedFloat taskAlpha = new AnimatedFloat(() -> { });
-        params.setTaskAlphaCallback((t, alpha) -> taskAlpha.value);
-        out.addFloat(taskAlpha, AnimatedFloat.VALUE, 0, 1, clampToProgress(LINEAR, 0, 0.2f));
+        out.addFloat(params, TransformParams.TARGET_ALPHA, 0, 1, clampToProgress(LINEAR, 0, 0.2f));
 
         if (!skipViewChanges && parallaxCenterAndAdjacentTask && topMostSimulator != null) {
             out.addFloat(v, VIEW_ALPHA, 1, 0, clampToProgress(LINEAR, 0.2f, 0.4f));
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java
index 4e1d849..3b08675 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -87,8 +87,8 @@
     private float mCurveScale = 1;
 
     // RecentsView properties
-    public final AnimatedFloat recentsViewScale = new AnimatedFloat(() -> { });
-    public final AnimatedFloat fullScreenProgress = new AnimatedFloat(() -> { });
+    public final AnimatedFloat recentsViewScale = new AnimatedFloat();
+    public final AnimatedFloat fullScreenProgress = new AnimatedFloat();
     private final ScrollState mScrollState = new ScrollState();
     private final int mPageSpacing;
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TransformParams.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TransformParams.java
index b929f26..9bb508e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TransformParams.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TransformParams.java
@@ -40,14 +40,27 @@
         }
     };
 
+    public static FloatProperty<TransformParams> TARGET_ALPHA =
+            new FloatProperty<TransformParams>("targetAlpha") {
+        @Override
+        public void setValue(TransformParams params, float v) {
+            params.setTargetAlpha(v);
+        }
+
+        @Override
+        public Float get(TransformParams params) {
+            return params.getTargetAlpha();
+        }
+    };
+
     private float mProgress;
     private float mTargetAlpha;
     private float mCornerRadius;
     private RemoteAnimationTargets mTargetSet;
     private SyncRtSurfaceTransactionApplierCompat mSyncTransactionApplier;
 
-    private TargetAlphaProvider mTaskAlphaCallback = (t, a) -> a;
-    private TargetAlphaProvider mBaseAlphaCallback = (t, a) -> 1;
+    private BuilderProxy mHomeBuilderProxy = BuilderProxy.ALWAYS_VISIBLE;
+    private BuilderProxy mBaseBuilderProxy = BuilderProxy.ALWAYS_VISIBLE;
 
     public TransformParams() {
         mProgress = 0;
@@ -105,18 +118,20 @@
     }
 
     /**
-     * Sets an alternate function which can be used to control the alpha of target app
+     * Sets an alternate function to control transform for non-target apps. The default
+     * implementation keeps the targets visible with alpha=1
      */
-    public TransformParams setTaskAlphaCallback(TargetAlphaProvider callback) {
-        mTaskAlphaCallback = callback;
+    public TransformParams setBaseBuilderProxy(BuilderProxy proxy) {
+        mBaseBuilderProxy = proxy;
         return this;
     }
 
     /**
-     * Sets an alternate function which can be used to control the alpha of non-target app
+     * Sets an alternate function to control transform for home target. The default
+     * implementation keeps the targets visible with alpha=1
      */
-    public TransformParams setBaseAlphaCallback(TargetAlphaProvider callback) {
-        mBaseAlphaCallback = callback;
+    public TransformParams setHomeBuilderProxy(BuilderProxy proxy) {
+        mHomeBuilderProxy = proxy;
         return this;
     }
 
@@ -127,25 +142,24 @@
             RemoteAnimationTargetCompat app = targets.unfilteredApps[i];
             SurfaceParams.Builder builder = new SurfaceParams.Builder(app.leash);
 
-            float progress = Utilities.boundToRange(getProgress(), 0, 1);
-            float alpha;
             if (app.mode == targets.targetMode) {
-                alpha = mTaskAlphaCallback.getAlpha(app, getTargetAlpha());
-                if (app.activityType != RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
+                if (app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
+                    mHomeBuilderProxy.onBuildTargetParams(builder, app, this);
+                } else {
                     // Fade out Assistant overlay.
                     if (app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_ASSISTANT
                             && app.isNotInRecents) {
-                        alpha = 1 - Interpolators.DEACCEL_2_5.getInterpolation(progress);
+                        float progress = Utilities.boundToRange(getProgress(), 0, 1);
+                        builder.withAlpha(1 - Interpolators.DEACCEL_2_5.getInterpolation(progress));
+                    } else {
+                        builder.withAlpha(getTargetAlpha());
                     }
-                } else if (targets.hasRecents) {
-                    // If home has a different target then recents, reverse anim the
-                    // home target.
-                    alpha = 1 - (progress * getTargetAlpha());
+
+                    proxy.onBuildTargetParams(builder, app, this);
                 }
             } else {
-                alpha = mBaseAlphaCallback.getAlpha(app, progress);
+                mBaseBuilderProxy.onBuildTargetParams(builder, app, this);
             }
-            proxy.onBuildParams(builder.withAlpha(alpha), app, targets.targetMode, this);
             surfaceParams[i] = builder.build();
         }
         return surfaceParams;
@@ -182,21 +196,13 @@
         }
     }
 
-    public interface TargetAlphaProvider {
-        float getAlpha(RemoteAnimationTargetCompat target, float expectedAlpha);
-    }
-
+    @FunctionalInterface
     public interface BuilderProxy {
 
-        default void onBuildParams(SurfaceParams.Builder builder,
-                RemoteAnimationTargetCompat app, int targetMode, TransformParams params) {
-            if (app.mode == targetMode
-                    && app.activityType != RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
-                onBuildTargetParams(builder, app, params);
-            }
-        }
+        BuilderProxy NO_OP = (builder, app, params) -> { };
+        BuilderProxy ALWAYS_VISIBLE = (builder, app, params) ->builder.withAlpha(1);
 
-        default void onBuildTargetParams(SurfaceParams.Builder builder,
-                RemoteAnimationTargetCompat app, TransformParams params) { }
+        void onBuildTargetParams(SurfaceParams.Builder builder,
+                RemoteAnimationTargetCompat app, TransformParams params);
     }
 }
diff --git a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
index e718598..25c07f1 100644
--- a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
@@ -32,6 +32,7 @@
 import static com.android.launcher3.anim.Interpolators.EXAGGERATED_EASE;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.config.FeatureFlags.KEYGUARD_ANIMATION;
+import static com.android.launcher3.config.FeatureFlags.SEPARATE_RECENTS_ACTIVITY;
 import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_TRANSITIONS;
 import static com.android.launcher3.statehandlers.DepthController.DEPTH;
 import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
@@ -647,6 +648,9 @@
      */
     @Override
     public void registerRemoteAnimations() {
+        if (SEPARATE_RECENTS_ACTIVITY.get()) {
+            return;
+        }
         if (hasControlRemoteAppTransitionPermission()) {
             mWallpaperOpenRunner = createWallpaperOpenRunner(false /* fromUnlock */);
 
@@ -677,6 +681,9 @@
      */
     @Override
     public void unregisterRemoteAnimations() {
+        if (SEPARATE_RECENTS_ACTIVITY.get()) {
+            return;
+        }
         if (hasControlRemoteAppTransitionPermission()) {
             new ActivityCompat(mLauncher).unregisterRemoteAnimations();
 
diff --git a/quickstep/src/com/android/quickstep/AnimatedFloat.java b/quickstep/src/com/android/quickstep/AnimatedFloat.java
index c3b90e3..f7e8781 100644
--- a/quickstep/src/com/android/quickstep/AnimatedFloat.java
+++ b/quickstep/src/com/android/quickstep/AnimatedFloat.java
@@ -38,11 +38,17 @@
                 }
             };
 
+    private static final Runnable NO_OP = () -> { };
+
     private final Runnable mUpdateCallback;
     private ObjectAnimator mValueAnimator;
 
     public float value;
 
+    public AnimatedFloat() {
+        this(NO_OP);
+    }
+
     public AnimatedFloat(Runnable updateCallback) {
         mUpdateCallback = updateCallback;
     }
diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
index 0449d0c..07f838b 100644
--- a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
+++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
@@ -20,6 +20,7 @@
 import static android.content.Intent.ACTION_PACKAGE_CHANGED;
 import static android.content.Intent.ACTION_PACKAGE_REMOVED;
 
+import static com.android.launcher3.config.FeatureFlags.SEPARATE_RECENTS_ACTIVITY;
 import static com.android.launcher3.util.PackageManagerHelper.getPackageFilter;
 import static com.android.systemui.shared.system.PackageManagerWrapper.ACTION_PREFERRED_ACTIVITY_CHANGED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED;
@@ -138,6 +139,13 @@
             mActivityInterface.onAssistantVisibilityChanged(0.f);
         }
 
+        if (SEPARATE_RECENTS_ACTIVITY.get()) {
+            mIsDefaultHome = false;
+            if (defaultHome == null) {
+                defaultHome = mMyHomeIntent.getComponent();
+            }
+        }
+
         if (!mDeviceState.isHomeDisabled() && (defaultHome == null || mIsDefaultHome)) {
             // User default home is same as out home app. Use Overview integrated in Launcher.
             mActivityInterface = LauncherActivityInterface.INSTANCE;
diff --git a/src/com/android/launcher3/LauncherRootView.java b/src/com/android/launcher3/LauncherRootView.java
index 226a924..9ac370f 100644
--- a/src/com/android/launcher3/LauncherRootView.java
+++ b/src/com/android/launcher3/LauncherRootView.java
@@ -1,5 +1,6 @@
 package com.android.launcher3;
 
+import static com.android.launcher3.config.FeatureFlags.SEPARATE_RECENTS_ACTIVITY;
 import static com.android.launcher3.util.SystemUiController.FLAG_DARK_NAV;
 import static com.android.launcher3.util.SystemUiController.UI_STATE_ROOT_VIEW;
 
@@ -177,7 +178,7 @@
 
     @TargetApi(Build.VERSION_CODES.Q)
     public void setDisallowBackGesture(boolean disallowBackGesture) {
-        if (!Utilities.ATLEAST_Q) {
+        if (!Utilities.ATLEAST_Q || SEPARATE_RECENTS_ACTIVITY.get()) {
             return;
         }
         mDisallowBackGesture = disallowBackGesture;
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 869dbbc..376b531 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -36,8 +36,6 @@
     private static final List<DebugFlag> sDebugFlags = new ArrayList<>();
 
     public static final String FLAGS_PREF_NAME = "featureFlags";
-    public static final String FLAG_ENABLE_FIXED_ROTATION_TRANSFORM =
-            "ENABLE_FIXED_ROTATION_TRANSFORM";
 
     private FeatureFlags() { }
 
@@ -168,6 +166,10 @@
             "ENABLE_ALL_APPS_EDU", true,
             "Shows user a tutorial on how to get to All Apps after X amount of attempts.");
 
+    public static final BooleanFlag SEPARATE_RECENTS_ACTIVITY = getDebugFlag(
+            "SEPARATE_RECENTS_ACTIVITY", false,
+            "Uses a separate recents activity instead of using the integrated recents+Launcher UI");
+
     public static void initialize(Context context) {
         synchronized (sDebugFlags) {
             for (DebugFlag flag : sDebugFlags) {