Merge "Migrate AllAppsSearch [part 1/3]" into ub-launcher3-master
diff --git a/OWNERS b/OWNERS
index 3069afa..1d6ad8c 100644
--- a/OWNERS
+++ b/OWNERS
@@ -28,6 +28,7 @@
 peanutbutter@google.com
 xuqiu@google.com
 sreyasr@google.com
+thiruram@google.com
 
 per-file FeatureFlags.java, globs = set noparent
 per-file FeatureFlags.java = sunnygoyal@google.com, winsonc@google.com, zakcohen@google.com, mrcasey@google.com, adamcohen@google.com, hyunyoungs@google.com
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index f87138e..beb2a6a 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -215,10 +215,8 @@
         switch (state.ordinal) {
             case HINT_STATE_ORDINAL: {
                 Workspace workspace = getWorkspace();
-                boolean willMoveScreens = workspace.getNextPage() != Workspace.DEFAULT_PAGE;
-                getStateManager().goToState(NORMAL, true,
-                        willMoveScreens ? null : getScrimView()::startDragHandleEducationAnim);
-                if (willMoveScreens) {
+                getStateManager().goToState(NORMAL);
+                if (workspace.getNextPage() != Workspace.DEFAULT_PAGE) {
                     workspace.post(workspace::moveToDefaultScreen);
                 }
                 break;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/AbsSwipeUpHandler.java
similarity index 81%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java
rename to quickstep/recents_ui_overrides/src/com/android/quickstep/AbsSwipeUpHandler.java
index 27d4846..5a3449b 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -15,6 +15,8 @@
  */
 package com.android.quickstep;
 
+import static android.widget.Toast.LENGTH_SHORT;
+
 import static com.android.launcher3.BaseActivity.INVISIBLE_BY_STATE_HANDLER;
 import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS;
 import static com.android.launcher3.anim.Interpolators.DEACCEL;
@@ -29,7 +31,9 @@
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_QUICKSWITCH_LEFT;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_QUICKSWITCH_RIGHT;
 import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW;
+import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
 import static com.android.quickstep.GestureState.GestureEndTarget.HOME;
 import static com.android.quickstep.GestureState.GestureEndTarget.LAST_TASK;
 import static com.android.quickstep.GestureState.GestureEndTarget.NEW_TASK;
@@ -51,13 +55,17 @@
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.PointF;
+import android.graphics.Rect;
 import android.os.Build;
 import android.os.SystemClock;
+import android.util.Log;
+import android.view.MotionEvent;
 import android.view.View;
 import android.view.View.OnApplyWindowInsetsListener;
 import android.view.ViewTreeObserver.OnDrawListener;
 import android.view.WindowInsets;
 import android.view.animation.Interpolator;
+import android.widget.Toast;
 
 import androidx.annotation.Nullable;
 import androidx.annotation.UiThread;
@@ -73,17 +81,24 @@
 import com.android.launcher3.logging.StatsLogManager.StatsLogger;
 import com.android.launcher3.logging.UserEventDispatcher;
 import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.launcher3.util.TraceHelper;
+import com.android.launcher3.util.VibratorWrapper;
+import com.android.launcher3.util.WindowBounds;
 import com.android.quickstep.BaseActivityInterface.AnimationFactory;
 import com.android.quickstep.GestureState.GestureEndTarget;
 import com.android.quickstep.inputconsumers.OverviewInputConsumer;
 import com.android.quickstep.util.ActiveGestureLog;
+import com.android.quickstep.util.ActivityInitListener;
+import com.android.quickstep.util.InputConsumerProxy;
 import com.android.quickstep.util.RectFSpringAnim;
 import com.android.quickstep.util.ShelfPeekAnim;
 import com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState;
+import com.android.quickstep.util.SurfaceTransactionApplier;
+import com.android.quickstep.util.TransformParams;
 import com.android.quickstep.views.LiveTileOverlay;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskView;
@@ -95,17 +110,34 @@
 import com.android.systemui.shared.system.TaskInfoCompat;
 import com.android.systemui.shared.system.TaskStackChangeListener;
 
+import java.util.ArrayList;
+import java.util.function.Consumer;
+
 /**
  * Handles the navigation gestures when Launcher is the default home activity.
- * TODO: Merge this with BaseSwipeUpHandler
  */
-@TargetApi(Build.VERSION_CODES.O)
-public abstract class BaseSwipeUpHandlerV2<T extends StatefulActivity<?>, Q extends RecentsView>
-        extends BaseSwipeUpHandler<T, Q> implements OnApplyWindowInsetsListener {
-    private static final String TAG = BaseSwipeUpHandlerV2.class.getSimpleName();
+@TargetApi(Build.VERSION_CODES.R)
+public abstract class AbsSwipeUpHandler<T extends StatefulActivity<?>, Q extends RecentsView>
+        extends SwipeUpAnimationLogic implements OnApplyWindowInsetsListener,
+        RecentsAnimationCallbacks.RecentsAnimationListener {
+    private static final String TAG = "AbsSwipeUpHandler";
 
     private static final String[] STATE_NAMES = DEBUG_STATES ? new String[16] : null;
 
+    protected final BaseActivityInterface<?, T> mActivityInterface;
+    protected final InputConsumerProxy mInputConsumerProxy;
+    protected final ActivityInitListener mActivityInitListener;
+    // Callbacks to be made once the recents animation starts
+    private final ArrayList<Runnable> mRecentsAnimationStartCallbacks = new ArrayList<>();
+    protected RecentsAnimationController mRecentsAnimationController;
+    protected RecentsAnimationTargets mRecentsAnimationTargets;
+    protected T mActivity;
+    protected Q mRecentsView;
+    protected Runnable mGestureEndCallback;
+    protected MultiStateCallback mStateCallback;
+    protected boolean mCanceled;
+    private boolean mRecentsViewScrollLinked = false;
+
     private static int getFlagForIndex(int index, String name) {
         if (DEBUG_STATES) {
             STATE_NAMES[index] = name;
@@ -201,11 +233,15 @@
 
     private final Runnable mOnDeferredActivityLaunch = this::onDeferredActivityLaunch;
 
-    public BaseSwipeUpHandlerV2(Context context, RecentsAnimationDeviceState deviceState,
+    public AbsSwipeUpHandler(Context context, RecentsAnimationDeviceState deviceState,
             TaskAnimationManager taskAnimationManager, GestureState gestureState,
             long touchTimeMs, boolean continuingLastGesture,
             InputConsumerController inputConsumer) {
-        super(context, deviceState, gestureState, inputConsumer);
+        super(context, deviceState, gestureState, new TransformParams());
+        mActivityInterface = gestureState.getActivityInterface();
+        mActivityInitListener = mActivityInterface.createActivityInitListener(this::onActivityInit);
+        mInputConsumerProxy =
+                new InputConsumerProxy(inputConsumer, this::createNewInputProxyHandler);
         mTaskAnimationManager = taskAnimationManager;
         mTouchTimeMs = touchTimeMs;
         mContinuingLastGesture = continuingLastGesture;
@@ -274,9 +310,17 @@
         }
     }
 
-    @Override
     protected boolean onActivityInit(Boolean alreadyOnHome) {
-        super.onActivityInit(alreadyOnHome);
+        T createdActivity = mActivityInterface.getCreatedActivity();
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.PAUSE_NOT_DETECTED, "BaseSwipeUpHandler.1");
+        }
+        if (createdActivity != null) {
+            if (TestProtocol.sDebugTracing) {
+                Log.d(TestProtocol.PAUSE_NOT_DETECTED, "BaseSwipeUpHandler.2");
+            }
+            initTransitionEndpoints(createdActivity.getDeviceProfile());
+        }
         final T activity = mActivityInterface.getCreatedActivity();
         if (mActivity == activity) {
             return true;
@@ -315,7 +359,9 @@
         return true;
     }
 
-    @Override
+    /**
+     * Return true if the window should be translated horizontally if the recents view scrolls
+     */
     protected boolean moveWindowWithRecentsScroll() {
         return mGestureState.getEndTarget() != HOME;
     }
@@ -394,7 +440,7 @@
         mGestureState.runOnceAtState(STATE_END_TARGET_SET,
                 () -> mDeviceState.getRotationTouchHelper().
                         onEndTargetCalculated(mGestureState.getEndTarget(),
-                        mActivityInterface));
+                                mActivityInterface));
 
         notifyGestureStartedAsync();
     }
@@ -444,7 +490,9 @@
                 .getHighResLoadingState().setVisible(true);
     }
 
-    @Override
+    /**
+     * Called when motion pause is detected
+     */
     public void onMotionPauseChanged(boolean isPaused) {
         setShelfState(isPaused ? PEEK : HIDE, ShelfPeekAnim.INTERPOLATOR, ShelfPeekAnim.DURATION);
     }
@@ -482,7 +530,6 @@
         mAnimationFactory.setRecentsAttachedToAppWindow(recentsAttachedToAppWindow, animate);
     }
 
-    @Override
     public void setIsLikelyToStartNewTask(boolean isLikelyToStartNewTask) {
         setIsLikelyToStartNewTask(isLikelyToStartNewTask, true /* animate */);
     }
@@ -538,11 +585,14 @@
         updateLauncherTransitionProgress();
     }
 
-    @Override
     public Intent getLaunchIntent() {
         return mGestureState.getOverviewIntent();
     }
 
+    /**
+     * Called when the value of {@link #mCurrentShift} changes
+     */
+    @UiThread
     @Override
     public void updateFinalShift() {
         final boolean passed = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW;
@@ -603,7 +653,41 @@
     public void onRecentsAnimationStart(RecentsAnimationController controller,
             RecentsAnimationTargets targets) {
         ActiveGestureLog.INSTANCE.addLog("startRecentsAnimationCallback", targets.apps.length);
-        super.onRecentsAnimationStart(controller, targets);
+        mRecentsAnimationController = controller;
+        mRecentsAnimationTargets = targets;
+        mTransformParams.setTargetSet(mRecentsAnimationTargets);
+        RemoteAnimationTargetCompat runningTaskTarget = targets.findTask(
+                mGestureState.getRunningTaskId());
+
+        if (runningTaskTarget != null) {
+            mTaskViewSimulator.setPreview(runningTaskTarget);
+        }
+
+        // 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) {
+            DeviceProfile dp = mTaskViewSimulator.getOrientationState().getLauncherDeviceProfile();
+            if (targets.minimizedHomeBounds != null && runningTaskTarget != null) {
+                Rect overviewStackBounds = mActivityInterface
+                        .getOverviewWindowBounds(targets.minimizedHomeBounds, runningTaskTarget);
+                dp = dp.getMultiWindowProfile(mContext,
+                        new WindowBounds(overviewStackBounds, targets.homeContentInsets));
+            } else {
+                // If we are not in multi-window mode, home insets should be same as system insets.
+                dp = dp.copy(mContext);
+            }
+            dp.updateInsets(targets.homeContentInsets);
+            dp.updateIsSeascape(mContext);
+            initTransitionEndpoints(dp);
+        }
+
+        // Notify when the animation starts
+        if (!mRecentsAnimationStartCallbacks.isEmpty()) {
+            for (Runnable action : new ArrayList<>(mRecentsAnimationStartCallbacks)) {
+                action.run();
+            }
+            mRecentsAnimationStartCallbacks.clear();
+        }
 
         // Only add the callback to enable the input consumer after we actually have the controller
         mStateCallback.runOnceAtState(STATE_APP_CONTROLLER_RECEIVED | STATE_GESTURE_STARTED,
@@ -620,10 +704,14 @@
         mStateCallback.setStateOnUiThread(STATE_GESTURE_CANCELLED | STATE_HANDLER_INVALIDATED);
 
         // Defer clearing the controller and the targets until after we've updated the state
-        super.onRecentsAnimationCanceled(thumbnailData);
+        mRecentsAnimationController = null;
+        mRecentsAnimationTargets = null;
+        if (mRecentsView != null) {
+            mRecentsView.setRecentsAnimationTargets(null, null);
+        }
     }
 
-    @Override
+    @UiThread
     public void onGestureStarted(boolean isLikelyToStartNewTask) {
         notifyGestureStartedAsync();
         setIsLikelyToStartNewTask(isLikelyToStartNewTask, false /* animate */);
@@ -647,7 +735,7 @@
     /**
      * Called as a result on ACTION_CANCEL to return the UI to the start state.
      */
-    @Override
+    @UiThread
     public void onGestureCancelled() {
         updateDisplacement(0);
         mStateCallback.setStateOnUiThread(STATE_GESTURE_COMPLETED);
@@ -660,7 +748,7 @@
      * @param velocity The x and y components of the velocity when the gesture ends.
      * @param downPos The x and y value of where the gesture started.
      */
-    @Override
+    @UiThread
     public void onGestureEnded(float endVelocity, PointF velocity, PointF downPos) {
         float flingThreshold = mContext.getResources()
                 .getDimension(R.dimen.quickstep_fling_threshold_velocity);
@@ -678,7 +766,10 @@
         handleNormalGestureEnd(endVelocity, isFling, velocity, false /* isCancel */);
     }
 
-    @Override
+    /**
+     * Called to create a input proxy for the running task
+     */
+    @UiThread
     protected InputConsumer createNewInputProxyHandler() {
         endRunningWindowAnim(mGestureState.getEndTarget() == HOME /* cancel */);
         endLauncherTransitionController();
@@ -719,7 +810,7 @@
         ActiveGestureLog.INSTANCE.addLog("onSettledOnEndTarget " + mGestureState.getEndTarget());
     }
 
-    @Override
+    /** @return Whether this was the task we were waiting to appear, and thus handled it. */
     protected boolean handleTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {
         if (mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)) {
             return false;
@@ -840,11 +931,9 @@
             }
         }
 
-        if (endTarget.isLauncher && mRecentsAnimationController != null) {
-            mRecentsAnimationController.enableInputProxy(mInputConsumer,
-                    this::createNewInputProxyHandler);
+        if (endTarget.isLauncher) {
+            mInputConsumerProxy.enable();
         }
-
         if (endTarget == HOME) {
             setShelfState(ShelfAnimState.CANCEL, LINEAR, 0);
             duration = Math.max(MIN_OVERSHOOT_DURATION, duration);
@@ -1099,10 +1188,12 @@
                 mActivityInterface.onSwipeUpToHomeComplete(mDeviceState);
             }
         });
+        if (mRecentsAnimationTargets != null) {
+            mRecentsAnimationTargets.addReleaseCheck(anim);
+        }
         return anim;
     }
 
-    @Override
     public void onConsumerAboutToBeSwitched() {
         if (mActivity != null) {
             // In the off chance that the gesture ends before Launcher is started, we should clear
@@ -1153,9 +1244,19 @@
         });
     }
 
-    @Override
+    /**
+     * Called when we successfully startNewTask() on the task that was previously running. Normally
+     * we call resumeLastTask() when returning to the previously running task, but this handles a
+     * specific edge case: if we switch from A to B, and back to A before B appears, we need to
+     * start A again to ensure it stays on top.
+     */
+    @androidx.annotation.CallSuper
     protected void onRestartPreviouslyAppearedTask() {
-        super.onRestartPreviouslyAppearedTask();
+        // Finish the controller here, since we won't get onTaskAppeared() for a task that already
+        // appeared.
+        if (mRecentsAnimationController != null) {
+            mRecentsAnimationController.finish(false, null);
+        }
         reset();
     }
 
@@ -1177,6 +1278,7 @@
     }
 
     private void invalidateHandler() {
+        mInputConsumerProxy.destroy();
         endRunningWindowAnim(false /* cancel */);
 
         if (mGestureEndCallback != null) {
@@ -1259,7 +1361,7 @@
                     // new thumbnail
                     finishTransitionPosted = ViewUtils.postDraw(taskView,
                             () -> mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED),
-                                    this::isCanceled);
+                            this::isCanceled);
                 }
             }
             if (!finishTransitionPosted) {
@@ -1328,4 +1430,172 @@
         return app.isNotInRecents
                 || app.activityType == ACTIVITY_TYPE_HOME;
     }
+
+    /**
+     * To be called at the end of constructor of subclasses. This calls various methods which can
+     * depend on proper class initialization.
+     */
+    protected void initAfterSubclassConstructor() {
+        initTransitionEndpoints(
+                mTaskViewSimulator.getOrientationState().getLauncherDeviceProfile());
+    }
+
+    protected void performHapticFeedback() {
+        VibratorWrapper.INSTANCE.get(mContext).vibrate(OVERVIEW_HAPTIC);
+    }
+
+    public Consumer<MotionEvent> getRecentsViewDispatcher(float navbarRotation) {
+        return mRecentsView != null ? mRecentsView.getEventDispatcher(navbarRotation) : null;
+    }
+
+    public void setGestureEndCallback(Runnable gestureEndCallback) {
+        mGestureEndCallback = gestureEndCallback;
+    }
+
+    protected void linkRecentsViewScroll() {
+        SurfaceTransactionApplier.create(mRecentsView, applier -> {
+            mTransformParams.setSyncTransactionApplier(applier);
+            runOnRecentsAnimationStart(() ->
+                    mRecentsAnimationTargets.addReleaseCheck(applier));
+        });
+
+        mRecentsView.setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) -> {
+            if (moveWindowWithRecentsScroll()) {
+                updateFinalShift();
+            }
+        });
+        runOnRecentsAnimationStart(() ->
+                mRecentsView.setRecentsAnimationTargets(mRecentsAnimationController,
+                        mRecentsAnimationTargets));
+        mRecentsViewScrollLinked = true;
+    }
+
+    protected void startNewTask(Consumer<Boolean> resultCallback) {
+        // Launch the task user scrolled to (mRecentsView.getNextPage()).
+        if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+            // We finish recents animation inside launchTask() when live tile is enabled.
+            mRecentsView.getNextPageTaskView().launchTask(false /* animate */,
+                    true /* freezeTaskList */);
+        } else {
+            if (!mCanceled) {
+                TaskView nextTask = mRecentsView.getNextPageTaskView();
+                if (nextTask != null) {
+                    int taskId = nextTask.getTask().key.id;
+                    mGestureState.updateLastStartedTaskId(taskId);
+                    boolean hasTaskPreviouslyAppeared = mGestureState.getPreviouslyAppearedTaskIds()
+                            .contains(taskId);
+                    nextTask.launchTask(false /* animate */, true /* freezeTaskList */,
+                            success -> {
+                                resultCallback.accept(success);
+                                if (success) {
+                                    if (hasTaskPreviouslyAppeared) {
+                                        onRestartPreviouslyAppearedTask();
+                                    }
+                                } else {
+                                    mActivityInterface.onLaunchTaskFailed();
+                                    nextTask.notifyTaskLaunchFailed(TAG);
+                                    mRecentsAnimationController.finish(true /* toRecents */, null);
+                                }
+                            }, MAIN_EXECUTOR.getHandler());
+                } else {
+                    mActivityInterface.onLaunchTaskFailed();
+                    Toast.makeText(mContext, R.string.activity_not_available, LENGTH_SHORT).show();
+                    mRecentsAnimationController.finish(true /* toRecents */, null);
+                }
+            }
+            mCanceled = false;
+        }
+    }
+
+    /**
+     * Runs the given {@param action} if the recents animation has already started, or queues it to
+     * be run when it is next started.
+     */
+    protected void runOnRecentsAnimationStart(Runnable action) {
+        if (mRecentsAnimationTargets == null) {
+            mRecentsAnimationStartCallbacks.add(action);
+        } else {
+            action.run();
+        }
+    }
+
+    /**
+     * TODO can we remove this now that we don't finish the controller until onTaskAppeared()?
+     * @return whether the recents animation has started and there are valid app targets.
+     */
+    protected boolean hasTargets() {
+        return mRecentsAnimationTargets != null && mRecentsAnimationTargets.hasTargets();
+    }
+
+    @Override
+    public void onRecentsAnimationFinished(RecentsAnimationController controller) {
+        mRecentsAnimationController = null;
+        mRecentsAnimationTargets = null;
+        if (mRecentsView != null) {
+            mRecentsView.setRecentsAnimationTargets(null, null);
+        }
+    }
+
+    @Override
+    public void onTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {
+        if (mRecentsAnimationController != null) {
+            if (handleTaskAppeared(appearedTaskTarget)) {
+                mRecentsAnimationController.finish(false /* toRecents */,
+                        null /* onFinishComplete */);
+                mActivityInterface.onLaunchTaskSuccess();
+                ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", false);
+            }
+        }
+    }
+
+    /**
+     * @return The index of the TaskView in RecentsView whose taskId matches the task that will
+     * resume if we finish the controller.
+     */
+    protected int getLastAppearedTaskIndex() {
+        return mGestureState.getLastAppearedTaskId() != -1
+                ? mRecentsView.getTaskIndexForId(mGestureState.getLastAppearedTaskId())
+                : mRecentsView.getRunningTaskIndex();
+    }
+
+    /**
+     * @return Whether we are continuing a gesture that already landed on a new task,
+     * but before that task appeared.
+     */
+    protected boolean hasStartedNewTask() {
+        return mGestureState.getLastStartedTaskId() != -1;
+    }
+
+    /**
+     * Registers a callback to run when the activity is ready.
+     * @param intent The intent that will be used to start the activity if it doesn't exist already.
+     */
+    public void initWhenReady(Intent intent) {
+        // Preload the plan
+        RecentsModel.INSTANCE.get(mContext).getTasks(null);
+
+        mActivityInitListener.register(intent);
+    }
+
+    /**
+     * Applies the transform on the recents animation
+     */
+    protected void applyWindowTransform() {
+        if (mWindowTransitionController != null) {
+            float progress = mCurrentShift.value / mDragLengthFactor;
+            mWindowTransitionController.setPlayFraction(progress);
+        }
+        if (mRecentsAnimationTargets != null) {
+            if (mRecentsViewScrollLinked) {
+                mTaskViewSimulator.setScroll(mRecentsView.getScrollOffset());
+            }
+            mTaskViewSimulator.apply(mTransformParams);
+        }
+    }
+
+    public interface Factory {
+
+        AbsSwipeUpHandler<StatefulActivity<?>, RecentsView> newHandler(
+                GestureState gestureState, long touchTimeMs, boolean continuingLastGesture);
+    }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
deleted file mode 100644
index b49299d..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
+++ /dev/null
@@ -1,393 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep;
-
-import static android.widget.Toast.LENGTH_SHORT;
-
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.os.Build;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.widget.Toast;
-
-import androidx.annotation.CallSuper;
-import androidx.annotation.UiThread;
-
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.R;
-import com.android.launcher3.statemanager.StatefulActivity;
-import com.android.launcher3.testing.TestProtocol;
-import com.android.launcher3.util.VibratorWrapper;
-import com.android.launcher3.util.WindowBounds;
-import com.android.quickstep.RecentsAnimationCallbacks.RecentsAnimationListener;
-import com.android.quickstep.util.ActiveGestureLog;
-import com.android.quickstep.util.ActivityInitListener;
-import com.android.quickstep.util.RectFSpringAnim;
-import com.android.quickstep.util.SurfaceTransactionApplier;
-import com.android.quickstep.util.TransformParams;
-import com.android.quickstep.views.RecentsView;
-import com.android.quickstep.views.TaskView;
-import com.android.systemui.shared.recents.model.ThumbnailData;
-import com.android.systemui.shared.system.InputConsumerController;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-
-import java.util.ArrayList;
-import java.util.function.Consumer;
-
-/**
- * Base class for swipe up handler with some utility methods
- */
-@TargetApi(Build.VERSION_CODES.Q)
-public abstract class BaseSwipeUpHandler<T extends StatefulActivity<?>, Q extends RecentsView>
-        extends SwipeUpAnimationLogic implements RecentsAnimationListener {
-
-    private static final String TAG = "BaseSwipeUpHandler";
-
-    protected final BaseActivityInterface<?, T> mActivityInterface;
-    protected final InputConsumerController mInputConsumer;
-
-    protected final ActivityInitListener mActivityInitListener;
-
-    protected RecentsAnimationController mRecentsAnimationController;
-    protected RecentsAnimationTargets mRecentsAnimationTargets;
-
-    // Callbacks to be made once the recents animation starts
-    private final ArrayList<Runnable> mRecentsAnimationStartCallbacks = new ArrayList<>();
-
-    protected T mActivity;
-    protected Q mRecentsView;
-
-    protected Runnable mGestureEndCallback;
-
-    protected MultiStateCallback mStateCallback;
-
-    protected boolean mCanceled;
-
-    private boolean mRecentsViewScrollLinked = false;
-
-    protected BaseSwipeUpHandler(Context context, RecentsAnimationDeviceState deviceState,
-            GestureState gestureState, InputConsumerController inputConsumer) {
-        super(context, deviceState, gestureState, new TransformParams());
-        mActivityInterface = gestureState.getActivityInterface();
-        mActivityInitListener = mActivityInterface.createActivityInitListener(this::onActivityInit);
-        mInputConsumer = inputConsumer;
-    }
-
-    /**
-     * To be called at the end of constructor of subclasses. This calls various methods which can
-     * depend on proper class initialization.
-     */
-    protected void initAfterSubclassConstructor() {
-        initTransitionEndpoints(
-                mTaskViewSimulator.getOrientationState().getLauncherDeviceProfile());
-    }
-
-    protected void performHapticFeedback() {
-        VibratorWrapper.INSTANCE.get(mContext).vibrate(OVERVIEW_HAPTIC);
-    }
-
-    public Consumer<MotionEvent> getRecentsViewDispatcher(float navbarRotation) {
-        return mRecentsView != null ? mRecentsView.getEventDispatcher(navbarRotation) : null;
-    }
-
-    public void setGestureEndCallback(Runnable gestureEndCallback) {
-        mGestureEndCallback = gestureEndCallback;
-    }
-
-    public abstract Intent getLaunchIntent();
-
-    protected void linkRecentsViewScroll() {
-        SurfaceTransactionApplier.create(mRecentsView, applier -> {
-            mTransformParams.setSyncTransactionApplier(applier);
-            runOnRecentsAnimationStart(() ->
-                    mRecentsAnimationTargets.addReleaseCheck(applier));
-        });
-
-        mRecentsView.setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) -> {
-            if (moveWindowWithRecentsScroll()) {
-                updateFinalShift();
-            }
-        });
-        runOnRecentsAnimationStart(() ->
-                mRecentsView.setRecentsAnimationTargets(mRecentsAnimationController,
-                        mRecentsAnimationTargets));
-        mRecentsViewScrollLinked = true;
-    }
-
-    protected void startNewTask(Consumer<Boolean> resultCallback) {
-        // Launch the task user scrolled to (mRecentsView.getNextPage()).
-        if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
-            // We finish recents animation inside launchTask() when live tile is enabled.
-            mRecentsView.getNextPageTaskView().launchTask(false /* animate */,
-                    true /* freezeTaskList */);
-        } else {
-            if (!mCanceled) {
-                TaskView nextTask = mRecentsView.getNextPageTaskView();
-                if (nextTask != null) {
-                    int taskId = nextTask.getTask().key.id;
-                    mGestureState.updateLastStartedTaskId(taskId);
-                    boolean hasTaskPreviouslyAppeared = mGestureState.getPreviouslyAppearedTaskIds()
-                            .contains(taskId);
-                    nextTask.launchTask(false /* animate */, true /* freezeTaskList */,
-                            success -> {
-                                resultCallback.accept(success);
-                                if (success) {
-                                    if (hasTaskPreviouslyAppeared) {
-                                        onRestartPreviouslyAppearedTask();
-                                    }
-                                } else {
-                                    mActivityInterface.onLaunchTaskFailed();
-                                    nextTask.notifyTaskLaunchFailed(TAG);
-                                    mRecentsAnimationController.finish(true /* toRecents */, null);
-                                }
-                            }, MAIN_EXECUTOR.getHandler());
-                } else {
-                    mActivityInterface.onLaunchTaskFailed();
-                    Toast.makeText(mContext, R.string.activity_not_available, LENGTH_SHORT).show();
-                    mRecentsAnimationController.finish(true /* toRecents */, null);
-                }
-            }
-            mCanceled = false;
-        }
-    }
-
-    /**
-     * Called when we successfully startNewTask() on the task that was previously running. Normally
-     * we call resumeLastTask() when returning to the previously running task, but this handles a
-     * specific edge case: if we switch from A to B, and back to A before B appears, we need to
-     * start A again to ensure it stays on top.
-     */
-    @CallSuper
-    protected void onRestartPreviouslyAppearedTask() {
-        // Finish the controller here, since we won't get onTaskAppeared() for a task that already
-        // appeared.
-        if (mRecentsAnimationController != null) {
-            mRecentsAnimationController.finish(false, null);
-        }
-    }
-
-    /**
-     * Runs the given {@param action} if the recents animation has already started, or queues it to
-     * be run when it is next started.
-     */
-    protected void runOnRecentsAnimationStart(Runnable action) {
-        if (mRecentsAnimationTargets == null) {
-            mRecentsAnimationStartCallbacks.add(action);
-        } else {
-            action.run();
-        }
-    }
-
-    /**
-     * TODO can we remove this now that we don't finish the controller until onTaskAppeared()?
-     * @return whether the recents animation has started and there are valid app targets.
-     */
-    protected boolean hasTargets() {
-        return mRecentsAnimationTargets != null && mRecentsAnimationTargets.hasTargets();
-    }
-
-    @Override
-    public void onRecentsAnimationStart(RecentsAnimationController recentsAnimationController,
-            RecentsAnimationTargets targets) {
-        mRecentsAnimationController = recentsAnimationController;
-        mRecentsAnimationTargets = targets;
-        mTransformParams.setTargetSet(mRecentsAnimationTargets);
-        RemoteAnimationTargetCompat runningTaskTarget = targets.findTask(
-                mGestureState.getRunningTaskId());
-
-        if (runningTaskTarget != null) {
-            mTaskViewSimulator.setPreview(runningTaskTarget);
-        }
-
-        // 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) {
-            DeviceProfile dp = mTaskViewSimulator.getOrientationState().getLauncherDeviceProfile();
-            if (targets.minimizedHomeBounds != null && runningTaskTarget != null) {
-                Rect overviewStackBounds = mActivityInterface
-                        .getOverviewWindowBounds(targets.minimizedHomeBounds, runningTaskTarget);
-                dp = dp.getMultiWindowProfile(mContext,
-                        new WindowBounds(overviewStackBounds, targets.homeContentInsets));
-            } else {
-                // If we are not in multi-window mode, home insets should be same as system insets.
-                dp = dp.copy(mContext);
-            }
-            dp.updateInsets(targets.homeContentInsets);
-            dp.updateIsSeascape(mContext);
-            initTransitionEndpoints(dp);
-        }
-
-        // Notify when the animation starts
-        if (!mRecentsAnimationStartCallbacks.isEmpty()) {
-            for (Runnable action : new ArrayList<>(mRecentsAnimationStartCallbacks)) {
-                action.run();
-            }
-            mRecentsAnimationStartCallbacks.clear();
-        }
-    }
-
-    @Override
-    public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
-        mRecentsAnimationController = null;
-        mRecentsAnimationTargets = null;
-        if (mRecentsView != null) {
-            mRecentsView.setRecentsAnimationTargets(null, null);
-        }
-    }
-
-    @Override
-    public void onRecentsAnimationFinished(RecentsAnimationController controller) {
-        mRecentsAnimationController = null;
-        mRecentsAnimationTargets = null;
-        if (mRecentsView != null) {
-            mRecentsView.setRecentsAnimationTargets(null, null);
-        }
-    }
-
-    @Override
-    public void onTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {
-        if (mRecentsAnimationController != null) {
-            if (handleTaskAppeared(appearedTaskTarget)) {
-                mRecentsAnimationController.finish(false /* toRecents */,
-                        null /* onFinishComplete */);
-                mActivityInterface.onLaunchTaskSuccess();
-                ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", false);
-            }
-        }
-    }
-
-    /** @return Whether this was the task we were waiting to appear, and thus handled it. */
-    protected abstract boolean handleTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget);
-
-    /**
-     * @return The index of the TaskView in RecentsView whose taskId matches the task that will
-     * resume if we finish the controller.
-     */
-    protected int getLastAppearedTaskIndex() {
-        return mGestureState.getLastAppearedTaskId() != -1
-                ? mRecentsView.getTaskIndexForId(mGestureState.getLastAppearedTaskId())
-                : mRecentsView.getRunningTaskIndex();
-    }
-
-    /**
-     * @return Whether we are continuing a gesture that already landed on a new task,
-     * but before that task appeared.
-     */
-    protected boolean hasStartedNewTask() {
-        return mGestureState.getLastStartedTaskId() != -1;
-    }
-
-    /**
-     * Return true if the window should be translated horizontally if the recents view scrolls
-     */
-    protected abstract boolean moveWindowWithRecentsScroll();
-
-    protected boolean onActivityInit(Boolean alreadyOnHome) {
-        T createdActivity = mActivityInterface.getCreatedActivity();
-        if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.PAUSE_NOT_DETECTED, "BaseSwipeUpHandler.1");
-        }
-        if (createdActivity != null) {
-            if (TestProtocol.sDebugTracing) {
-                Log.d(TestProtocol.PAUSE_NOT_DETECTED, "BaseSwipeUpHandler.2");
-            }
-            initTransitionEndpoints(createdActivity.getDeviceProfile());
-        }
-        return true;
-    }
-
-    /**
-     * Called to create a input proxy for the running task
-     */
-    @UiThread
-    protected abstract InputConsumer createNewInputProxyHandler();
-
-    /**
-     * Called when the value of {@link #mCurrentShift} changes
-     */
-    @UiThread
-    public abstract void updateFinalShift();
-
-    /**
-     * Called when motion pause is detected
-     */
-    public abstract void onMotionPauseChanged(boolean isPaused);
-
-    @UiThread
-    public void onGestureStarted(boolean isLikelyToStartNewTask) { }
-
-    @UiThread
-    public abstract void onGestureCancelled();
-
-    @UiThread
-    public abstract void onGestureEnded(float endVelocity, PointF velocity, PointF downPos);
-
-    public abstract void onConsumerAboutToBeSwitched();
-
-    public void setIsLikelyToStartNewTask(boolean isLikelyToStartNewTask) { }
-
-    /**
-     * Registers a callback to run when the activity is ready.
-     * @param intent The intent that will be used to start the activity if it doesn't exist already.
-     */
-    public void initWhenReady(Intent intent) {
-        // Preload the plan
-        RecentsModel.INSTANCE.get(mContext).getTasks(null);
-
-        mActivityInitListener.register(intent);
-    }
-
-    /**
-     * Applies the transform on the recents animation
-     */
-    protected void applyWindowTransform() {
-        if (mWindowTransitionController != null) {
-            float progress = mCurrentShift.value / mDragLengthFactor;
-            mWindowTransitionController.setPlayFraction(progress);
-        }
-        if (mRecentsAnimationTargets != null) {
-            if (mRecentsViewScrollLinked) {
-                mTaskViewSimulator.setScroll(mRecentsView.getScrollOffset());
-            }
-            mTaskViewSimulator.apply(mTransformParams);
-        }
-    }
-
-    @Override
-    protected RectFSpringAnim createWindowAnimationToHome(float startProgress,
-            HomeAnimationFactory homeAnimationFactory) {
-        RectFSpringAnim anim =
-                super.createWindowAnimationToHome(startProgress, homeAnimationFactory);
-        if (mRecentsAnimationTargets != null) {
-            mRecentsAnimationTargets.addReleaseCheck(anim);
-        }
-        return anim;
-    }
-
-    public interface Factory {
-
-        BaseSwipeUpHandler newHandler(
-                GestureState gestureState, long touchTimeMs, boolean continuingLastGesture);
-    }
-}
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 f60a50b..ffb05df 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -71,7 +71,7 @@
  */
 @TargetApi(Build.VERSION_CODES.R)
 public class FallbackSwipeHandler extends
-        BaseSwipeUpHandlerV2<RecentsActivity, FallbackRecentsView> {
+        AbsSwipeUpHandler<RecentsActivity, FallbackRecentsView> {
 
     /**
      * Message used for receiving gesture nav contract information. We use a static messenger to
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandlerV2.java
index 052d0a6..4411455 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandlerV2.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandlerV2.java
@@ -39,7 +39,7 @@
  * Temporary class to allow easier refactoring
  */
 public class LauncherSwipeHandlerV2 extends
-        BaseSwipeUpHandlerV2<BaseQuickstepLauncher, RecentsView> {
+        AbsSwipeUpHandler<BaseQuickstepLauncher, RecentsView> {
 
     public LauncherSwipeHandlerV2(Context context, RecentsAnimationDeviceState deviceState,
             TaskAnimationManager taskAnimationManager, GestureState gestureState, long touchTimeMs,
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
index e5852be..1012ab2 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -269,9 +269,9 @@
         return sIsInitialized;
     }
 
-    private final BaseSwipeUpHandler.Factory mLauncherSwipeHandlerFactory =
+    private final AbsSwipeUpHandler.Factory mLauncherSwipeHandlerFactory =
             this::createLauncherSwipeHandler;
-    private final BaseSwipeUpHandler.Factory mFallbackSwipeHandlerFactory =
+    private final AbsSwipeUpHandler.Factory mFallbackSwipeHandlerFactory =
             this::createFallbackSwipeHandler;
 
     private ActivityManagerWrapper mAM;
@@ -716,7 +716,7 @@
     private InputConsumer createOtherActivityInputConsumer(GestureState gestureState,
             MotionEvent event) {
 
-        final BaseSwipeUpHandler.Factory factory;
+        final AbsSwipeUpHandler.Factory factory;
         if (!mOverviewComponentObserver.isHomeAndOverviewSame()) {
             factory = mFallbackSwipeHandlerFactory;
         } else {
@@ -889,13 +889,13 @@
         }
     }
 
-    private BaseSwipeUpHandler createLauncherSwipeHandler(
+    private AbsSwipeUpHandler createLauncherSwipeHandler(
             GestureState gestureState, long touchTimeMs, boolean continuingLastGesture) {
         return new LauncherSwipeHandlerV2(this, mDeviceState, mTaskAnimationManager,
                 gestureState, touchTimeMs, continuingLastGesture, mInputConsumer);
     }
 
-    private BaseSwipeUpHandler createFallbackSwipeHandler(
+    private AbsSwipeUpHandler createFallbackSwipeHandler(
             GestureState gestureState, long touchTimeMs, boolean continuingLastGesture) {
         return new FallbackSwipeHandler(this, mDeviceState, mTaskAnimationManager,
                 gestureState, touchTimeMs, continuingLastGesture, mInputConsumer);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
index a676390..db1948b 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
@@ -21,7 +21,7 @@
 
 import static com.android.launcher3.Utilities.squaredHypot;
 import static com.android.launcher3.Utilities.squaredTouchSlop;
-import static com.android.quickstep.BaseSwipeUpHandlerV2.MIN_PROGRESS_FOR_OVERVIEW;
+import static com.android.quickstep.AbsSwipeUpHandler.MIN_PROGRESS_FOR_OVERVIEW;
 import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
 import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID;
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index 6259f1f..f02e5e6 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -53,8 +53,8 @@
 import com.android.launcher3.util.Preconditions;
 import com.android.launcher3.util.TraceHelper;
 import com.android.quickstep.BaseActivityInterface;
-import com.android.quickstep.BaseSwipeUpHandler;
-import com.android.quickstep.BaseSwipeUpHandler.Factory;
+import com.android.quickstep.AbsSwipeUpHandler;
+import com.android.quickstep.AbsSwipeUpHandler.Factory;
 import com.android.quickstep.GestureState;
 import com.android.quickstep.InputConsumer;
 import com.android.quickstep.RecentsAnimationCallbacks;
@@ -93,7 +93,7 @@
     private final InputMonitorCompat mInputMonitorCompat;
     private final BaseActivityInterface mActivityInterface;
 
-    private final BaseSwipeUpHandler.Factory mHandlerFactory;
+    private final AbsSwipeUpHandler.Factory mHandlerFactory;
 
     private final Consumer<OtherActivityInputConsumer> mOnCompleteCallback;
     private final MotionPauseDetector mMotionPauseDetector;
@@ -101,7 +101,7 @@
 
     private VelocityTracker mVelocityTracker;
 
-    private BaseSwipeUpHandler mInteractionHandler;
+    private AbsSwipeUpHandler mInteractionHandler;
 
     private final boolean mIsDeferredDownTarget;
     private final PointF mDownPos = new PointF();
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/InputConsumerProxy.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/InputConsumerProxy.java
new file mode 100644
index 0000000..3e87f48
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/InputConsumerProxy.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.util;
+
+import static android.view.MotionEvent.ACTION_CANCEL;
+import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_UP;
+
+import android.util.Log;
+import android.view.InputEvent;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+import com.android.quickstep.InputConsumer;
+import com.android.systemui.shared.system.InputConsumerController;
+
+import java.util.function.Supplier;
+
+/**
+ * Utility class which manages proxying input events from {@link InputConsumerController}
+ * to an {@link InputConsumer}
+ */
+public class InputConsumerProxy {
+
+    private static final String TAG = "InputConsumerProxy";
+
+    private final InputConsumerController mInputConsumerController;
+    private final Supplier<InputConsumer> mConsumerSupplier;
+
+    // The consumer is created lazily on demand.
+    private InputConsumer mInputConsumer;
+
+    private boolean mDestroyed = false;
+    private boolean mTouchInProgress = false;
+    private boolean mDestroyPending = false;
+
+    public InputConsumerProxy(InputConsumerController inputConsumerController,
+            Supplier<InputConsumer> consumerSupplier) {
+        mInputConsumerController = inputConsumerController;
+        mConsumerSupplier = consumerSupplier;
+    }
+
+    public void enable() {
+        if (mDestroyed) {
+            return;
+        }
+        mInputConsumerController.setInputListener(this::onInputConsumerEvent);
+    }
+
+    private boolean onInputConsumerEvent(InputEvent ev) {
+        if (ev instanceof MotionEvent) {
+            onInputConsumerMotionEvent((MotionEvent) ev);
+        } else if (ev instanceof KeyEvent) {
+            if (mInputConsumer == null) {
+                mInputConsumer = mConsumerSupplier.get();
+            }
+            mInputConsumer.onKeyEvent((KeyEvent) ev);
+            return true;
+        }
+        return false;
+    }
+
+    private boolean onInputConsumerMotionEvent(MotionEvent ev) {
+        int action = ev.getAction();
+
+        // Just to be safe, verify that ACTION_DOWN comes before any other action,
+        // and ignore any ACTION_DOWN after the first one (though that should not happen).
+        if (!mTouchInProgress && action != ACTION_DOWN) {
+            Log.w(TAG, "Received non-down motion before down motion: " + action);
+            return false;
+        }
+        if (mTouchInProgress && action == ACTION_DOWN) {
+            Log.w(TAG, "Received down motion while touch was already in progress");
+            return false;
+        }
+
+        if (action == ACTION_DOWN) {
+            mTouchInProgress = true;
+            if (mInputConsumer == null) {
+                mInputConsumer = mConsumerSupplier.get();
+            }
+        } else if (action == ACTION_CANCEL || action == ACTION_UP) {
+            // Finish any pending actions
+            mTouchInProgress = false;
+            if (mDestroyPending) {
+                destroy();
+            }
+        }
+        if (mInputConsumer != null) {
+            mInputConsumer.onMotionEvent(ev);
+        }
+
+        return true;
+    }
+
+    public void destroy() {
+        if (mTouchInProgress) {
+            mDestroyPending = true;
+            return;
+        }
+        mDestroyPending = false;
+        mDestroyed = true;
+        mInputConsumerController.setInputListener(null);
+    }
+}
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 105b62a..2de4036 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
@@ -184,7 +184,7 @@
     }
 
     /**
-     * Adds animation for all the components corresponding to transition from an app to overview
+     * Adds animation for all the components corresponding to transition from an app to overview.
      */
     public void addAppToOverviewAnim(PendingAnimation pa, TimeInterpolator interpolator) {
         pa.addFloat(fullScreenProgress, AnimatedFloat.VALUE, 1, 0, interpolator);
@@ -192,6 +192,14 @@
     }
 
     /**
+     * Adds animation for all the components corresponding to transition from overview to the app.
+     */
+    public void addOverviewToAppAnim(PendingAnimation pa, TimeInterpolator interpolator) {
+        pa.addFloat(fullScreenProgress, AnimatedFloat.VALUE, 0, 1, interpolator);
+        pa.addFloat(recentsViewScale, AnimatedFloat.VALUE, 1, getFullScreenScale(), interpolator);
+    }
+
+    /**
      * Returns the current clipped/visible window bounds in the window coordinate space
      */
     public RectF getCurrentCropRect() {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
index 0fcd74b..f31bc19 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -38,11 +38,9 @@
 import com.android.launcher3.BaseQuickstepLauncher;
 import com.android.launcher3.Hotseat;
 import com.android.launcher3.LauncherState;
-import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.statehandlers.DepthController;
 import com.android.launcher3.statemanager.StateManager.StateListener;
 import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
-import com.android.launcher3.views.ScrimView;
 import com.android.quickstep.LauncherActivityInterface;
 import com.android.quickstep.SysUINavigationMode;
 import com.android.systemui.plugins.PluginListener;
@@ -126,29 +124,10 @@
         }
         anim.play(ObjectAnimator.ofFloat(
                 mActivity.getAllAppsController(), ALL_APPS_PROGRESS, allAppsProgressOffscreen));
-
-        ObjectAnimator dragHandleAnim = ObjectAnimator.ofInt(
-                mActivity.getScrimView(), ScrimView.DRAG_HANDLE_ALPHA, 0);
-        dragHandleAnim.setInterpolator(Interpolators.ACCEL_2);
-        anim.play(dragHandleAnim);
-
         return anim;
     }
 
     @Override
-    protected void onTaskLaunchAnimationUpdate(float progress, TaskView tv) {
-        if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
-            if (tv.isRunningTask()) {
-                mLiveTileParams.setProgress(1 - progress)
-                        .setSyncTransactionApplier(mSyncTransactionApplier);
-                // TODO: Revisit live tiles
-            } else {
-                redrawLiveTile();
-            }
-        }
-    }
-
-    @Override
     protected void onTaskLaunchAnimationEnd(boolean success) {
         if (success) {
             mActivity.getStateManager().goToState(NORMAL, false /* animate */);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
index 49a9db9..b478161 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
@@ -1978,8 +1978,6 @@
                             ? targetSysUiFlags
                             : 0);
 
-            onTaskLaunchAnimationUpdate(animator.getAnimatedFraction(), tv);
-
             // Passing the threshold from taskview to fullscreen app will vibrate
             final boolean passed = animator.getAnimatedFraction() >=
                     SUCCESS_TRANSITION_PROGRESS;
@@ -2003,6 +2001,10 @@
 
         mPendingAnimation = new PendingAnimation(duration);
         mPendingAnimation.add(anim);
+        if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+            mLiveTileTaskViewSimulator.addOverviewToAppAnim(mPendingAnimation, interpolator);
+            mPendingAnimation.addOnFrameCallback(this::redrawLiveTile);
+        }
         mPendingAnimation.addEndListener((endState) -> {
             if (endState.isSuccess) {
                 Consumer<Boolean> onLaunchResult = (result) -> {
@@ -2028,9 +2030,6 @@
         return mPendingAnimation;
     }
 
-    protected void onTaskLaunchAnimationUpdate(float progress, TaskView tv) {
-    }
-
     protected void onTaskLaunchAnimationEnd(boolean success) {
         if (success) {
             resetTaskVisuals();
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index f4a394a..0ae386f 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -20,7 +20,7 @@
 import static com.android.launcher3.anim.Interpolators.INSTANT;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
-import static com.android.quickstep.BaseSwipeUpHandlerV2.RECENTS_ATTACH_DURATION;
+import static com.android.quickstep.AbsSwipeUpHandler.RECENTS_ATTACH_DURATION;
 import static com.android.quickstep.SysUINavigationMode.getMode;
 import static com.android.quickstep.SysUINavigationMode.hideShelfInTwoButtonLandscape;
 import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
index 4e9aa61..51f5e5d 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
@@ -15,49 +15,30 @@
  */
 package com.android.quickstep;
 
-import static android.view.MotionEvent.ACTION_CANCEL;
-import static android.view.MotionEvent.ACTION_DOWN;
-import static android.view.MotionEvent.ACTION_UP;
-
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 
-import android.os.SystemClock;
-import android.util.Log;
-import android.view.InputEvent;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-
 import androidx.annotation.NonNull;
 import androidx.annotation.UiThread;
 
 import com.android.launcher3.util.Preconditions;
 import com.android.systemui.shared.recents.model.ThumbnailData;
-import com.android.systemui.shared.system.InputConsumerController;
 import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
 import java.util.function.Consumer;
-import java.util.function.Supplier;
 
 /**
  * Wrapper around RecentsAnimationControllerCompat to help with some synchronization
  */
 public class RecentsAnimationController {
 
-    private static final String TAG = "RecentsAnimationController";
-
     private final RecentsAnimationControllerCompat mController;
     private final Consumer<RecentsAnimationController> mOnFinishedListener;
     private final boolean mAllowMinimizeSplitScreen;
 
-    private InputConsumerController mInputConsumerController;
-    private Supplier<InputConsumer> mInputProxySupplier;
-    private InputConsumer mInputConsumer;
     private boolean mUseLauncherSysBarFlags = false;
     private boolean mSplitScreenMinimized = false;
-    private boolean mTouchInProgress;
-    private boolean mDisableInputProxyPending;
 
     public RecentsAnimationController(RecentsAnimationControllerCompat controller,
             boolean allowMinimizeSplitScreen,
@@ -136,12 +117,12 @@
 
     @UiThread
     public void finishAnimationToHome() {
-        finishAndDisableInputProxy(true /* toRecents */, null, false /* sendUserLeaveHint */);
+        finishController(true /* toRecents */, null, false /* sendUserLeaveHint */);
     }
 
     @UiThread
     public void finishAnimationToApp() {
-        finishAndDisableInputProxy(false /* toRecents */, null, false /* sendUserLeaveHint */);
+        finishController(false /* toRecents */, null, false /* sendUserLeaveHint */);
     }
 
     /** See {@link #finish(boolean, Runnable, boolean)} */
@@ -160,18 +141,6 @@
     @UiThread
     public void finish(boolean toRecents, Runnable onFinishComplete, boolean sendUserLeaveHint) {
         Preconditions.assertUIThread();
-        if (toRecents && mTouchInProgress) {
-            // Finish the controller as requested, but don't disable input proxy yet.
-            mDisableInputProxyPending = true;
-            finishController(toRecents, onFinishComplete, sendUserLeaveHint);
-        } else {
-            finishAndDisableInputProxy(toRecents, onFinishComplete, sendUserLeaveHint);
-        }
-    }
-
-    private void finishAndDisableInputProxy(boolean toRecents, Runnable onFinishComplete,
-            boolean sendUserLeaveHint) {
-        disableInputProxy();
         finishController(toRecents, onFinishComplete, sendUserLeaveHint);
     }
 
@@ -179,7 +148,6 @@
     public void finishController(boolean toRecents, Runnable callback, boolean sendUserLeaveHint) {
         mOnFinishedListener.accept(this);
         UI_HELPER_EXECUTOR.execute(() -> {
-            mController.setInputConsumerEnabled(false);
             mController.finish(toRecents, sendUserLeaveHint);
             if (callback != null) {
                 MAIN_EXECUTOR.execute(callback);
@@ -197,75 +165,8 @@
         });
     }
 
-    public void enableInputProxy(InputConsumerController inputConsumerController,
-            Supplier<InputConsumer> inputProxySupplier) {
-        mInputProxySupplier = inputProxySupplier;
-        mInputConsumerController = inputConsumerController;
-        mInputConsumerController.setInputListener(this::onInputConsumerEvent);
-    }
-
     /** @return wrapper controller. */
     public RecentsAnimationControllerCompat getController() {
         return mController;
     }
-
-    private void disableInputProxy() {
-        if (mInputConsumer != null && mTouchInProgress) {
-            long now = SystemClock.uptimeMillis();
-            MotionEvent dummyCancel = MotionEvent.obtain(now,  now, ACTION_CANCEL, 0, 0, 0);
-            mInputConsumer.onMotionEvent(dummyCancel);
-            dummyCancel.recycle();
-        }
-        if (mInputConsumerController != null) {
-            mInputConsumerController.setInputListener(null);
-        }
-        mInputProxySupplier = null;
-    }
-
-    private boolean onInputConsumerEvent(InputEvent ev) {
-        if (ev instanceof MotionEvent) {
-            onInputConsumerMotionEvent((MotionEvent) ev);
-        } else if (ev instanceof KeyEvent) {
-            if (mInputConsumer == null) {
-                mInputConsumer = mInputProxySupplier.get();
-            }
-            mInputConsumer.onKeyEvent((KeyEvent) ev);
-            return true;
-        }
-        return false;
-    }
-
-    private boolean onInputConsumerMotionEvent(MotionEvent ev) {
-        int action = ev.getAction();
-
-        // Just to be safe, verify that ACTION_DOWN comes before any other action,
-        // and ignore any ACTION_DOWN after the first one (though that should not happen).
-        if (!mTouchInProgress && action != ACTION_DOWN) {
-            Log.w(TAG, "Received non-down motion before down motion: " + action);
-            return false;
-        }
-        if (mTouchInProgress && action == ACTION_DOWN) {
-            Log.w(TAG, "Received down motion while touch was already in progress");
-            return false;
-        }
-
-        if (action == ACTION_DOWN) {
-            mTouchInProgress = true;
-            if (mInputConsumer == null) {
-                mInputConsumer = mInputProxySupplier.get();
-            }
-        } else if (action == ACTION_CANCEL || action == ACTION_UP) {
-            // Finish any pending actions
-            mTouchInProgress = false;
-            if (mDisableInputProxyPending) {
-                mDisableInputProxyPending = false;
-                disableInputProxy();
-            }
-        }
-        if (mInputConsumer != null) {
-            mInputConsumer.onMotionEvent(ev);
-        }
-
-        return true;
-    }
 }
diff --git a/quickstep/src/com/android/quickstep/SysUINavigationMode.java b/quickstep/src/com/android/quickstep/SysUINavigationMode.java
index 6994d66..6b50218 100644
--- a/quickstep/src/com/android/quickstep/SysUINavigationMode.java
+++ b/quickstep/src/com/android/quickstep/SysUINavigationMode.java
@@ -17,6 +17,9 @@
 package com.android.quickstep;
 
 import static com.android.launcher3.ResourceUtils.INVALID_RESOURCE_HANDLE;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NAVIGATION_MODE_2_BUTTON;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NAVIGATION_MODE_3_BUTTON;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NAVIGATION_MODE_GESTURE_BUTTON;
 import static com.android.launcher3.util.PackageManagerHelper.getPackageFilter;
 
 import android.content.BroadcastReceiver;
@@ -25,6 +28,7 @@
 import android.util.Log;
 
 import com.android.launcher3.ResourceUtils;
+import com.android.launcher3.logging.StatsLogManager.LauncherEvent;
 import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.launcher3.util.MainThreadInitializedObject;
 
@@ -38,16 +42,18 @@
 public class SysUINavigationMode {
 
     public enum Mode {
-        THREE_BUTTONS(false, 0),
-        TWO_BUTTONS(true, 1),
-        NO_BUTTON(true, 2);
+        THREE_BUTTONS(false, 0, LAUNCHER_NAVIGATION_MODE_3_BUTTON),
+        TWO_BUTTONS(true, 1, LAUNCHER_NAVIGATION_MODE_2_BUTTON),
+        NO_BUTTON(true, 2, LAUNCHER_NAVIGATION_MODE_GESTURE_BUTTON);
 
         public final boolean hasGestures;
         public final int resValue;
+        public final LauncherEvent launcherEvent;
 
-        Mode(boolean hasGestures, int resValue) {
+        Mode(boolean hasGestures, int resValue, LauncherEvent launcherEvent) {
             this.hasGestures = hasGestures;
             this.resValue = resValue;
+            this.launcherEvent = launcherEvent;
         }
     }
 
@@ -183,12 +189,10 @@
     }
 
     public interface NavigationModeChangeListener {
-
         void onNavigationModeChanged(Mode newMode);
     }
 
     public interface OneHandedModeChangeListener {
-
         void onOneHandedModeChanged(int newGesturalHeight);
     }
 }
\ No newline at end of file
diff --git a/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java b/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
index 0e2312b..81c4d0c 100644
--- a/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
+++ b/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
@@ -16,6 +16,7 @@
 package com.android.quickstep.interaction;
 
 import static com.android.launcher3.Utilities.squaredHypot;
+import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
 import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.ASSISTANT_COMPLETED;
 import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.ASSISTANT_NOT_STARTED_BAD_ANGLE;
 import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.ASSISTANT_NOT_STARTED_SWIPE_TOO_SHORT;
@@ -48,6 +49,7 @@
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.util.VibratorWrapper;
 import com.android.quickstep.SysUINavigationMode.Mode;
+import com.android.quickstep.util.MotionPauseDetector;
 import com.android.quickstep.util.NavBarPosition;
 import com.android.quickstep.util.TriggerSwipeUpTouchTracker;
 import com.android.systemui.shared.system.QuickStepContract;
@@ -74,6 +76,7 @@
     private final PointF mAssistantStartDragPos = new PointF();
     private final PointF mDownPos = new PointF();
     private final PointF mLastPos = new PointF();
+    private final MotionPauseDetector mMotionPauseDetector;
     private boolean mTouchCameFromAssistantCorner;
     private boolean mTouchCameFromNavBar;
     private boolean mPassedAssistantSlop;
@@ -100,6 +103,7 @@
                 new TriggerSwipeUpTouchTracker(context, true /*disableHorizontalSwipe*/,
                         new NavBarPosition(Mode.NO_BUTTON, displayRotation),
                         null /*onInterceptTouch*/, this);
+        mMotionPauseDetector = new MotionPauseDetector(context);
 
         final Resources resources = context.getResources();
         mBottomGestureHeight =
@@ -177,12 +181,14 @@
                 }
                 mLaunchedAssistant = false;
                 mSwipeUpTouchTracker.init();
+                mMotionPauseDetector.clear();
+                mMotionPauseDetector.setOnMotionPauseListener(this::onMotionPauseChanged);
                 break;
             case MotionEvent.ACTION_MOVE:
+                mLastPos.set(event.getX(), event.getY());
                 if (!mAssistantGestureActive) {
                     break;
                 }
-                mLastPos.set(event.getX(), event.getY());
 
                 if (!mPassedAssistantSlop) {
                     // Normal gesture, ensure we pass the slop before we start tracking the gesture
@@ -213,6 +219,8 @@
                 break;
             case MotionEvent.ACTION_UP:
             case MotionEvent.ACTION_CANCEL:
+                mMotionPauseDetector.clear();
+                mMotionPauseDetector.setOnMotionPauseListener(null);
                 if (mGestureCallback != null && !intercepted && mTouchCameFromNavBar) {
                     mGestureCallback.onNavBarGestureAttempted(
                             HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION, new PointF());
@@ -239,9 +247,17 @@
         }
         mSwipeUpTouchTracker.onMotionEvent(event);
         mAssistantGestureDetector.onTouchEvent(event);
+        mMotionPauseDetector.addPosition(event);
+        mMotionPauseDetector.setDisallowPause(mLastPos.y >= mDisplaySize.y - mBottomGestureHeight);
         return intercepted;
     }
 
+    protected void onMotionPauseChanged(boolean isPaused) {
+        if (isPaused) {
+            VibratorWrapper.INSTANCE.get(mContext).vibrate(OVERVIEW_HAPTIC);
+        }
+    }
+
     /**
      * Determine if angle is larger than threshold for assistant detection
      */
diff --git a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
index 4110b33..044e010 100644
--- a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
@@ -18,7 +18,7 @@
 import static com.android.launcher3.anim.Interpolators.ACCEL;
 import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
 import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
-import static com.android.quickstep.BaseSwipeUpHandlerV2.MAX_SWIPE_DURATION;
+import static com.android.quickstep.AbsSwipeUpHandler.MAX_SWIPE_DURATION;
 import static com.android.quickstep.interaction.TutorialController.TutorialType.HOME_NAVIGATION_COMPLETE;
 import static com.android.quickstep.interaction.TutorialController.TutorialType.OVERVIEW_NAVIGATION_COMPLETE;
 
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index b978c09..059d158 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -59,6 +59,7 @@
 import com.android.systemui.shared.system.SysUiStatsLog;
 
 import java.util.ArrayList;
+import java.util.List;
 import java.util.Optional;
 import java.util.OptionalInt;
 import java.util.concurrent.CopyOnWriteArrayList;
@@ -100,21 +101,22 @@
     }
 
     /**
-     * Logs the workspace layout information on the model thread.
+     * Logs impression of the current workspace with additional launcher events.
      */
     @Override
-    public void logSnapshot() {
+    public void logSnapshot(List<EventEnum> extraEvents) {
         LauncherAppState.getInstance(mContext).getModel().enqueueModelUpdateTask(
-                new SnapshotWorker());
+                new SnapshotWorker(extraEvents));
     }
 
     private class SnapshotWorker extends BaseModelUpdateTask {
-
         private final InstanceId mInstanceId;
+        private final List<EventEnum> mExtraEvents;
 
-        SnapshotWorker() {
-            mInstanceId = new InstanceIdSequence(
-                    1 << 20 /*InstanceId.INSTANCE_ID_MAX*/).newInstanceId();
+        SnapshotWorker(List<EventEnum> extraEvents) {
+            mInstanceId = new InstanceIdSequence(1 << 20 /*InstanceId.INSTANCE_ID_MAX*/)
+                    .newInstanceId();
+            this.mExtraEvents = extraEvents;
         }
 
         @Override
@@ -155,6 +157,9 @@
                 LauncherAtom.ItemInfo atomInfo = info.buildProto(null);
                 writeSnapshot(atomInfo, mInstanceId);
             }
+            mExtraEvents
+                    .forEach(eventName -> logger().withInstanceId(mInstanceId).log(eventName));
+
             getDevicePrefs(mContext).edit()
                     .putLong(LAST_SNAPSHOT_TIME_MILLIS, currentTimeMillis()).apply();
         }
diff --git a/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java b/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
index 9ed2bbe..c2e67c1 100644
--- a/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
+++ b/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
@@ -87,20 +87,6 @@
             });
         }
 
-        if (!hasReachedMaxCount(ALL_APPS_COUNT)) {
-            stateManager.addStateListener(new StateListener<LauncherState>() {
-                @Override
-                public void onStateTransitionComplete(LauncherState finalState) {
-                    if (finalState == ALL_APPS) {
-                        if (incrementEventCount(ALL_APPS_COUNT)) {
-                            stateManager.removeStateListener(this);
-                            mLauncher.getScrimView().updateDragHandleVisibility();
-                        }
-                    }
-                }
-            });
-        }
-
         if (FeatureFlags.ENABLE_HYBRID_HOTSEAT.get() && !hasReachedMaxCount(
                 HOTSEAT_DISCOVERY_TIP_COUNT)) {
             stateManager.addStateListener(new StateListener<LauncherState>() {
diff --git a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
index 19e278b..f1ac6a5 100644
--- a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
+++ b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
@@ -44,7 +44,6 @@
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.uioverrides.states.OverviewState;
-import com.android.launcher3.util.OnboardingPrefs;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.views.ScrimView;
 import com.android.quickstep.SysUINavigationMode;
@@ -77,15 +76,11 @@
     private final float mRadius;
     private final int mMaxScrimAlpha;
     private final Paint mPaint;
-    private final OnboardingPrefs mOnboardingPrefs;
 
     // Mid point where the alpha changes
     private int mMidAlpha;
     private float mMidProgress;
 
-    // The progress at which the drag handle starts moving up with the shelf.
-    private float mDragHandleProgress;
-
     private Interpolator mBeforeMidProgressColorInterpolator = ACCEL;
     private Interpolator mAfterMidProgressColorInterpolator = ACCEL;
 
@@ -103,7 +98,6 @@
     private boolean mRemainingScreenPathValid = false;
 
     private Mode mSysUINavigationMode;
-    private boolean mIsTwoZoneSwipeModel;
 
     public ShelfScrimView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -112,7 +106,6 @@
         mEndAlpha = Color.alpha(mEndScrim);
         mRadius = BOTTOM_CORNER_RADIUS_RATIO * Themes.getDialogCornerRadius(context);
         mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
-        mOnboardingPrefs = mLauncher.getOnboardingPrefs();
 
         // Just assume the easiest UI for now, until we have the proper layout information.
         mDrawingFlatColor = true;
@@ -145,11 +138,9 @@
             // Show the shelf more quickly before reaching overview progress.
             mBeforeMidProgressColorInterpolator = ACCEL_2;
             mAfterMidProgressColorInterpolator = ACCEL;
-            mIsTwoZoneSwipeModel = FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get();
         } else {
             mBeforeMidProgressColorInterpolator = ACCEL;
             mAfterMidProgressColorInterpolator = Interpolators.clampToProgress(ACCEL, 0.5f, 1f);
-            mIsTwoZoneSwipeModel = false;
         }
     }
 
@@ -164,7 +155,6 @@
 
             Context context = getContext();
             if ((OVERVIEW.getVisibleElements(mLauncher) & ALL_APPS_HEADER_EXTRA) == 0) {
-                mDragHandleProgress = 1;
                 if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get()
                         && SysUINavigationMode.removeShelfFromOverview(context)) {
                     // Fade in all apps background quickly to distinguish from swiping from nav bar.
@@ -182,29 +172,22 @@
                         + hotseatPadding.bottom + hotseatPadding.top;
                 float dragHandleTop =
                         Math.min(hotseatSize, LayoutUtils.getDefaultSwipeHeight(context, dp));
-                mDragHandleProgress =  1 - (dragHandleTop / mShiftRange);
             }
-            mTopOffset = dp.getInsets().top - mDragHandleSize.y;
+            mTopOffset = dp.getInsets().top;
             mShelfTopAtThreshold = mShiftRange * SCRIM_CATCHUP_THRESHOLD + mTopOffset;
         }
         updateColors();
         updateSysUiColors();
-        updateDragHandleAlpha();
         invalidate();
     }
 
     @Override
     public void updateColors() {
         super.updateColors();
-        mDragHandleOffset = 0;
         if (mDrawingFlatColor) {
             return;
         }
 
-        if (mProgress < mDragHandleProgress) {
-            mDragHandleOffset = mShiftRange * (mDragHandleProgress - mProgress);
-        }
-
         if (mProgress >= SCRIM_CATCHUP_THRESHOLD) {
             mShelfTop = mShiftRange * mProgress + mTopOffset;
         } else {
@@ -259,19 +242,7 @@
     }
 
     @Override
-    protected boolean shouldDragHandleBeVisible() {
-        boolean needsAllAppsEdu = mIsTwoZoneSwipeModel
-                && !mOnboardingPrefs.hasReachedMaxCount(OnboardingPrefs.ALL_APPS_COUNT);
-        return needsAllAppsEdu || super.shouldDragHandleBeVisible();
-    }
-
-    @Override
     protected void onDraw(Canvas canvas) {
-        drawBackground(canvas);
-        drawDragHandle(canvas);
-    }
-
-    private void drawBackground(Canvas canvas) {
         if (mDrawingFlatColor) {
             if (mCurrentFlatColor != 0) {
                 canvas.drawColor(mCurrentFlatColor);
@@ -311,9 +282,4 @@
         mPaint.setColor(mShelfColor);
         canvas.drawRoundRect(0, mShelfTop, width, height + mRadius, mRadius, mRadius, mPaint);
     }
-
-    @Override
-    public float getVisualTop() {
-        return mShelfTop;
-    }
 }
diff --git a/res/drawable-v24/drag_handle_indicator_shadow.xml b/res/drawable-v24/drag_handle_indicator_shadow.xml
deleted file mode 100644
index 774bc38..0000000
--- a/res/drawable-v24/drag_handle_indicator_shadow.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2020 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-        http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<com.android.launcher3.graphics.ShadowDrawable
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/drag_handle_indicator_no_shadow"
-    android:elevation="@dimen/vertical_drag_handle_elevation" />
diff --git a/res/drawable/drag_handle_indicator_no_shadow.xml b/res/drawable/drag_handle_indicator_no_shadow.xml
deleted file mode 100644
index 341e60c..0000000
--- a/res/drawable/drag_handle_indicator_no_shadow.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2020 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="@dimen/vertical_drag_handle_width"
-    android:height="@dimen/vertical_drag_handle_height"
-    android:viewportWidth="18.0"
-    android:viewportHeight="6.0"
-    android:tint="?attr/workspaceTextColor" >
-
-    <path
-        android:pathData="M17,6c-0.15,0-0.3-0.03-0.45-0.11L9,2.12L1.45,5.89c-0.5,0.25-1.09,
-        0.05-1.34-0.45S0.06,4.35,0.55,4.11l8-4c0.28-0.14,0.61-0.14,0.89,0l8,4c0.49,0.25,0.69,
-        0.85,0.45,1.34C17.72,5.8,17.37,6,17,6z"
-        android:fillColor="@android:color/white" />
-</vector>
diff --git a/res/layout/launcher.xml b/res/layout/launcher.xml
index a137908..0c18c8a 100644
--- a/res/layout/launcher.xml
+++ b/res/layout/launcher.xml
@@ -28,6 +28,12 @@
         android:clipToPadding="false"
         android:importantForAccessibility="no">
 
+        <com.android.launcher3.views.AccessibilityActionsView
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:contentDescription="@string/home_screen"
+            />
+
         <!-- The workspace contains 5 screens of cells -->
         <!-- DO NOT CHANGE THE ID -->
         <com.android.launcher3.Workspace
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 969765f..f59f02f 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -40,14 +40,6 @@
     <dimen name="workspace_page_indicator_line_height">1dp</dimen>
     <dimen name="workspace_page_indicator_overlap_workspace">0dp</dimen>
 
-    <!-- Hotseat/all-apps scrim -->
-    <dimen name="all_apps_scrim_blur">4dp</dimen>
-    <dimen name="vertical_drag_handle_width">18dp</dimen>
-    <dimen name="vertical_drag_handle_height">6dp</dimen>
-    <dimen name="vertical_drag_handle_elevation">1dp</dimen>
-    <dimen name="vertical_drag_handle_touch_size">48dp</dimen>
-    <dimen name="vertical_drag_handle_padding_in_vertical_bar_layout">16dp</dimen>
-
 <!-- Drop target bar -->
     <dimen name="dynamic_grid_drop_target_size">48dp</dimen>
     <dimen name="drop_target_vertical_gap">20dp</dimen>
diff --git a/res/values/drawables.xml b/res/values/drawables.xml
index 7d63142..9c57ec1 100644
--- a/res/values/drawables.xml
+++ b/res/values/drawables.xml
@@ -18,5 +18,4 @@
     <drawable name="ic_remove_shadow">@drawable/ic_remove_no_shadow</drawable>
     <drawable name="ic_uninstall_shadow">@drawable/ic_uninstall_no_shadow</drawable>
     <drawable name="ic_block_shadow">@drawable/ic_block_no_shadow</drawable>
-    <drawable name="all_apps_arrow_shadow">@drawable/drag_handle_indicator_no_shadow</drawable>
 </resources>
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 0ec0581..ef47eef 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -36,7 +36,7 @@
     <!-- Message shown when a shortcut is not available. It could have been temporarily disabled and may start working again after some time. -->
     <string name="shortcut_not_available">Shortcut isn\'t available</string>
     <!-- User visible name for the launcher/home screen. [CHAR_LIMIT=30] -->
-    <string name="home_screen">Home screen</string>
+    <string name="home_screen">Home</string>
     <!-- Label for showing custom action list of a shortcut or widget. [CHAR_LIMIT=30] -->
     <string name="custom_actions">Custom actions</string>
 
@@ -92,10 +92,6 @@
     <string name="all_apps_button_personal_label">Personal apps list</string>
     <string name="all_apps_button_work_label">Work apps list</string>
 
-    <!-- Label for button in all applications label to go back home (to the workspace / desktop)
-         for accessibilty (spoken when the button gets focus). -->
-    <string name="all_apps_home_button_label">Home</string>
-
     <!-- Label for remove drop target (from the homescreen only).
          May appear next to uninstall_drop_target_label [CHAR_LIMIT=20] -->
     <string name="remove_drop_target_label">Remove</string>
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index e49c455..f9fba6f 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -453,10 +453,10 @@
                     mAppsView.getAlphaProperty(APPS_VIEW_ALPHA_CHANNEL_INDEX).setValue(alpha);
                 } else if (finalState == OVERVIEW || finalState == OVERVIEW_PEEK) {
                     mAppsView.getAlphaProperty(APPS_VIEW_ALPHA_CHANNEL_INDEX).setValue(alpha);
-                    mScrimView.getAlphaProperty(SCRIM_VIEW_ALPHA_CHANNEL_INDEX).setValue(alpha);
+                    mScrimView.setAlpha(alpha);
                 } else {
                     mAppsView.getAlphaProperty(APPS_VIEW_ALPHA_CHANNEL_INDEX).setValue(1f);
-                    mScrimView.getAlphaProperty(SCRIM_VIEW_ALPHA_CHANNEL_INDEX).setValue(1f);
+                    mScrimView.setAlpha(1f);
                 }
             }
         });
@@ -553,7 +553,7 @@
             mAppsView.getAlphaProperty(APPS_VIEW_ALPHA_CHANNEL_INDEX).setValue(alpha);
         } else if (state == OVERVIEW || state == OVERVIEW_PEEK) {
             mAppsView.getAlphaProperty(APPS_VIEW_ALPHA_CHANNEL_INDEX).setValue(alpha);
-            mScrimView.getAlphaProperty(SCRIM_VIEW_ALPHA_CHANNEL_INDEX).setValue(alpha);
+            mScrimView.setAlpha(alpha);
         }
     }
 
@@ -1936,7 +1936,7 @@
         // Populate event with a fake title based on the current state.
         // TODO: When can workspace be null?
         text.add(mWorkspace == null
-                ? getString(R.string.all_apps_home_button_label)
+                ? getString(R.string.home_screen)
                 : mStateManager.getState().getDescription(this));
         return result;
     }
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 15e0daa..3be9ac7 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -3263,7 +3263,7 @@
         }
         if (nScreens == 0) {
             // When the workspace is not loaded, we do not know how many screen will be bound.
-            return getContext().getString(R.string.all_apps_home_button_label);
+            return getContext().getString(R.string.home_screen);
         }
         return getContext().getString(R.string.workspace_scroll_format, page + 1, nScreens);
     }
diff --git a/src/com/android/launcher3/allapps/AllAppsInsetTransitionController.java b/src/com/android/launcher3/allapps/AllAppsInsetTransitionController.java
index 5af9113..d7af5f1 100644
--- a/src/com/android/launcher3/allapps/AllAppsInsetTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsInsetTransitionController.java
@@ -58,6 +58,17 @@
         mApps = appsView;
     }
 
+    public void hide() {
+        if (!BuildCompat.isAtLeastR()) return;
+
+        WindowInsets insets = mApps.getRootWindowInsets();
+        if (insets == null) return;
+
+        if (insets.isVisible(WindowInsets.Type.ime())) {
+            mApps.getWindowInsetsController().hide(WindowInsets.Type.ime());
+        }
+    }
+
     /**
      * Initializes member variables and requests for the {@link WindowInsetsAnimationController}
      * object.
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 5b00631..0268b96 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -19,7 +19,6 @@
 import static com.android.launcher3.LauncherState.ALL_APPS_HEADER_EXTRA;
 import static com.android.launcher3.LauncherState.APPS_VIEW_ITEM_MASK;
 import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.LauncherState.VERTICAL_SWIPE_INDICATOR;
 import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
 import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
 import static com.android.launcher3.anim.Interpolators.INSTANT;
@@ -37,6 +36,7 @@
 import android.util.FloatProperty;
 import android.view.View;
 import android.view.animation.Interpolator;
+import android.widget.EditText;
 
 import androidx.core.os.BuildCompat;
 
@@ -222,9 +222,6 @@
 
         mAppsView.getSearchUiManager().setContentVisibility(visibleElements, setter, allAppsFade);
 
-        setter.setInt(mScrimView, ScrimView.DRAG_HANDLE_ALPHA,
-                (visibleElements & VERTICAL_SWIPE_INDICATOR) != 0 ? 255 : 0, allAppsFade);
-
         // Set visibility of the container at the very beginning or end of the transition.
         setter.setViewAlpha(mAppsView, hasAnyVisibleItem ? 1 : 0,
                 hasAnyVisibleItem ? INSTANT : FINAL_FRAME);
@@ -267,8 +264,16 @@
         }
         if (FeatureFlags.ENABLE_DEVICE_SEARCH.get() && BuildCompat.isAtLeastR()) {
             mInsetController.onAnimationEnd(mProgress);
+            if (Float.compare(mProgress, 0f) == 0) {
+                EditText editText = mAppsView.getSearchUiManager().getEditText();
+                if (editText != null) {
+                    editText.requestFocus();
+                }
+            }
             if (Float.compare(mProgress, 1f) == 0) {
-                mAppsView.getSearchUiManager().setTextSearchEnabled(true).requestFocus();
+                // Called when home gesture closes all apps container.
+                // TODO: should make the controller hide synchronously
+                mInsetController.hide();
             }
         }
     }
diff --git a/src/com/android/launcher3/allapps/SearchUiManager.java b/src/com/android/launcher3/allapps/SearchUiManager.java
index 7d5363f..aa056a0 100644
--- a/src/com/android/launcher3/allapps/SearchUiManager.java
+++ b/src/com/android/launcher3/allapps/SearchUiManager.java
@@ -66,12 +66,8 @@
     }
 
     /**
-     * Called to control how the search UI result should be handled.
-     *
-     * @param isEnabled when {@code true}, the search is all handled inside AOSP
-     *                  and is not overlayable.
-     * @return the searchbox edit text object
+     * @return the edit text object
      */
     @Nullable
-    EditText setTextSearchEnabled(boolean isEnabled);
+    EditText getEditText();
 }
diff --git a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
index df6a89b..16a1efd 100644
--- a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
+++ b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
@@ -217,7 +217,7 @@
     }
 
     @Override
-    public EditText setTextSearchEnabled(boolean isEnabled) {
+    public EditText getEditText() {
         return this;
     }
 }
diff --git a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
index 1d32d1d..30c3417 100644
--- a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
+++ b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
@@ -70,7 +70,8 @@
         final Bundle parcel = new Bundle();
         parcel.putInt(TestProtocol.STATE_FIELD, stateOrdinal);
 
-        sendEventToTest(accessibilityManager, TestProtocol.SWITCHED_TO_STATE_MESSAGE, parcel);
+        sendEventToTest(
+                accessibilityManager, context, TestProtocol.SWITCHED_TO_STATE_MESSAGE, parcel);
         Log.d(TestProtocol.PERMANENT_DIAG_TAG, "sendStateEventToTest: " + stateOrdinal);
     }
 
@@ -78,22 +79,24 @@
         final AccessibilityManager accessibilityManager = getAccessibilityManagerForTest(context);
         if (accessibilityManager == null) return;
 
-        sendEventToTest(accessibilityManager, TestProtocol.SCROLL_FINISHED_MESSAGE, null);
+        sendEventToTest(accessibilityManager, context, TestProtocol.SCROLL_FINISHED_MESSAGE, null);
     }
 
     public static void sendPauseDetectedEventToTest(Context context) {
         final AccessibilityManager accessibilityManager = getAccessibilityManagerForTest(context);
         if (accessibilityManager == null) return;
 
-        sendEventToTest(accessibilityManager, TestProtocol.PAUSE_DETECTED_MESSAGE, null);
+        sendEventToTest(accessibilityManager, context, TestProtocol.PAUSE_DETECTED_MESSAGE, null);
     }
 
     private static void sendEventToTest(
-            AccessibilityManager accessibilityManager, String eventTag, Bundle data) {
+            AccessibilityManager accessibilityManager,
+            Context context, String eventTag, Bundle data) {
         final AccessibilityEvent e = AccessibilityEvent.obtain(
                 AccessibilityEvent.TYPE_ANNOUNCEMENT);
         e.setClassName(eventTag);
         e.setParcelableData(data);
+        e.setPackageName(context.getApplicationContext().getPackageName());
         accessibilityManager.sendAccessibilityEvent(e);
     }
 
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index acf4482..de72534 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -30,11 +30,16 @@
 import com.android.launcher3.userevent.LauncherLogProto;
 import com.android.launcher3.util.ResourceBasedOverride;
 
+import java.util.List;
+
 /**
  * Handles the user event logging in R+.
+ *
+ * <pre>
  * All of the event ids are defined here.
  * Most of the methods are dummy methods for Launcher3
  * Actual call happens only for Launcher variant that implements QuickStep.
+ * </pre>
  */
 public class StatsLogManager implements ResourceBasedOverride {
 
@@ -46,8 +51,8 @@
     public static final int LAUNCHER_STATE_UNCHANGED = 5;
 
     /**
-     * Returns proper launcher state enum for {@link StatsLogManager}
-     * (to be removed during UserEventDispatcher cleanup)
+     * Returns proper launcher state enum for {@link StatsLogManager}(to be removed during
+     * UserEventDispatcher cleanup)
      */
     public static int containerTypeToAtomState(int containerType) {
         switch (containerType) {
@@ -64,9 +69,8 @@
     }
 
     /**
-     * Returns event enum based on the two {@link ContainerType} transition information when
-     * swipe gesture happens.
-     * (to be removed during UserEventDispatcher cleanup)
+     * Returns event enum based on the two {@link ContainerType} transition information when swipe
+     * gesture happens(to be removed during UserEventDispatcher cleanup).
      */
     public static EventEnum getLauncherAtomEvent(int startContainerType,
             int targetContainerType, EventEnum fallbackEvent) {
@@ -270,7 +274,46 @@
         LAUNCHER_SELECT_MODE_CLOSE(583),
 
         @UiEvent(doc = "User tapped on the highlight items in select mode")
-        LAUNCHER_SELECT_MODE_ITEM(584);
+        LAUNCHER_SELECT_MODE_ITEM(584),
+
+        @UiEvent(doc = "Notification dot on app icon enabled.")
+        LAUNCHER_NOTIFICATION_DOT_ENABLED(611),
+
+        @UiEvent(doc = "Notification dot on app icon disabled.")
+        LAUNCHER_NOTIFICATION_DOT_DISABLED(612),
+
+        @UiEvent(doc = "For new apps, add app icons to home screen enabled.")
+        LAUNCHER_ADD_NEW_APPS_TO_HOME_SCREEN_ENABLED(613),
+
+        @UiEvent(doc = "For new apps, add app icons to home screen disabled.")
+        LAUNCHER_ADD_NEW_APPS_TO_HOME_SCREEN_DISABLED(614),
+
+        @UiEvent(doc = "Home screen rotation is enabled when phone is rotated.")
+        LAUNCHER_HOME_SCREEN_ROTATION_ENABLED(615),
+
+        @UiEvent(doc = "Home screen rotation is disabled when phone is rotated.")
+        LAUNCHER_HOME_SCREEN_ROTATION_DISABLED(616),
+
+        @UiEvent(doc = "Suggestions in all apps list enabled.")
+        LAUNCHER_ALL_APPS_SUGGESTIONS_ENABLED(619),
+
+        @UiEvent(doc = "Suggestions in all apps list disabled.")
+        LAUNCHER_ALL_APPS_SUGGESTIONS_DISABLED(620),
+
+        @UiEvent(doc = "Suggestions on home screen is enabled.")
+        LAUNCHER_HOME_SCREEN_SUGGESTIONS_ENABLED(621),
+
+        @UiEvent(doc = "Suggestions on home screen is disabled.")
+        LAUNCHER_HOME_SCREEN_SUGGESTIONS_DISABLED(622),
+
+        @UiEvent(doc = "System navigation is 3 button mode.")
+        LAUNCHER_NAVIGATION_MODE_3_BUTTON(623),
+
+        @UiEvent(doc = "System navigation mode is 2 button mode.")
+        LAUNCHER_NAVIGATION_MODE_2_BUTTON(624),
+
+        @UiEvent(doc = "System navigation mode is 0 button mode/gesture navigation mode .")
+        LAUNCHER_NAVIGATION_MODE_GESTURE_BUTTON(625);
 
         // ADD MORE
 
@@ -401,8 +444,8 @@
     }
 
     /**
-     * Logs snapshot, or impression of the current workspace.
+     * Logs impression of the current workspace with additional launcher events.
      */
-    public void logSnapshot() {
+    public void logSnapshot(List<EventEnum> additionalEvents) {
     }
 }
diff --git a/src/com/android/launcher3/pm/UserCache.java b/src/com/android/launcher3/pm/UserCache.java
index 5aab41a..2d7d6b0 100644
--- a/src/com/android/launcher3/pm/UserCache.java
+++ b/src/com/android/launcher3/pm/UserCache.java
@@ -21,8 +21,10 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.ArrayMap;
+import android.util.Log;
 import android.util.LongSparseArray;
 
+import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.util.SafeCloseable;
 import com.android.launcher3.util.SimpleBroadcastReceiver;
@@ -102,6 +104,9 @@
                 mUsers = null;
                 mUserToSerialMap = null;
             }
+            if (TestProtocol.sDebugTracing) {
+                Log.d(TestProtocol.WORK_PROFILE_REMOVED, "Work profile removed", new Exception());
+            }
         }
     }
 
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
index c996748..31c3014 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
@@ -20,6 +20,7 @@
 import android.app.ActivityOptions;
 import android.content.Intent;
 import android.os.Bundle;
+import android.view.Display;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.ViewAnimationUtils;
@@ -168,7 +169,9 @@
 
     @Override
     public ActivityOptions getActivityLaunchOptions(View v) {
-        return null;
+        final Display display = getWindow().getDecorView().getDisplay();
+        return display != null ? ActivityOptions.makeBasic().setLaunchDisplayId(
+                       display.getDisplayId()) : null;
     }
 
     @Override
diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java
index 8165627..9816f13 100644
--- a/src/com/android/launcher3/testing/TestProtocol.java
+++ b/src/com/android/launcher3/testing/TestProtocol.java
@@ -107,4 +107,5 @@
     public static final String PAUSE_NOT_DETECTED = "b/139891609";
     public static final String OVERIEW_NOT_ALLAPPS = "b/156095088";
     public static final String NO_SWIPE_TO_HOME = "b/158017601";
+    public static final String WORK_PROFILE_REMOVED = "b/159671700";
 }
diff --git a/src/com/android/launcher3/util/OnboardingPrefs.java b/src/com/android/launcher3/util/OnboardingPrefs.java
index 26313e5..6e5e7d9 100644
--- a/src/com/android/launcher3/util/OnboardingPrefs.java
+++ b/src/com/android/launcher3/util/OnboardingPrefs.java
@@ -36,7 +36,6 @@
     public static final String SHELF_BOUNCE_SEEN = "launcher.shelf_bounce_seen";
     public static final String HOME_BOUNCE_COUNT = "launcher.home_bounce_count";
     public static final String SHELF_BOUNCE_COUNT = "launcher.shelf_bounce_count";
-    public static final String ALL_APPS_COUNT = "launcher.all_apps_count";
     public static final String HOTSEAT_DISCOVERY_TIP_COUNT = "launcher.hotseat_discovery_tip_count";
     public static final String HOTSEAT_LONGPRESS_TIP_SEEN = "launcher.hotseat_longpress_tip_seen";
 
@@ -56,7 +55,6 @@
     @StringDef(value = {
             HOME_BOUNCE_COUNT,
             SHELF_BOUNCE_COUNT,
-            ALL_APPS_COUNT,
             HOTSEAT_DISCOVERY_TIP_COUNT
     })
     @Retention(RetentionPolicy.SOURCE)
@@ -67,7 +65,6 @@
         Map<String, Integer> maxCounts = new ArrayMap<>(4);
         maxCounts.put(HOME_BOUNCE_COUNT, 3);
         maxCounts.put(SHELF_BOUNCE_COUNT, 3);
-        maxCounts.put(ALL_APPS_COUNT, 5);
         maxCounts.put(HOTSEAT_DISCOVERY_TIP_COUNT, 5);
         MAX_COUNTS = Collections.unmodifiableMap(maxCounts);
     }
diff --git a/src/com/android/launcher3/views/AccessibilityActionsView.java b/src/com/android/launcher3/views/AccessibilityActionsView.java
new file mode 100644
index 0000000..0eacaa3
--- /dev/null
+++ b/src/com/android/launcher3/views/AccessibilityActionsView.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.views;
+
+import static com.android.launcher3.LauncherState.ALL_APPS;
+import static com.android.launcher3.LauncherState.NORMAL;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.R;
+import com.android.launcher3.statemanager.StateManager.StateListener;
+import com.android.launcher3.views.OptionsPopupView.OptionItem;
+
+/**
+ * Placeholder view to expose additional Launcher actions via accessibility actions
+ */
+public class AccessibilityActionsView extends View implements StateListener<LauncherState> {
+
+    public AccessibilityActionsView(Context context) {
+        this(context, null);
+    }
+
+    public AccessibilityActionsView(Context context, @Nullable AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public AccessibilityActionsView(Context context, @Nullable AttributeSet attrs,
+            int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        Launcher.getLauncher(context).getStateManager().addStateListener(this);
+        setWillNotDraw(true);
+    }
+
+    @Override
+    public void onStateTransitionComplete(LauncherState finalState) {
+        setImportantForAccessibility(finalState == NORMAL
+                ? IMPORTANT_FOR_ACCESSIBILITY_YES : IMPORTANT_FOR_ACCESSIBILITY_NO);
+    }
+
+    @Override
+    public AccessibilityNodeInfo createAccessibilityNodeInfo() {
+        AccessibilityNodeInfo info = super.createAccessibilityNodeInfo();
+        Launcher l = Launcher.getLauncher(getContext());
+        info.addAction(new AccessibilityAction(
+                R.string.all_apps_button_label, l.getText(R.string.all_apps_button_label)));
+        for (OptionItem item : OptionsPopupView.getOptions(l)) {
+            info.addAction(new AccessibilityAction(item.labelRes, l.getText(item.labelRes)));
+        }
+        return info;
+    }
+
+    @Override
+    public boolean performAccessibilityAction(int action, Bundle arguments) {
+        if (super.performAccessibilityAction(action, arguments)) {
+            return true;
+        }
+        Launcher l = Launcher.getLauncher(getContext());
+        if (action == R.string.all_apps_button_label) {
+            l.getStateManager().goToState(ALL_APPS);
+            return true;
+        }
+        for (OptionItem item : OptionsPopupView.getOptions(l)) {
+            if (item.labelRes == action) {
+                if (item.eventId.getId() > 0) {
+                    l.getStatsLogManager().logger().log(item.eventId);
+                }
+                if (item.clickListener.onLongClick(this)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+}
diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java
index 049a1ac..e95dc5b 100644
--- a/src/com/android/launcher3/views/OptionsPopupView.java
+++ b/src/com/android/launcher3/views/OptionsPopupView.java
@@ -85,10 +85,10 @@
         if (item == null) {
             return false;
         }
-        if (item.mEventId.getId() > 0) {
-            mLauncher.getStatsLogManager().logger().log(item.mEventId);
+        if (item.eventId.getId() > 0) {
+            mLauncher.getStatsLogManager().logger().log(item.eventId);
         }
-        if (item.mClickListener.onLongClick(view)) {
+        if (item.clickListener.onLongClick(view)) {
             close(true);
             return true;
         }
@@ -130,8 +130,8 @@
         for (OptionItem item : items) {
             DeepShortcutView view =
                     (DeepShortcutView) popup.inflateAndAdd(R.layout.system_shortcut, popup);
-            view.getIconView().setBackgroundResource(item.mIconRes);
-            view.getBubbleText().setText(item.mLabelRes);
+            view.getIconView().setBackgroundResource(item.iconRes);
+            view.getBubbleText().setText(item.labelRes);
             view.setDividerVisibility(View.INVISIBLE);
             view.setOnClickListener(popup);
             view.setOnLongClickListener(popup);
@@ -152,7 +152,13 @@
             y = launcher.getDragLayer().getHeight() / 2;
         }
         RectF target = new RectF(x - halfSize, y - halfSize, x + halfSize, y + halfSize);
+        show(launcher, target, getOptions(launcher));
+    }
 
+    /**
+     * Returns the list of supported actions
+     */
+    public static ArrayList<OptionItem> getOptions(Launcher launcher) {
         ArrayList<OptionItem> options = new ArrayList<>();
         int resString = Utilities.existsStyleWallpapers(launcher) ?
                 R.string.styles_wallpaper_button_text : R.string.wallpaper_button_text;
@@ -170,10 +176,10 @@
                 LAUNCHER_SETTINGS_BUTTON_TAP_OR_LONGPRESS,
                 OptionsPopupView::startSettings));
 
-        show(launcher, target, options);
+        return options;
     }
 
-    public static boolean onWidgetsClicked(View view) {
+    private static boolean onWidgetsClicked(View view) {
         return openWidgets(Launcher.getLauncher(view.getContext())) != null;
     }
 
@@ -188,7 +194,7 @@
         }
     }
 
-    public static boolean startSettings(View view) {
+    private static boolean startSettings(View view) {
         TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "start: startSettings");
         Launcher launcher = Launcher.getLauncher(view.getContext());
         launcher.startActivity(new Intent(Intent.ACTION_APPLICATION_PREFERENCES)
@@ -201,7 +207,7 @@
      * Event handler for the wallpaper picker button that appears after a long press
      * on the home screen.
      */
-    public static boolean startWallpaperPicker(View v) {
+    private static boolean startWallpaperPicker(View v) {
         Launcher launcher = Launcher.getLauncher(v.getContext());
         if (!Utilities.isWallpaperAllowed(launcher)) {
             Toast.makeText(launcher, R.string.msg_disabled_by_admin, Toast.LENGTH_SHORT).show();
@@ -233,17 +239,17 @@
 
     public static class OptionItem {
 
-        private final int mLabelRes;
-        private final int mIconRes;
-        private final EventEnum mEventId;
-        private final OnLongClickListener mClickListener;
+        public final int labelRes;
+        public final int iconRes;
+        public final EventEnum eventId;
+        public final OnLongClickListener clickListener;
 
         public OptionItem(int labelRes, int iconRes, EventEnum eventId,
                 OnLongClickListener clickListener) {
-            mLabelRes = labelRes;
-            mIconRes = iconRes;
-            mEventId = eventId;
-            mClickListener = clickListener;
+            this.labelRes = labelRes;
+            this.iconRes = iconRes;
+            this.eventId = eventId;
+            this.clickListener = clickListener;
         }
     }
 }
diff --git a/src/com/android/launcher3/views/ScrimView.java b/src/com/android/launcher3/views/ScrimView.java
index 22faf97..7f0765b 100644
--- a/src/com/android/launcher3/views/ScrimView.java
+++ b/src/com/android/launcher3/views/ScrimView.java
@@ -15,119 +15,37 @@
  */
 package com.android.launcher3.views;
 
-import static android.content.Context.ACCESSIBILITY_SERVICE;
-import static android.view.MotionEvent.ACTION_DOWN;
-
 import static androidx.core.graphics.ColorUtils.compositeColors;
 
-import static com.android.launcher3.LauncherState.ALL_APPS;
-import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
-import static com.android.launcher3.anim.Interpolators.DEACCEL;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.anim.Interpolators.clampToProgress;
 import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
 import static com.android.launcher3.util.SystemUiController.UI_STATE_SCRIM_VIEW;
 
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.Keyframe;
-import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
-import android.animation.RectEvaluator;
 import android.content.Context;
-import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.graphics.Color;
-import android.graphics.Point;
 import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
 import android.util.AttributeSet;
-import android.util.IntProperty;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
 import android.view.View;
-import android.view.accessibility.AccessibilityManager;
-import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
 
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 import androidx.core.graphics.ColorUtils;
-import androidx.core.view.ViewCompat;
-import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
-import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat;
-import androidx.customview.widget.ExploreByTouchHelper;
 
-import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Insettable;
 import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
 import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.statemanager.StateManager;
-import com.android.launcher3.statemanager.StateManager.StateListener;
 import com.android.launcher3.uioverrides.WallpaperColorInfo;
 import com.android.launcher3.uioverrides.WallpaperColorInfo.OnChangeListener;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
-import com.android.launcher3.util.MultiValueAlpha;
-import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
 import com.android.launcher3.util.Themes;
-import com.android.launcher3.widget.WidgetsFullSheet;
-
-import java.util.List;
 
 /**
  * Simple scrim which draws a flat color
  */
-public class ScrimView<T extends Launcher> extends View implements Insettable, OnChangeListener,
-        AccessibilityStateChangeListener {
-
-    public static final IntProperty<ScrimView> DRAG_HANDLE_ALPHA =
-            new IntProperty<ScrimView>("dragHandleAlpha") {
-
-                @Override
-                public Integer get(ScrimView scrimView) {
-                    return scrimView.mDragHandleAlpha;
-                }
-
-                @Override
-                public void setValue(ScrimView scrimView, int value) {
-                    scrimView.setDragHandleAlpha(value);
-                }
-            };
-    private static final int WALLPAPERS = R.string.wallpaper_button_text;
-    private static final int WIDGETS = R.string.widget_button_text;
-    private static final int SETTINGS = R.string.settings_button_text;
-    private static final int ALPHA_CHANNEL_COUNT = 1;
-
-    private static final long DRAG_HANDLE_BOUNCE_DURATION_MS = 300;
-    // How much to delay before repeating the bounce.
-    private static final long DRAG_HANDLE_BOUNCE_DELAY_MS = 200;
-    // Repeat this many times (i.e. total number of bounces is 1 + this).
-    private static final int DRAG_HANDLE_BOUNCE_REPEAT_COUNT = 2;
-
-    private final Rect mTempRect = new Rect();
-    private final int[] mTempPos = new int[2];
+public class ScrimView<T extends Launcher> extends View implements Insettable, OnChangeListener {
 
     protected final T mLauncher;
     private final WallpaperColorInfo mWallpaperColorInfo;
-    private final AccessibilityManager mAM;
     protected final int mEndScrim;
     protected final boolean mIsScrimDark;
 
-    private final StateListener<LauncherState> mAccessibilityLauncherStateListener =
-            new StateListener<LauncherState>() {
-        @Override
-        public void onStateTransitionComplete(LauncherState finalState) {
-            setImportantForAccessibility(finalState == ALL_APPS
-                    ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
-                    : IMPORTANT_FOR_ACCESSIBILITY_AUTO);
-        }
-    };
-
     protected float mMaxScrimAlpha;
 
     protected float mProgress = 1;
@@ -137,22 +55,6 @@
     protected int mEndFlatColor;
     protected int mEndFlatColorAlpha;
 
-    protected final Point mDragHandleSize;
-    private final int mDragHandleTouchSize;
-    private final int mDragHandlePaddingInVerticalBarLayout;
-    protected float mDragHandleOffset;
-    private final Rect mDragHandleBounds;
-    private final RectF mHitRect = new RectF();
-    private ObjectAnimator mDragHandleAnim;
-
-    private final MultiValueAlpha mMultiValueAlpha;
-
-    private final AccessibilityHelper mAccessibilityHelper;
-    @Nullable
-    protected Drawable mDragHandle;
-
-    private int mDragHandleAlpha = 255;
-
     public ScrimView(Context context, AttributeSet attrs) {
         super(context, attrs);
         mLauncher = Launcher.cast(Launcher.getLauncher(context));
@@ -161,59 +63,24 @@
         mIsScrimDark = ColorUtils.calculateLuminance(mEndScrim) < 0.5f;
 
         mMaxScrimAlpha = 0.7f;
-
-        Resources res = context.getResources();
-        mDragHandleSize = new Point(res.getDimensionPixelSize(R.dimen.vertical_drag_handle_width),
-                res.getDimensionPixelSize(R.dimen.vertical_drag_handle_height));
-        mDragHandleBounds = new Rect(0, 0, mDragHandleSize.x, mDragHandleSize.y);
-        mDragHandleTouchSize = res.getDimensionPixelSize(R.dimen.vertical_drag_handle_touch_size);
-        mDragHandlePaddingInVerticalBarLayout = context.getResources()
-                .getDimensionPixelSize(R.dimen.vertical_drag_handle_padding_in_vertical_bar_layout);
-
-        mAccessibilityHelper = createAccessibilityHelper();
-        ViewCompat.setAccessibilityDelegate(this, mAccessibilityHelper);
-
-        mAM = (AccessibilityManager) context.getSystemService(ACCESSIBILITY_SERVICE);
         setFocusable(false);
-        mMultiValueAlpha = new MultiValueAlpha(this, ALPHA_CHANNEL_COUNT);
-    }
-
-    public AlphaProperty getAlphaProperty(int index) {
-        return mMultiValueAlpha.getProperty(index);
-    }
-
-    @NonNull
-    protected AccessibilityHelper createAccessibilityHelper() {
-        return new AccessibilityHelper();
     }
 
     @Override
-    public void setInsets(Rect insets) {
-        updateDragHandleBounds();
-        updateDragHandleVisibility();
-    }
+    public void setInsets(Rect insets) { }
 
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-        updateDragHandleBounds();
-    }
 
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
         mWallpaperColorInfo.addOnChangeListener(this);
         onExtractedColorsChanged(mWallpaperColorInfo);
-
-        mAM.addAccessibilityStateChangeListener(this);
-        onAccessibilityStateChanged(mAM.isEnabled());
     }
 
     @Override
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
         mWallpaperColorInfo.removeOnChangeListener(this);
-        mAM.removeAccessibilityStateChangeListener(this);
     }
 
     @Override
@@ -234,10 +101,8 @@
     public void setProgress(float progress) {
         if (mProgress != progress) {
             mProgress = progress;
-            stopDragHandleEducationAnim();
             updateColors();
             updateSysUiColors();
-            updateDragHandleAlpha();
             invalidate();
         }
     }
@@ -260,286 +125,10 @@
         }
     }
 
-    protected void updateDragHandleAlpha() {
-        if (mDragHandle != null) {
-            mDragHandle.setAlpha(mDragHandleAlpha);
-        }
-    }
-
-    private void setDragHandleAlpha(int alpha) {
-        if (alpha != mDragHandleAlpha) {
-            mDragHandleAlpha = alpha;
-            if (mDragHandle != null) {
-                mDragHandle.setAlpha(mDragHandleAlpha);
-                invalidate();
-            }
-        }
-    }
-
     @Override
     protected void onDraw(Canvas canvas) {
         if (mCurrentFlatColor != 0) {
             canvas.drawColor(mCurrentFlatColor);
         }
-        drawDragHandle(canvas);
-    }
-
-    protected void drawDragHandle(Canvas canvas) {
-        if (mDragHandle != null) {
-            canvas.translate(0, -mDragHandleOffset);
-            mDragHandle.draw(canvas);
-            canvas.translate(0, mDragHandleOffset);
-        }
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent event) {
-        boolean superHandledTouch = super.onTouchEvent(event);
-        if (event.getAction() == ACTION_DOWN) {
-            if (!superHandledTouch && mHitRect.contains(event.getX(), event.getY())) {
-                if (startDragHandleEducationAnim()) {
-                    return true;
-                }
-            }
-            stopDragHandleEducationAnim();
-        }
-        return superHandledTouch;
-    }
-
-    /**
-     * Animates the drag handle to demonstrate how to get to all apps.
-     * @return Whether the animation was started (false if drag handle is invisible).
-     */
-    public boolean startDragHandleEducationAnim() {
-        stopDragHandleEducationAnim();
-
-        if (mDragHandle == null || mDragHandle.getAlpha() != 255) {
-            return false;
-        }
-
-        final Drawable drawable = mDragHandle;
-        mDragHandle = null;
-
-        Rect bounds = new Rect(mDragHandleBounds);
-        bounds.offset(0, -(int) mDragHandleOffset);
-        drawable.setBounds(bounds);
-
-        Rect topBounds = new Rect(bounds);
-        topBounds.offset(0, -bounds.height());
-
-        Rect invalidateRegion = new Rect(bounds);
-        invalidateRegion.top = topBounds.top;
-
-        final float progressToReachTop = 0.6f;
-        Keyframe frameTop = Keyframe.ofObject(progressToReachTop, topBounds);
-        frameTop.setInterpolator(DEACCEL);
-        Keyframe frameBot = Keyframe.ofObject(1, bounds);
-        frameBot.setInterpolator(ACCEL_DEACCEL);
-        PropertyValuesHolder holder = PropertyValuesHolder.ofKeyframe("bounds",
-                Keyframe.ofObject(0, bounds), frameTop, frameBot);
-        holder.setEvaluator(new RectEvaluator());
-
-        mDragHandleAnim = ObjectAnimator.ofPropertyValuesHolder(drawable, holder);
-        long totalBounceDuration = DRAG_HANDLE_BOUNCE_DURATION_MS + DRAG_HANDLE_BOUNCE_DELAY_MS;
-        // The bounce finishes by this progress, the rest of the duration just delays next bounce.
-        float delayStartProgress = 1f - (float) DRAG_HANDLE_BOUNCE_DELAY_MS / totalBounceDuration;
-        mDragHandleAnim.addUpdateListener((v) -> invalidate(invalidateRegion));
-        mDragHandleAnim.setDuration(totalBounceDuration);
-        mDragHandleAnim.setInterpolator(clampToProgress(LINEAR, 0, delayStartProgress));
-        mDragHandleAnim.setRepeatCount(DRAG_HANDLE_BOUNCE_REPEAT_COUNT);
-        getOverlay().add(drawable);
-
-        mDragHandleAnim.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                mDragHandleAnim = null;
-                getOverlay().remove(drawable);
-                updateDragHandleVisibility(drawable);
-            }
-        });
-        mDragHandleAnim.start();
-        return true;
-    }
-
-    private void stopDragHandleEducationAnim() {
-        if (mDragHandleAnim != null) {
-            mDragHandleAnim.end();
-        }
-    }
-
-    protected void updateDragHandleBounds() {
-        DeviceProfile grid = mLauncher.getDeviceProfile();
-        final int left;
-        final int width = getMeasuredWidth();
-        final int top = getMeasuredHeight() - mDragHandleSize.y - grid.getInsets().bottom;
-        final int topMargin;
-
-        if (grid.isVerticalBarLayout()) {
-            topMargin = grid.workspacePadding.bottom + mDragHandlePaddingInVerticalBarLayout;
-            if (grid.isSeascape()) {
-                left = width - grid.getInsets().right - mDragHandleSize.x
-                        - mDragHandlePaddingInVerticalBarLayout;
-            } else {
-                left = grid.getInsets().left + mDragHandlePaddingInVerticalBarLayout;
-            }
-        } else {
-            left = Math.round((width - mDragHandleSize.x) / 2f);
-            topMargin = grid.hotseatBarSizePx;
-        }
-        mDragHandleBounds.offsetTo(left, top - topMargin);
-        mHitRect.set(mDragHandleBounds);
-        // Inset outwards to increase touch size.
-        mHitRect.inset((mDragHandleSize.x - mDragHandleTouchSize) / 2f,
-                (mDragHandleSize.y - mDragHandleTouchSize) / 2f);
-
-        if (mDragHandle != null) {
-            mDragHandle.setBounds(mDragHandleBounds);
-        }
-    }
-
-    @Override
-    public void onAccessibilityStateChanged(boolean enabled) {
-        StateManager<LauncherState> stateManager = mLauncher.getStateManager();
-        stateManager.removeStateListener(mAccessibilityLauncherStateListener);
-
-        if (enabled) {
-            stateManager.addStateListener(mAccessibilityLauncherStateListener);
-            mAccessibilityLauncherStateListener.onStateTransitionComplete(stateManager.getState());
-        } else {
-            setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
-        }
-        updateDragHandleVisibility();
-    }
-
-    public void updateDragHandleVisibility() {
-        updateDragHandleVisibility(null);
-    }
-
-    private void updateDragHandleVisibility(@Nullable Drawable recycle) {
-        boolean visible = shouldDragHandleBeVisible();
-        boolean wasVisible = mDragHandle != null;
-        if (visible != wasVisible) {
-            if (visible) {
-                mDragHandle = recycle != null ? recycle :
-                        mLauncher.getDrawable(R.drawable.drag_handle_indicator_shadow);
-                mDragHandle.setBounds(mDragHandleBounds);
-
-                updateDragHandleAlpha();
-            } else {
-                mDragHandle = null;
-            }
-            invalidate();
-        }
-    }
-
-    protected boolean shouldDragHandleBeVisible() {
-        return mLauncher.getDeviceProfile().isVerticalBarLayout() || mAM.isEnabled();
-    }
-
-    @Override
-    public boolean dispatchHoverEvent(MotionEvent event) {
-        return mAccessibilityHelper.dispatchHoverEvent(event) || super.dispatchHoverEvent(event);
-    }
-
-    @Override
-    public boolean dispatchKeyEvent(KeyEvent event) {
-        return mAccessibilityHelper.dispatchKeyEvent(event) || super.dispatchKeyEvent(event);
-    }
-
-    @Override
-    public void onFocusChanged(boolean gainFocus, int direction,
-            Rect previouslyFocusedRect) {
-        super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
-        mAccessibilityHelper.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
-    }
-
-    protected class AccessibilityHelper extends ExploreByTouchHelper {
-
-        private static final int DRAG_HANDLE_ID = 1;
-
-        public AccessibilityHelper() {
-            super(ScrimView.this);
-        }
-
-        @Override
-        protected int getVirtualViewAt(float x, float y) {
-            return  mHitRect.contains((int) x, (int) y)
-                    ? DRAG_HANDLE_ID : INVALID_ID;
-        }
-
-        @Override
-        protected void getVisibleVirtualViews(List<Integer> virtualViewIds) {
-            virtualViewIds.add(DRAG_HANDLE_ID);
-        }
-
-        @Override
-        protected void onPopulateNodeForVirtualView(int virtualViewId,
-                AccessibilityNodeInfoCompat node) {
-            node.setContentDescription(getContext().getString(R.string.all_apps_button_label));
-            node.setBoundsInParent(mDragHandleBounds);
-
-            getLocationOnScreen(mTempPos);
-            mTempRect.set(mDragHandleBounds);
-            mTempRect.offset(mTempPos[0], mTempPos[1]);
-            node.setBoundsInScreen(mTempRect);
-
-            node.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK);
-            node.setClickable(true);
-            node.setFocusable(true);
-
-            if (mLauncher.isInState(NORMAL)) {
-                Context context = getContext();
-                if (Utilities.isWallpaperAllowed(context)) {
-                    node.addAction(
-                            new AccessibilityActionCompat(WALLPAPERS, context.getText(WALLPAPERS)));
-                }
-                node.addAction(new AccessibilityActionCompat(WIDGETS, context.getText(WIDGETS)));
-                node.addAction(new AccessibilityActionCompat(SETTINGS, context.getText(SETTINGS)));
-            }
-        }
-
-        @Override
-        protected boolean onPerformActionForVirtualView(
-                int virtualViewId, int action, Bundle arguments) {
-            if (action == AccessibilityNodeInfoCompat.ACTION_CLICK) {
-                mLauncher.getUserEventDispatcher().logActionOnControl(
-                        Action.Touch.TAP, ControlType.ALL_APPS_BUTTON,
-                        mLauncher.getStateManager().getState().containerType);
-                mLauncher.getStateManager().goToState(ALL_APPS);
-                return true;
-            } else if (action == WALLPAPERS) {
-                return OptionsPopupView.startWallpaperPicker(ScrimView.this);
-            } else if (action == WIDGETS) {
-                int originalImportanceForAccessibility = getImportantForAccessibility();
-                setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
-                WidgetsFullSheet widgetsFullSheet = OptionsPopupView.openWidgets(mLauncher);
-                if (widgetsFullSheet == null) {
-                    setImportantForAccessibility(originalImportanceForAccessibility);
-                    return false;
-                }
-                widgetsFullSheet.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
-                    @Override
-                    public void onViewAttachedToWindow(View view) {}
-
-                    @Override
-                    public void onViewDetachedFromWindow(View view) {
-                        setImportantForAccessibility(originalImportanceForAccessibility);
-                        widgetsFullSheet.removeOnAttachStateChangeListener(this);
-                    }
-                });
-                return true;
-            } else if (action == SETTINGS) {
-                return OptionsPopupView.startSettings(ScrimView.this);
-            }
-
-            return false;
-        }
-    }
-
-    /**
-     * @return The top of this scrim view, or {@link Float#MAX_VALUE} if there's no distinct top.
-     */
-    public float getVisualTop() {
-        return Float.MAX_VALUE;
     }
 }
diff --git a/tests/src/com/android/launcher3/ui/WorkTabTest.java b/tests/src/com/android/launcher3/ui/WorkTabTest.java
index 8d571ff..f5f93c4 100644
--- a/tests/src/com/android/launcher3/ui/WorkTabTest.java
+++ b/tests/src/com/android/launcher3/ui/WorkTabTest.java
@@ -24,6 +24,7 @@
 import android.os.Process;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.util.Log;
 import android.widget.TextView;
 
 import androidx.test.filters.LargeTest;
@@ -34,6 +35,7 @@
 import com.android.launcher3.allapps.AllAppsPagedView;
 import com.android.launcher3.allapps.WorkModeSwitch;
 import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.views.WorkEduView;
 
 import org.junit.After;
@@ -132,6 +134,10 @@
                     l.getResources().getString(R.string.work_profile_edu_personal_apps));
             workEduView.findViewById(R.id.proceed).callOnClick();
         });
+
+        executeOnLauncher(launcher -> Log.d(TestProtocol.WORK_PROFILE_REMOVED,
+                "Work profile status: " + launcher.getAppsView().isPersonalTabVisible()));
+
         // verify work edu is seen next
         waitForLauncherCondition("Launcher did not show the next edu screen", l ->
                 ((AllAppsPagedView) l.getAppsView().getContentView()).getCurrentPage() == WORK_PAGE
diff --git a/tests/tapl/com/android/launcher3/tapl/Background.java b/tests/tapl/com/android/launcher3/tapl/Background.java
index 0c4e5a9..e538f60 100644
--- a/tests/tapl/com/android/launcher3/tapl/Background.java
+++ b/tests/tapl/com/android/launcher3/tapl/Background.java
@@ -93,7 +93,7 @@
 
                 mLauncher.sendPointer(
                         downTime, downTime, MotionEvent.ACTION_DOWN, start, gestureScope);
-                mLauncher.executeAndWaitForEvent(
+                mLauncher.executeAndWaitForLauncherEvent(
                         () -> mLauncher.movePointer(
                                 downTime,
                                 downTime,
diff --git a/tests/tapl/com/android/launcher3/tapl/Launchable.java b/tests/tapl/com/android/launcher3/tapl/Launchable.java
index 13ecfb8..093c024 100644
--- a/tests/tapl/com/android/launcher3/tapl/Launchable.java
+++ b/tests/tapl/com/android/launcher3/tapl/Launchable.java
@@ -56,15 +56,18 @@
     private Background launch(BySelector selector) {
         LauncherInstrumentation.log("Launchable.launch before click " +
                 mObject.getVisibleCenter() + " in " + mLauncher.getVisibleBounds(mObject));
+        final String label = mObject.getText();
 
         mLauncher.executeAndWaitForEvent(
-                () -> mLauncher.clickLauncherObject(mObject),
+                () -> {
+                    mLauncher.clickLauncherObject(mObject);
+                    expectActivityStartEvents();
+                },
                 event -> event.getEventType() == TYPE_WINDOW_STATE_CHANGED,
-                () -> "Launching an app didn't open a new window: " + mObject.getText());
-        expectActivityStartEvents();
+                () -> "Launching an app didn't open a new window: " + label);
 
         mLauncher.assertTrue(
-                "App didn't start: " + selector,
+                "App didn't start: " + label,
                 mLauncher.getDevice().wait(Until.hasObject(selector),
                         LauncherInstrumentation.WAIT_TIME_MS));
         return new Background(mLauncher);
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index c74dea9..4e2ed11 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -103,6 +103,7 @@
 
     static final Pattern EVENT_TOUCH_DOWN_TIS = getTouchEventPatternTIS("ACTION_DOWN");
     static final Pattern EVENT_TOUCH_UP_TIS = getTouchEventPatternTIS("ACTION_UP");
+    private final String mLauncherPackage;
 
     // Types for launcher containers that the user is interacting with. "Background" is a
     // pseudo-container corresponding to inactive launcher covered by another app.
@@ -216,11 +217,11 @@
         // Launcher package. As during inproc tests the tested launcher may not be selected as the
         // current launcher, choosing target package for inproc. For out-of-proc, use the installed
         // launcher package.
-        final String authorityPackage = testPackage.equals(targetPackage) ?
-                getLauncherPackageName() :
-                targetPackage;
+        mLauncherPackage = testPackage.equals(targetPackage)
+                ? getLauncherPackageName()
+                : targetPackage;
 
-        String testProviderAuthority = authorityPackage + ".TestInfo";
+        String testProviderAuthority = mLauncherPackage + ".TestInfo";
         mTestProviderUri = new Uri.Builder()
                 .scheme(ContentResolver.SCHEME_CONTENT)
                 .authority(testProviderAuthority)
@@ -450,7 +451,7 @@
             }
         }
 
-        dumpDiagnostics();
+        dumpDiagnostics(message);
 
         log("Hierarchy dump for: " + message);
         dumpViewHierarchy();
@@ -458,10 +459,11 @@
         return message;
     }
 
-    private void dumpDiagnostics() {
-        Log.e("b/156287114", "Input:");
+    private void dumpDiagnostics(String message) {
+        log("Diagnostics for failure: " + message);
+        log("Input:");
         logShellCommand("dumpsys input");
-        Log.e("b/156287114", "TIS:");
+        log("TIS:");
         logShellCommand("dumpsys activity service TouchInteractionService");
     }
 
@@ -469,10 +471,10 @@
         try {
             for (String line : mDevice.executeShellCommand(command).split("\\n")) {
                 SystemClock.sleep(10);
-                Log.d("b/156287114", line);
+                log(line);
             }
         } catch (IOException e) {
-            Log.d("b/156287114", "Failed to execute " + command);
+            log("Failed to execute " + command);
         }
     }
 
@@ -628,6 +630,14 @@
         fail("Launcher didn't initialize");
     }
 
+    Parcelable executeAndWaitForLauncherEvent(Runnable command,
+            UiAutomation.AccessibilityEventFilter eventFilter, Supplier<String> message) {
+        return executeAndWaitForEvent(
+                command,
+                e -> mLauncherPackage.equals(e.getPackageName()) && eventFilter.accept(e),
+                message);
+    }
+
     Parcelable executeAndWaitForEvent(Runnable command,
             UiAutomation.AccessibilityEventFilter eventFilter, Supplier<String> message) {
         try {
@@ -972,7 +982,7 @@
 
     void runToState(Runnable command, int expectedState) {
         final List<Integer> actualEvents = new ArrayList<>();
-        executeAndWaitForEvent(
+        executeAndWaitForLauncherEvent(
                 command,
                 event -> isSwitchToStateEvent(event, expectedState, actualEvents),
                 () -> "Failed to receive an event for the state change: expected ["
@@ -1087,7 +1097,7 @@
                 return;
         }
 
-        executeAndWaitForEvent(
+        executeAndWaitForLauncherEvent(
                 () -> linearGesture(
                         startX, startY, endX, endY, steps, slowDown, GestureScope.INSIDE),
                 event -> TestProtocol.SCROLL_FINISHED_MESSAGE.equals(event.getClassName()),
@@ -1362,7 +1372,7 @@
                 if (mCheckEventsForSuccessfulGestures) {
                     final String message = eventChecker.verify(WAIT_TIME_MS, true);
                     if (message != null) {
-                        dumpDiagnostics();
+                        dumpDiagnostics(message);
                         checkForAnomaly();
                         Assert.fail(formatSystemHealthMessage(
                                 "http://go/tapl : successful gesture produced " + message));