Merge "Relying on the controller state instead of animator state for icon alignment" into tm-qpr-dev
diff --git a/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java b/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java
index a91ff44..bdac88a 100644
--- a/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java
+++ b/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java
@@ -17,6 +17,7 @@
 package com.android.launcher3.testing;
 
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
 
 import android.app.Activity;
 import android.app.Application;
@@ -248,6 +249,9 @@
                 return response;
             }
 
+            case TestProtocol.REQUEST_MODEL_QUEUE_CLEARED:
+                return getFromExecutorSync(MODEL_EXECUTOR, Bundle::new);
+
             default:
                 return super.call(method, arg, extras);
         }
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 3225078..6e3fd32 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -288,6 +288,8 @@
     <dimen name="transient_taskbar_margin">24dp</dimen>
     <dimen name="transient_taskbar_shadow_blur">40dp</dimen>
     <dimen name="transient_taskbar_key_shadow_distance">10dp</dimen>
+    <dimen name="transient_taskbar_stashed_size">32dp</dimen>
+    <dimen name="transient_taskbar_icon_spacing">10dp</dimen>
     <!-- Taskbar swipe up thresholds -->
     <dimen name="taskbar_app_window_threshold">150dp</dimen>
     <dimen name="taskbar_home_overview_threshold">225dp</dimen>
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index afd659f..72ae1d1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -190,8 +190,13 @@
         if (isPhoneMode()) {
             // DeviceProfile's taskbar vars aren't initialized w/ the flag off
             Resources resources = mActivity.getResources();
-            mUnstashedHeight = resources.getDimensionPixelSize(R.dimen.taskbar_size);
-            mStashedHeight = resources.getDimensionPixelOffset(R.dimen.taskbar_stashed_size);
+            boolean isTransientTaskbar = DisplayController.isTransientTaskbar(mActivity);
+            mUnstashedHeight = resources.getDimensionPixelSize(isTransientTaskbar
+                    ? R.dimen.transient_taskbar_size
+                    : R.dimen.taskbar_size);
+            mStashedHeight = resources.getDimensionPixelSize(isTransientTaskbar
+                    ? R.dimen.transient_taskbar_stashed_size
+                    : R.dimen.taskbar_stashed_size);
         } else {
             mUnstashedHeight = mActivity.getDeviceProfile().taskbarSize;
             mStashedHeight = mActivity.getDeviceProfile().stashedTaskbarSize;
@@ -344,7 +349,8 @@
      * @see WindowInsets.Type#systemBars()
      */
     public int getContentHeightToReportToApps() {
-        if (isPhoneMode() && !mActivity.isThreeButtonNav()) {
+        if ((isPhoneMode() && !mActivity.isThreeButtonNav())
+                || DisplayController.isTransientTaskbar(mActivity)) {
             return getStashedHeight();
         }
 
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index b228fdb..9813c8a 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -219,6 +219,8 @@
 
         mEnableWidgetDepth = ENABLE_WIDGET_PICKER_DEPTH.get()
                 && SystemProperties.getBoolean("ro.launcher.depth.widget", true);
+        getWorkspace().addOverlayCallback(progress ->
+                onTaskbarInAppDisplayProgressUpdate(progress, MINUS_ONE_PAGE_PROGRESS_INDEX));
     }
 
     @Override
@@ -594,17 +596,6 @@
         recentsView.finishRecentsAnimation(true /* toRecents */, null);
     }
 
-    /**
-     * {@code LauncherOverlayCallbacks} scroll amount.
-     * Indicates transition progress to -1 screen.
-     * @param progress From 0 to 1.
-     */
-    @Override
-    public void onScrollChanged(float progress) {
-        super.onScrollChanged(progress);
-        onTaskbarInAppDisplayProgressUpdate(progress, MINUS_ONE_PAGE_PROGRESS_INDEX);
-    }
-
     @Override
     public void onAllAppsTransition(float progress) {
         super.onAllAppsTransition(progress);
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 5cc5f10..7e4c2f6 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -48,7 +48,7 @@
 import static com.android.quickstep.GestureState.STATE_RECENTS_SCROLLING_FINISHED;
 import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
 import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.CANCEL_RECENTS_ANIMATION;
-import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.FINISH_RECENTS_ANIMATION;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.EXPECTING_TASK_APPEARED;
 import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.ON_SETTLED_ON_END_TARGET;
 import static com.android.quickstep.util.VibratorWrapper.OVERVIEW_HAPTIC;
 import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
@@ -1704,10 +1704,6 @@
     private void resumeLastTask() {
         if (mRecentsAnimationController != null) {
             mRecentsAnimationController.finish(false /* toRecents */, null);
-            ActiveGestureLog.INSTANCE.addLog(
-                    /* event= */ "finishRecentsAnimation",
-                    /* extras= */ false,
-                    /* gestureEvent= */ FINISH_RECENTS_ANIMATION);
         }
         doLogGesture(LAST_TASK, null);
         reset();
@@ -1916,10 +1912,6 @@
                 mRecentsAnimationController.detachNavigationBarFromApp(true);
             }
         }
-        ActiveGestureLog.INSTANCE.addLog(
-                /* event= */ "finishRecentsAnimation",
-                /* extras= */ true,
-                /* gestureEvent= */ FINISH_RECENTS_ANIMATION);
     }
 
     private void finishCurrentTransitionToHome() {
@@ -1931,10 +1923,6 @@
             finishRecentsControllerToHome(
                     () -> mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED));
         }
-        ActiveGestureLog.INSTANCE.addLog(
-                /* event= */ "finishRecentsAnimation",
-                /* extras= */ true,
-                /* gestureEvent= */ FINISH_RECENTS_ANIMATION);
         doLogGesture(HOME, mRecentsView == null ? null : mRecentsView.getCurrentPageTaskView());
     }
 
@@ -2045,6 +2033,9 @@
                 mGestureState.updateLastStartedTaskId(taskId);
                 boolean hasTaskPreviouslyAppeared = mGestureState.getPreviouslyAppearedTaskIds()
                         .contains(taskId);
+                if (!hasTaskPreviouslyAppeared) {
+                    ActiveGestureLog.INSTANCE.trackEvent(EXPECTING_TASK_APPEARED);
+                }
                 nextTask.launchTask(success -> {
                     resultCallback.accept(success);
                     if (success) {
diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
index 3c4ee75..31b78b3 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -21,7 +21,6 @@
 import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
 import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.SET_END_TARGET;
 import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.SET_END_TARGET_HOME;
-import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.SET_END_TARGET_LAST_TASK;
 import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.SET_END_TARGET_NEW_TASK;
 
 import android.annotation.Nullable;
@@ -341,8 +340,6 @@
                 ActiveGestureLog.INSTANCE.trackEvent(SET_END_TARGET_NEW_TASK);
                 break;
             case LAST_TASK:
-                ActiveGestureLog.INSTANCE.trackEvent(SET_END_TARGET_LAST_TASK);
-                break;
             case RECENTS:
             default:
                 // No-Op
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
index 2451ad8..b82ff03 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
@@ -130,7 +130,7 @@
     public final void onAnimationCanceled(HashMap<Integer, ThumbnailData> thumbnailDatas) {
         Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> {
             ActiveGestureLog.INSTANCE.addLog(
-                    /* event= */ "onRecentsAnimationCancelled",
+                    /* event= */ "RecentsAnimationCallbacks.onAnimationCanceled",
                     /* gestureEvent= */ CANCEL_RECENTS_ANIMATION);
             for (RecentsAnimationListener listener : getListeners()) {
                 listener.onRecentsAnimationCanceled(thumbnailDatas);
@@ -142,7 +142,7 @@
     @Override
     public void onTasksAppeared(RemoteAnimationTarget[] apps) {
         Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> {
-            ActiveGestureLog.INSTANCE.addLog("onTasksAppeared",
+            ActiveGestureLog.INSTANCE.addLog("RecentsAnimationCallbacks.onTasksAppeared",
                     ActiveGestureErrorDetector.GestureEvent.TASK_APPEARED);
             for (RecentsAnimationListener listener : getListeners()) {
                 listener.onTasksAppeared(apps);
@@ -164,6 +164,8 @@
 
     private final void onAnimationFinished(RecentsAnimationController controller) {
         Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> {
+            ActiveGestureLog.INSTANCE.addLog(
+                    /* event= */ "RecentsAnimationCallbacks.onAnimationFinished");
             for (RecentsAnimationListener listener : getListeners()) {
                 listener.onRecentsAnimationFinished(controller);
             }
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
index 81e3782..cf1c137 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
@@ -18,6 +18,7 @@
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.FINISH_RECENTS_ANIMATION;
 
 import android.content.Context;
 import android.os.RemoteException;
@@ -155,6 +156,10 @@
             mPendingFinishCallbacks.add(callback);
             return;
         }
+        ActiveGestureLog.INSTANCE.addLog(
+                /* event= */ "finishRecentsAnimation",
+                /* extras= */ toRecents,
+                /* gestureEvent= */ FINISH_RECENTS_ANIMATION);
 
         // Finish not yet requested
         mFinishRequested = true;
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index 90e8091..c45b2f0 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -21,6 +21,7 @@
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_INITIALIZED;
 import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_STARTED;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.START_RECENTS_ANIMATION;
 import static com.android.systemui.shared.system.RemoteTransitionCompat.newRemoteTransition;
 
 import android.app.ActivityManager;
@@ -38,6 +39,7 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.quickstep.TopTaskTracker.CachedTaskInfo;
+import com.android.quickstep.util.ActiveGestureLog;
 import com.android.quickstep.views.RecentsView;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -101,6 +103,9 @@
     @UiThread
     public RecentsAnimationCallbacks startRecentsAnimation(GestureState gestureState,
             Intent intent, RecentsAnimationCallbacks.RecentsAnimationListener listener) {
+        ActiveGestureLog.INSTANCE.addLog(
+                /* event= */ "startRecentsAnimation",
+                /* gestureEvent= */ START_RECENTS_ANIMATION);
         // Notify if recents animation is still running
         if (mController != null) {
             String msg = "New recents animation started before old animation completed";
@@ -250,6 +255,7 @@
      * Continues the existing running recents animation for a new gesture.
      */
     public RecentsAnimationCallbacks continueRecentsAnimation(GestureState gestureState) {
+        ActiveGestureLog.INSTANCE.addLog(/* event= */ "continueRecentsAnimation");
         mCallbacks.removeListener(mLastGestureState);
         mLastGestureState = gestureState;
         mCallbacks.addListener(gestureState);
@@ -288,6 +294,8 @@
      */
     public void finishRunningRecentsAnimation(boolean toHome) {
         if (mController != null) {
+            ActiveGestureLog.INSTANCE.addLog(
+                    /* event= */ "finishRunningRecentsAnimation", toHome);
             mCallbacks.notifyAnimationCanceled();
             Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), toHome
                     ? mController::finishAnimationToHome
@@ -320,6 +328,7 @@
      * Cleans up the recents animation entirely.
      */
     private void cleanUpRecentsAnimation() {
+        ActiveGestureLog.INSTANCE.addLog(/* event= */ "cleanUpRecentsAnimation");
         if (mLiveTileCleanUpHandler != null) {
             mLiveTileCleanUpHandler.run();
             mLiveTileCleanUpHandler = null;
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 43aafbe..4cdb5f8 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -15,6 +15,7 @@
  */
 package com.android.quickstep;
 
+import static android.accessibilityservice.AccessibilityService.GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS;
 import static android.view.MotionEvent.ACTION_CANCEL;
 import static android.view.MotionEvent.ACTION_DOWN;
 import static android.view.MotionEvent.ACTION_UP;
@@ -22,6 +23,7 @@
 import static com.android.launcher3.config.FeatureFlags.ASSISTANT_GIVES_LAUNCHER_FOCUS;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.quickstep.GestureState.DEFAULT_STATE;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER;
 import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.MOTION_DOWN;
 import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.MOTION_UP;
 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
@@ -46,7 +48,6 @@
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.content.res.Configuration;
-import android.graphics.Rect;
 import android.graphics.Region;
 import android.graphics.drawable.Icon;
 import android.os.Build;
@@ -86,7 +87,6 @@
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.OnboardingPrefs;
 import com.android.launcher3.util.TraceHelper;
-import com.android.launcher3.util.WindowBounds;
 import com.android.quickstep.inputconsumers.AccessibilityInputConsumer;
 import com.android.quickstep.inputconsumers.AssistantInputConsumer;
 import com.android.quickstep.inputconsumers.DeviceLockedInputConsumer;
@@ -103,7 +103,6 @@
 import com.android.quickstep.util.ActiveGestureLog.CompoundString;
 import com.android.quickstep.util.ProtoTracer;
 import com.android.quickstep.util.ProxyScreenStatusProvider;
-import com.android.quickstep.util.SplitScreenBounds;
 import com.android.systemui.shared.recents.IOverviewProxy;
 import com.android.systemui.shared.recents.ISystemUiProxy;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -142,18 +141,7 @@
     private static final boolean BUBBLES_HOME_GESTURE_ENABLED =
             SystemProperties.getBoolean("persist.wm.debug.bubbles_home_gesture", true);
 
-    private static final String KEY_BACK_NOTIFICATION_COUNT = "backNotificationCount";
-    private static final String NOTIFY_ACTION_BACK = "com.android.quickstep.action.BACK_GESTURE";
     private static final String HAS_ENABLED_QUICKSTEP_ONCE = "launcher.has_enabled_quickstep_once";
-    private static final int MAX_BACK_NOTIFICATION_COUNT = 3;
-
-    /**
-     * System Action ID to show all apps.
-     * TODO: Use AccessibilityService's corresponding global action constant in S
-     */
-    private static final int SYSTEM_ACTION_ID_ALL_APPS = 14;
-
-    private int mBackGestureNotificationCounter = -1;
 
     private final TISBinder mTISBinder = new TISBinder();
 
@@ -264,12 +252,6 @@
             MAIN_EXECUTOR.execute(() -> mDeviceState.setDeferredGestureRegion(region));
         }
 
-        @Override
-        public void onSplitScreenSecondaryBoundsChanged(Rect bounds, Rect insets) {
-            WindowBounds wb = new WindowBounds(bounds, insets);
-            MAIN_EXECUTOR.execute(() -> SplitScreenBounds.INSTANCE.setSecondaryWindowBounds(wb));
-        }
-
         @BinderThread
         @Override
         public void onScreenTurnedOn() {
@@ -483,8 +465,6 @@
 
         // Temporarily disable model preload
         // new ModelPreload().start(this);
-        mBackGestureNotificationCounter = Math.max(0, Utilities.getDevicePrefs(this)
-                .getInt(KEY_BACK_NOTIFICATION_COUNT, MAX_BACK_NOTIFICATION_COUNT));
         resetHomeBounceSeenOnQuickstepEnabledFirstTime();
 
         mOverviewComponentObserver.setOverviewChangeListener(this::onOverviewTargetChange);
@@ -522,11 +502,11 @@
                     Icon.createWithResource(this, R.drawable.ic_apps),
                     getString(R.string.all_apps_label),
                     getString(R.string.all_apps_label),
-                    PendingIntent.getActivity(this, SYSTEM_ACTION_ID_ALL_APPS, intent,
+                    PendingIntent.getActivity(this, GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS, intent,
                             PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE));
-            am.registerSystemAction(allAppsAction, SYSTEM_ACTION_ID_ALL_APPS);
+            am.registerSystemAction(allAppsAction, GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS);
         } else {
-            am.unregisterSystemAction(SYSTEM_ACTION_ID_ALL_APPS);
+            am.unregisterSystemAction(GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS);
         }
 
         StatefulActivity newOverviewActivity = mOverviewComponentObserver.getActivityInterface()
@@ -604,7 +584,7 @@
         ProtoTracer.INSTANCE.get(this).remove(this);
 
         getSystemService(AccessibilityManager.class)
-                .unregisterSystemAction(SYSTEM_ACTION_ID_ALL_APPS);
+                .unregisterSystemAction(GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS);
 
         mTaskbarManager.destroy();
         sConnected = false;
@@ -927,6 +907,9 @@
                 .append(consumer.getName())
                 .append(". reason(s):")
                 .append(reasonString));
+        if ((consumer.getType() & InputConsumer.TYPE_OTHER_ACTIVITY) != 0) {
+            ActiveGestureLog.INSTANCE.trackEvent(FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER);
+        }
     }
 
     private void handleOrientationSetup(InputConsumer baseInputConsumer) {
@@ -1251,12 +1234,22 @@
     }
 
     private void onCommand(PrintWriter pw, LinkedList<String> args) {
-        switch (args.pollFirst()) {
+        String cmd = args.pollFirst();
+        if (cmd == null) {
+            pw.println("Command missing");
+            printAvailableCommands(pw);
+            return;
+        }
+        switch (cmd) {
             case "clear-touch-log":
                 ActiveGestureLog.INSTANCE.clear();
                 break;
             case "print-gesture-log":
                 ActiveGestureLog.INSTANCE.dump("", pw);
+                break;
+            default:
+                pw.println("Command does not exist: " + cmd);
+                printAvailableCommands(pw);
         }
     }
 
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index 9d269fb..b3d3c3d 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -30,7 +30,6 @@
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.TraceHelper.FLAG_CHECK_FOR_RACE_CONDITIONS;
 import static com.android.launcher3.util.VelocityUtils.PX_PER_MS;
-import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.START_RECENTS_ANIMATION;
 import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID;
 
 import android.annotation.TargetApi;
@@ -67,7 +66,6 @@
 import com.android.quickstep.RecentsAnimationTargets;
 import com.android.quickstep.RotationTouchHelper;
 import com.android.quickstep.TaskAnimationManager;
-import com.android.quickstep.util.ActiveGestureLog;
 import com.android.quickstep.util.CachedEventDispatcher;
 import com.android.quickstep.util.MotionPauseDetector;
 import com.android.quickstep.util.NavBarPosition;
@@ -387,10 +385,6 @@
     }
 
     private void startTouchTrackingForWindowAnimation(long touchTimeMs) {
-        ActiveGestureLog.INSTANCE.addLog(
-                /* event= */ "startRecentsAnimation",
-                /* gestureEvent= */ START_RECENTS_ANIMATION);
-
         mInteractionHandler = mHandlerFactory.newHandler(mGestureState, touchTimeMs);
         mInteractionHandler.setGestureEndCallback(this::onInteractionGestureFinished);
         mMotionPauseDetector.setOnMotionPauseListener(mInteractionHandler.getMotionPauseListener());
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java
index b9b5e7c..2462394 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java
@@ -28,8 +28,6 @@
 import android.graphics.Point;
 import android.view.MotionEvent;
 
-import androidx.annotation.Nullable;
-
 import com.android.launcher3.anim.AnimatorListeners;
 import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.testing.shared.TestProtocol;
@@ -43,7 +41,6 @@
 import com.android.quickstep.RecentsAnimationController;
 import com.android.quickstep.RecentsAnimationTargets;
 import com.android.quickstep.TaskAnimationManager;
-import com.android.quickstep.util.ActiveGestureErrorDetector;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.InputMonitorCompat;
 
@@ -102,8 +99,7 @@
         mDisplaySize = DisplayController.INSTANCE.get(context).getInfo().currentSize;
 
         // Init states
-        mStateCallback = new MultiStateCallback(
-                STATE_NAMES, ProgressDelegateInputConsumer::getTrackedEventForState);
+        mStateCallback = new MultiStateCallback(STATE_NAMES);
         mStateCallback.runOnceAtState(STATE_TARGET_RECEIVED | STATE_HANDLER_INVALIDATED,
                 this::endRemoteAnimation);
         mStateCallback.runOnceAtState(STATE_TARGET_RECEIVED | STATE_FLING_FINISHED,
@@ -113,14 +109,6 @@
         mSwipeDetector.setDetectableScrollConditions(DIRECTION_POSITIVE, false);
     }
 
-    @Nullable
-    private static ActiveGestureErrorDetector.GestureEvent getTrackedEventForState(int stateFlag) {
-        if (stateFlag == STATE_HANDLER_INVALIDATED) {
-            return ActiveGestureErrorDetector.GestureEvent.STATE_HANDLER_INVALIDATED;
-        }
-        return null;
-    }
-
     @Override
     public int getType() {
         return TYPE_PROGRESS_DELEGATE;
diff --git a/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java b/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java
index 53e0c2b..60065fb 100644
--- a/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java
+++ b/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java
@@ -33,10 +33,11 @@
      * Enums associated to gesture navigation events.
      */
     public enum GestureEvent {
-        MOTION_DOWN, MOTION_UP, SET_END_TARGET, SET_END_TARGET_HOME, SET_END_TARGET_LAST_TASK,
-        SET_END_TARGET_NEW_TASK, ON_SETTLED_ON_END_TARGET, START_RECENTS_ANIMATION,
-        FINISH_RECENTS_ANIMATION, CANCEL_RECENTS_ANIMATION, SET_ON_PAGE_TRANSITION_END_CALLBACK,
-        CANCEL_CURRENT_ANIMATION, CLEANUP_SCREENSHOT, SCROLLER_ANIMATION_ABORTED, TASK_APPEARED,
+        MOTION_DOWN, MOTION_UP, SET_END_TARGET, SET_END_TARGET_HOME, SET_END_TARGET_NEW_TASK,
+        ON_SETTLED_ON_END_TARGET, START_RECENTS_ANIMATION, FINISH_RECENTS_ANIMATION,
+        CANCEL_RECENTS_ANIMATION, SET_ON_PAGE_TRANSITION_END_CALLBACK, CANCEL_CURRENT_ANIMATION,
+        CLEANUP_SCREENSHOT, SCROLLER_ANIMATION_ABORTED, TASK_APPEARED, EXPECTING_TASK_APPEARED,
+        FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER,
 
         /**
          * These GestureEvents are specifically associated to state flags that get set in
@@ -91,35 +92,40 @@
                     case MOTION_UP:
                         errorDetected |= printErrorIfTrue(
                                 !encounteredEvents.contains(GestureEvent.MOTION_DOWN),
-                                /* errorMessage= */ prefix + "\t\tMotion up detected before/without"
+                                prefix,
+                                /* errorMessage= */ "Motion up detected before/without"
                                         + " motion down.",
                                 writer);
                         break;
                     case ON_SETTLED_ON_END_TARGET:
                         errorDetected |= printErrorIfTrue(
                                 !encounteredEvents.contains(GestureEvent.SET_END_TARGET),
-                                /* errorMessage= */ prefix + "\t\tonSettledOnEndTarget called "
+                                prefix,
+                                /* errorMessage= */ "onSettledOnEndTarget called "
                                         + "before/without setEndTarget.",
                                 writer);
                         break;
                     case FINISH_RECENTS_ANIMATION:
                         errorDetected |= printErrorIfTrue(
                                 !encounteredEvents.contains(GestureEvent.START_RECENTS_ANIMATION),
-                                /* errorMessage= */ prefix + "\t\tfinishRecentsAnimation called "
+                                prefix,
+                                /* errorMessage= */ "finishRecentsAnimation called "
                                         + "before/without startRecentsAnimation.",
                                 writer);
                         break;
                     case CANCEL_RECENTS_ANIMATION:
                         errorDetected |= printErrorIfTrue(
                                 !encounteredEvents.contains(GestureEvent.START_RECENTS_ANIMATION),
-                                /* errorMessage= */ prefix + "\t\tcancelRecentsAnimation called "
+                                prefix,
+                                /* errorMessage= */ "cancelRecentsAnimation called "
                                         + "before/without startRecentsAnimation.",
                                 writer);
                         break;
                     case CLEANUP_SCREENSHOT:
                         errorDetected |= printErrorIfTrue(
                                 !encounteredEvents.contains(GestureEvent.STATE_SCREENSHOT_CAPTURED),
-                                /* errorMessage= */ prefix + "\t\trecents activity screenshot was "
+                                prefix,
+                                /* errorMessage= */ "recents activity screenshot was "
                                         + "cleaned up before/without STATE_SCREENSHOT_CAPTURED "
                                         + "being set.",
                                 writer);
@@ -129,48 +135,66 @@
                                 encounteredEvents.contains(GestureEvent.SET_END_TARGET_HOME)
                                         && !encounteredEvents.contains(
                                                 GestureEvent.ON_SETTLED_ON_END_TARGET),
-                                /* errorMessage= */ prefix + "\t\trecents view scroller animation "
+                                prefix,
+                                /* errorMessage= */ "recents view scroller animation "
                                         + "aborted after setting end target HOME, but before"
                                         + " settling on end target.",
                                 writer);
                         break;
                     case TASK_APPEARED:
                         errorDetected |= printErrorIfTrue(
-                                !encounteredEvents.contains(GestureEvent.SET_END_TARGET_LAST_TASK)
-                                        && !encounteredEvents.contains(
-                                        GestureEvent.SET_END_TARGET_NEW_TASK),
-                                /* errorMessage= */ prefix + "\t\tonTasksAppeared called "
-                                        + "before/without setting end target to last or new task",
+                                !encounteredEvents.contains(GestureEvent.SET_END_TARGET_NEW_TASK),
+                                prefix,
+                                /* errorMessage= */ "onTasksAppeared called "
+                                        + "before/without setting end target to new task",
+                                writer);
+                        errorDetected |= printErrorIfTrue(
+                                !encounteredEvents.contains(GestureEvent.EXPECTING_TASK_APPEARED),
+                                prefix,
+                                /* errorMessage= */ "onTasksAppeared was not expected to be called",
+                                writer);
+                        break;
+                    case EXPECTING_TASK_APPEARED:
+                        errorDetected |= printErrorIfTrue(
+                                !encounteredEvents.contains(GestureEvent.SET_END_TARGET_NEW_TASK),
+                                prefix,
+                                /* errorMessage= */ "expecting onTasksAppeared to be called "
+                                        + "before/without setting end target to new task",
                                 writer);
                         break;
                     case STATE_GESTURE_COMPLETED:
                         errorDetected |= printErrorIfTrue(
                                 !encounteredEvents.contains(GestureEvent.MOTION_UP),
-                                /* errorMessage= */ prefix + "\t\tSTATE_GESTURE_COMPLETED set "
+                                prefix,
+                                /* errorMessage= */ "STATE_GESTURE_COMPLETED set "
                                         + "before/without motion up.",
                                 writer);
                         errorDetected |= printErrorIfTrue(
                                 !encounteredEvents.contains(GestureEvent.STATE_GESTURE_STARTED),
-                                /* errorMessage= */ prefix + "\t\tSTATE_GESTURE_COMPLETED set "
+                                prefix,
+                                /* errorMessage= */ "STATE_GESTURE_COMPLETED set "
                                         + "before/without STATE_GESTURE_STARTED.",
                                 writer);
                         break;
                     case STATE_GESTURE_CANCELLED:
                         errorDetected |= printErrorIfTrue(
                                 !encounteredEvents.contains(GestureEvent.MOTION_UP),
-                                /* errorMessage= */ prefix + "\t\tSTATE_GESTURE_CANCELLED set "
+                                prefix,
+                                /* errorMessage= */ "STATE_GESTURE_CANCELLED set "
                                         + "before/without motion up.",
                                 writer);
                         errorDetected |= printErrorIfTrue(
                                 !encounteredEvents.contains(GestureEvent.STATE_GESTURE_STARTED),
-                                /* errorMessage= */ prefix + "\t\tSTATE_GESTURE_CANCELLED set "
+                                prefix,
+                                /* errorMessage= */ "STATE_GESTURE_CANCELLED set "
                                         + "before/without STATE_GESTURE_STARTED.",
                                 writer);
                         break;
                     case STATE_SCREENSHOT_CAPTURED:
                         errorDetected |= printErrorIfTrue(
                                 !encounteredEvents.contains(GestureEvent.STATE_CAPTURE_SCREENSHOT),
-                                /* errorMessage= */ prefix + "\t\tSTATE_SCREENSHOT_CAPTURED set "
+                                prefix,
+                                /* errorMessage= */ "STATE_SCREENSHOT_CAPTURED set "
                                         + "before/without STATE_CAPTURE_SCREENSHOT.",
                                 writer);
                         break;
@@ -178,7 +202,8 @@
                         errorDetected |= printErrorIfTrue(
                                 !encounteredEvents.contains(
                                         GestureEvent.SET_ON_PAGE_TRANSITION_END_CALLBACK),
-                                /* errorMessage= */ prefix + "\t\tSTATE_RECENTS_SCROLLING_FINISHED "
+                                prefix,
+                                /* errorMessage= */ "STATE_RECENTS_SCROLLING_FINISHED "
                                         + "set before/without calling "
                                         + "setOnPageTransitionEndCallback.",
                                 writer);
@@ -187,16 +212,19 @@
                         errorDetected |= printErrorIfTrue(
                                 !encounteredEvents.contains(
                                         GestureEvent.START_RECENTS_ANIMATION),
-                                /* errorMessage= */ prefix + "\t\tSTATE_RECENTS_ANIMATION_CANCELED "
+                                prefix,
+                                /* errorMessage= */ "STATE_RECENTS_ANIMATION_CANCELED "
                                         + "set before/without startRecentsAnimation.",
                                 writer);
                         break;
                     case MOTION_DOWN:
                     case SET_END_TARGET:
                     case SET_END_TARGET_HOME:
+                    case SET_END_TARGET_NEW_TASK:
                     case START_RECENTS_ANIMATION:
                     case SET_ON_PAGE_TRANSITION_END_CALLBACK:
                     case CANCEL_CURRENT_ANIMATION:
+                    case FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER:
                     case STATE_GESTURE_STARTED:
                     case STATE_END_TARGET_ANIMATION_FINISHED:
                     case STATE_CAPTURE_SCREENSHOT:
@@ -210,31 +238,36 @@
             // Check that all required events were found.
             errorDetected |= printErrorIfTrue(
                     !encounteredEvents.contains(GestureEvent.MOTION_DOWN),
-                    /* errorMessage= */ prefix + "\t\tMotion down never detected.",
+                    prefix,
+                    /* errorMessage= */ "Motion down never detected.",
                     writer);
             errorDetected |= printErrorIfTrue(
                     !encounteredEvents.contains(GestureEvent.MOTION_UP),
-                    /* errorMessage= */ prefix + "\t\tMotion up never detected.",
+                    prefix,
+                    /* errorMessage= */ "Motion up never detected.",
                     writer);
 
             errorDetected |= printErrorIfTrue(
                     /* condition= */ encounteredEvents.contains(GestureEvent.SET_END_TARGET)
                             && !encounteredEvents.contains(GestureEvent.ON_SETTLED_ON_END_TARGET),
-                    /* errorMessage= */ prefix + "\t\tsetEndTarget was called, but "
+                    prefix,
+                    /* errorMessage= */ "setEndTarget was called, but "
                             + "onSettledOnEndTarget wasn't.",
                     writer);
             errorDetected |= printErrorIfTrue(
                     /* condition= */ encounteredEvents.contains(GestureEvent.SET_END_TARGET)
                             && !encounteredEvents.contains(
                                     GestureEvent.STATE_END_TARGET_ANIMATION_FINISHED),
-                    /* errorMessage= */ prefix + "\t\tsetEndTarget was called, but "
+                    prefix,
+                    /* errorMessage= */ "setEndTarget was called, but "
                             + "STATE_END_TARGET_ANIMATION_FINISHED was never set.",
                     writer);
             errorDetected |= printErrorIfTrue(
                     /* condition= */ encounteredEvents.contains(GestureEvent.SET_END_TARGET)
                             && !encounteredEvents.contains(
                                     GestureEvent.STATE_RECENTS_SCROLLING_FINISHED),
-                    /* errorMessage= */ prefix + "\t\tsetEndTarget was called, but "
+                    prefix,
+                    /* errorMessage= */ "setEndTarget was called, but "
                             + "STATE_RECENTS_SCROLLING_FINISHED was never set.",
                     writer);
             errorDetected |= printErrorIfTrue(
@@ -243,7 +276,8 @@
                             && encounteredEvents.contains(
                                     GestureEvent.STATE_RECENTS_SCROLLING_FINISHED)
                             && !encounteredEvents.contains(GestureEvent.ON_SETTLED_ON_END_TARGET),
-                    /* errorMessage= */ prefix + "\t\tSTATE_END_TARGET_ANIMATION_FINISHED and "
+                    prefix,
+                    /* errorMessage= */ "STATE_END_TARGET_ANIMATION_FINISHED and "
                             + "STATE_RECENTS_SCROLLING_FINISHED were set, but onSettledOnEndTarget "
                             + "wasn't called.",
                     writer);
@@ -253,7 +287,8 @@
                             GestureEvent.START_RECENTS_ANIMATION)
                             && !encounteredEvents.contains(GestureEvent.FINISH_RECENTS_ANIMATION)
                             && !encounteredEvents.contains(GestureEvent.CANCEL_RECENTS_ANIMATION),
-                    /* errorMessage= */ prefix + "\t\tstartRecentsAnimation was called, but "
+                    prefix,
+                    /* errorMessage= */ "startRecentsAnimation was called, but "
                             + "finishRecentsAnimation and cancelRecentsAnimation weren't.",
                     writer);
 
@@ -261,7 +296,8 @@
                     /* condition= */ encounteredEvents.contains(GestureEvent.STATE_GESTURE_STARTED)
                             && !encounteredEvents.contains(GestureEvent.STATE_GESTURE_COMPLETED)
                             && !encounteredEvents.contains(GestureEvent.STATE_GESTURE_CANCELLED),
-                    /* errorMessage= */ prefix + "\t\tSTATE_GESTURE_STARTED was set, but "
+                    prefix,
+                    /* errorMessage= */ "STATE_GESTURE_STARTED was set, but "
                             + "STATE_GESTURE_COMPLETED and STATE_GESTURE_CANCELLED weren't.",
                     writer);
 
@@ -269,7 +305,8 @@
                     /* condition= */ encounteredEvents.contains(
                             GestureEvent.STATE_CAPTURE_SCREENSHOT)
                             && !encounteredEvents.contains(GestureEvent.STATE_SCREENSHOT_CAPTURED),
-                    /* errorMessage= */ prefix + "\t\tSTATE_CAPTURE_SCREENSHOT was set, but "
+                    prefix,
+                    /* errorMessage= */ "STATE_CAPTURE_SCREENSHOT was set, but "
                             + "STATE_SCREENSHOT_CAPTURED wasn't.",
                     writer);
 
@@ -278,15 +315,18 @@
                             GestureEvent.SET_ON_PAGE_TRANSITION_END_CALLBACK)
                             && !encounteredEvents.contains(
                                     GestureEvent.STATE_RECENTS_SCROLLING_FINISHED),
-                    /* errorMessage= */ prefix + "\t\tsetOnPageTransitionEndCallback called, but "
+                    prefix,
+                    /* errorMessage= */ "setOnPageTransitionEndCallback called, but "
                             + "STATE_RECENTS_SCROLLING_FINISHED wasn't set.",
                     writer);
 
             errorDetected |= printErrorIfTrue(
-                    /* condition= */ !encounteredEvents.contains(
-                            GestureEvent.CANCEL_CURRENT_ANIMATION)
+                    /* condition= */ encounteredEvents.contains(
+                            GestureEvent.FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER)
+                            && !encounteredEvents.contains(GestureEvent.CANCEL_CURRENT_ANIMATION)
                             && !encounteredEvents.contains(GestureEvent.STATE_HANDLER_INVALIDATED),
-                    /* errorMessage= */ prefix + "\t\tAbsSwipeUpHandler.cancelCurrentAnimation "
+                    prefix,
+                    /* errorMessage= */ "AbsSwipeUpHandler.cancelCurrentAnimation "
                             + "wasn't called and STATE_HANDLER_INVALIDATED wasn't set.",
                     writer);
 
@@ -294,23 +334,17 @@
                     /* condition= */ encounteredEvents.contains(
                             GestureEvent.STATE_RECENTS_ANIMATION_CANCELED)
                             && !encounteredEvents.contains(GestureEvent.CLEANUP_SCREENSHOT),
-                    /* errorMessage= */ prefix + "\t\tSTATE_RECENTS_ANIMATION_CANCELED was set but "
+                    prefix,
+                    /* errorMessage= */ "STATE_RECENTS_ANIMATION_CANCELED was set but "
                             + "the task screenshot wasn't cleaned up.",
                     writer);
 
             errorDetected |= printErrorIfTrue(
                     /* condition= */ encounteredEvents.contains(
-                            GestureEvent.SET_END_TARGET_LAST_TASK)
+                            GestureEvent.EXPECTING_TASK_APPEARED)
                             && !encounteredEvents.contains(GestureEvent.TASK_APPEARED),
-                    /* errorMessage= */ prefix + "\t\tend target set to last task, but "
-                            + "onTaskAppeared wasn't called.",
-                    writer);
-            errorDetected |= printErrorIfTrue(
-                    /* condition= */ encounteredEvents.contains(
-                            GestureEvent.SET_END_TARGET_NEW_TASK)
-                            && !encounteredEvents.contains(GestureEvent.TASK_APPEARED),
-                    /* errorMessage= */ prefix + "\t\tend target set to new task, but "
-                            + "onTaskAppeared wasn't called.",
+                    prefix,
+                    /* errorMessage= */ "onTaskAppeared was expected to be called but wasn't.",
                     writer);
 
             if (!errorDetected) {
@@ -320,11 +354,11 @@
     }
 
     private static boolean printErrorIfTrue(
-            boolean condition, String errorMessage, PrintWriter writer) {
+            boolean condition, String prefix, String errorMessage, PrintWriter writer) {
         if (!condition) {
             return false;
         }
-        writer.println(errorMessage);
+        writer.println(prefix + "\t\t- " + errorMessage);
         return true;
     }
 }
diff --git a/quickstep/src/com/android/quickstep/util/SplitScreenBounds.java b/quickstep/src/com/android/quickstep/util/SplitScreenBounds.java
deleted file mode 100644
index 483a1c6..0000000
--- a/quickstep/src/com/android/quickstep/util/SplitScreenBounds.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * 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.Surface.ROTATION_0;
-import static android.view.Surface.ROTATION_180;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.os.Build;
-import android.view.WindowManager;
-import android.view.WindowMetrics;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.UiThread;
-
-import com.android.launcher3.R;
-import com.android.launcher3.util.DisplayController;
-import com.android.launcher3.util.WindowBounds;
-
-import java.util.ArrayList;
-
-/**
- * Utility class to hold the information abound a window bounds for split screen
- */
-@TargetApi(Build.VERSION_CODES.R)
-public class SplitScreenBounds {
-
-    public static final SplitScreenBounds INSTANCE = new SplitScreenBounds();
-    private final ArrayList<OnChangeListener> mListeners = new ArrayList<>();
-
-    @Nullable
-    private WindowBounds mBounds;
-
-    private SplitScreenBounds() { }
-
-    @UiThread
-    public void setSecondaryWindowBounds(@NonNull WindowBounds bounds) {
-        if (!bounds.equals(mBounds)) {
-            mBounds = bounds;
-            for (OnChangeListener listener : mListeners) {
-                listener.onSecondaryWindowBoundsChanged();
-            }
-        }
-    }
-
-    public @NonNull WindowBounds getSecondaryWindowBounds(Context context) {
-        if (mBounds == null) {
-            mBounds = createDefaultWindowBounds(context);
-        }
-        return mBounds;
-    }
-
-    /**
-     * Creates window bounds as 50% of device size
-     */
-    private static WindowBounds createDefaultWindowBounds(Context context) {
-        WindowMetrics wm = context.getSystemService(WindowManager.class).getMaximumWindowMetrics();
-        WindowBounds bounds = WindowBounds.fromWindowMetrics(wm);
-
-        int rotation = DisplayController.INSTANCE.get(context).getInfo().rotation;
-        int halfDividerSize = context.getResources()
-                .getDimensionPixelSize(R.dimen.multi_window_task_divider_size) / 2;
-
-        if (rotation == ROTATION_0 || rotation == ROTATION_180) {
-            bounds.bounds.top = bounds.insets.top + bounds.availableSize.y / 2 + halfDividerSize;
-            bounds.insets.top = 0;
-        } else {
-            bounds.bounds.left = bounds.insets.left + bounds.availableSize.x / 2 + halfDividerSize;
-            bounds.insets.left = 0;
-        }
-        return new WindowBounds(bounds.bounds, bounds.insets);
-    }
-
-    public void addOnChangeListener(OnChangeListener listener) {
-        mListeners.add(listener);
-    }
-
-    public void removeOnChangeListener(OnChangeListener listener) {
-        mListeners.remove(listener);
-    }
-
-    /**
-     * Interface to receive window bounds changes
-     */
-    public interface OnChangeListener {
-
-        /**
-         * Called when window bounds for secondary window changes
-         */
-        void onSecondaryWindowBoundsChanged();
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 8be544e..35414a6 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -178,7 +178,6 @@
 import com.android.quickstep.util.LayoutUtils;
 import com.android.quickstep.util.RecentsOrientedState;
 import com.android.quickstep.util.SplitAnimationTimings;
-import com.android.quickstep.util.SplitScreenBounds;
 import com.android.quickstep.util.SplitSelectStateController;
 import com.android.quickstep.util.SurfaceTransaction;
 import com.android.quickstep.util.SurfaceTransactionApplier;
@@ -210,7 +209,7 @@
 public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_TYPE>,
         STATE_TYPE extends BaseState<STATE_TYPE>> extends PagedView implements Insettable,
         TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback,
-        TaskVisualsChangeListener, SplitScreenBounds.OnChangeListener {
+        TaskVisualsChangeListener {
 
     private static final String TAG = "RecentsView";
     private static final boolean DEBUG = false;
@@ -948,7 +947,6 @@
         SystemUiProxy.INSTANCE.get(getContext()).setPipAnimationListener(
                 mIPipAnimationListener);
         mOrientationState.initListeners();
-        SplitScreenBounds.INSTANCE.addOnChangeListener(this);
         mTaskOverlayFactory.initListeners();
     }
 
@@ -965,7 +963,6 @@
         executeSideTaskLaunchCallback();
         RecentsModel.INSTANCE.get(getContext()).removeThumbnailChangeListener(this);
         SystemUiProxy.INSTANCE.get(getContext()).setPipAnimationListener(null);
-        SplitScreenBounds.INSTANCE.removeOnChangeListener(this);
         mIPipAnimationListener.setActivityAndRecentsView(null, null);
         mOrientationState.destroyListeners();
         mTaskOverlayFactory.removeListeners();
@@ -2900,6 +2897,19 @@
 
         InteractionJankMonitorWrapper.begin(this,
                 InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER, "First tile selected");
+        anim.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                if (mSplitHiddenTaskView == getRunningTaskView()) {
+                    finishRecentsAnimation(true /* toRecents */, false /* shouldPip */,
+                            null /* onFinishComplete */);
+                } else {
+                    switchToScreenshot(
+                            () -> finishRecentsAnimation(true /* toRecents */,
+                                    false /* shouldPip */, null /* onFinishComplete */));
+                }
+            }
+        });
         anim.addEndListener(success -> {
             if (success) {
                 InteractionJankMonitorWrapper.end(
@@ -4190,8 +4200,6 @@
         mSplitSelectStateController.setInitialTaskSelect(taskView.getTask(),
                 stagePosition, splitEvent, taskView.getItemInfo());
         mSplitHiddenTaskViewIndex = indexOfChild(taskView);
-        finishRecentsAnimation(true /* toRecents */, false /* shouldPip */,
-                null /* onFinishComplete */);
     }
 
     /**
@@ -5198,12 +5206,6 @@
         return null;
     }
 
-    @Override
-    public void onSecondaryWindowBoundsChanged() {
-        // Invalidate the task view size
-        setInsets(mInsets);
-    }
-
     /** Enables or disables modal state for RecentsView */
     public abstract void setModalStateEnabled(boolean isModalState, boolean animate);
 
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index afdb071..8f48ec8 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -367,7 +367,9 @@
     <dimen name="transient_taskbar_margin">0dp</dimen>
     <dimen name="transient_taskbar_shadow_blur">0dp</dimen>
     <dimen name="transient_taskbar_key_shadow_distance">0dp</dimen>
-    <dimen name="transient_taskbar_icon_spacing">10dp</dimen>
+    <dimen name="transient_taskbar_stashed_size">0dp</dimen>
+    <!-- Note that this applies to both sides of all icons, so visible space is double this. -->
+    <dimen name="transient_taskbar_icon_spacing">0dp</dimen>
     <!-- Note that this applies to both sides of all icons, so visible space is double this. -->
     <dimen name="taskbar_icon_spacing">8dp</dimen>
     <dimen name="taskbar_nav_buttons_size">0dp</dimen>
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index edd809c..2b9eb29 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -312,10 +312,14 @@
         }
 
         if (isTaskbarPresent) {
-            taskbarSize = DisplayController.isTransientTaskbar(context)
-                    ? res.getDimensionPixelSize(R.dimen.transient_taskbar_size)
-                    : res.getDimensionPixelSize(R.dimen.taskbar_size);
-            stashedTaskbarSize = res.getDimensionPixelSize(R.dimen.taskbar_stashed_size);
+            if (DisplayController.isTransientTaskbar(context)) {
+                taskbarSize = res.getDimensionPixelSize(R.dimen.transient_taskbar_size);
+                stashedTaskbarSize =
+                        res.getDimensionPixelSize(R.dimen.transient_taskbar_stashed_size);
+            } else {
+                taskbarSize = res.getDimensionPixelSize(R.dimen.taskbar_size);
+                stashedTaskbarSize = res.getDimensionPixelSize(R.dimen.taskbar_stashed_size);
+            }
         }
 
         edgeMarginPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin);
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 9426ae9..c73e077 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -53,6 +53,8 @@
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_EXIT;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ONRESUME;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ONSTOP;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SWIPELEFT;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SWIPERIGHT;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGET_RECONFIGURED;
 import static com.android.launcher3.model.ItemInstallQueue.FLAG_ACTIVITY_PAUSED;
 import static com.android.launcher3.model.ItemInstallQueue.FLAG_DRAG_AND_DROP;
@@ -183,7 +185,6 @@
 import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.touch.AllAppsSwipeController;
-import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
 import com.android.launcher3.util.ActivityResultInfo;
 import com.android.launcher3.util.ActivityTracker;
@@ -222,7 +223,6 @@
 import com.android.systemui.plugins.shared.LauncherExterns;
 import com.android.systemui.plugins.shared.LauncherOverlayManager;
 import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlay;
-import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlayCallbacks;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -242,7 +242,7 @@
  */
 public class Launcher extends StatefulActivity<LauncherState>
         implements LauncherExterns, Callbacks, InvariantDeviceProfile.OnIDPChangeListener,
-        PluginListener<LauncherOverlayPlugin>, LauncherOverlayCallbacks {
+        PluginListener<LauncherOverlayPlugin> {
     public static final String TAG = "Launcher";
 
     public static final ActivityTracker<Launcher> ACTIVITY_TRACKER = new ActivityTracker<>();
@@ -696,17 +696,9 @@
      */
     @Override
     public void setLauncherOverlay(LauncherOverlay overlay) {
-        if (overlay != null) {
-            overlay.setOverlayCallbacks(this);
-        }
         mWorkspace.setLauncherOverlay(overlay);
     }
 
-    @Override
-    public void runOnOverlayHidden(Runnable runnable) {
-        getWorkspace().runOnOverlayHidden(runnable);
-    }
-
     public boolean setLauncherCallbacks(LauncherCallbacks callbacks) {
         mLauncherCallbacks = callbacks;
         return true;
@@ -1214,18 +1206,6 @@
     }
 
     /**
-     * {@code LauncherOverlayCallbacks} scroll amount.
-     * Indicates transition progress to -1 screen.
-     * @param progress From 0 to 1.
-     */
-    @Override
-    public void onScrollChanged(float progress) {
-        if (mWorkspace != null) {
-            mWorkspace.onOverlayScrollChanged(progress);
-        }
-    }
-
-    /**
      * Restores the previous state, if it exists.
      *
      * @param savedState The previous state.
@@ -2895,7 +2875,16 @@
     /**
      * Informs us that the overlay (-1 screen, typically), has either become visible or invisible.
      */
-    public void onOverlayVisibilityChanged(boolean visible) {}
+    public void onOverlayVisibilityChanged(boolean visible) {
+        getStatsLogManager().logger()
+                .withSrcState(LAUNCHER_STATE_HOME)
+                .withDstState(LAUNCHER_STATE_HOME)
+                .withContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
+                        .setWorkspace(WorkspaceContainer.newBuilder()
+                                .setPageIndex(visible ? 0 : -1))
+                        .build())
+                .log(visible ? LAUNCHER_SWIPELEFT : LAUNCHER_SWIPERIGHT);
+    }
 
     /**
      * Informs us that the page transition has ended, so that we can react to the newly selected
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index b6eb589..8f07a0d 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -26,7 +26,6 @@
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.SPRING_LOADED;
 import static com.android.launcher3.anim.AnimatorListeners.forSuccessCallback;
-import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_OVERLAY;
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SWIPELEFT;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SWIPERIGHT;
@@ -58,7 +57,6 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.FrameLayout;
 import android.widget.Toast;
@@ -119,6 +117,7 @@
 import com.android.launcher3.widget.dragndrop.AppWidgetHostViewDragListener;
 import com.android.launcher3.widget.util.WidgetSizes;
 import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlay;
+import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlayCallbacks;
 
 import java.util.ArrayList;
 import java.util.Iterator;
@@ -136,7 +135,7 @@
 public class Workspace<T extends View & PageIndicator> extends PagedView<T>
         implements DropTarget, DragSource, View.OnTouchListener,
         DragController.DragListener, Insettable, StateHandler<LauncherState>,
-        WorkspaceLayoutManager, LauncherBindableItemsContainer {
+        WorkspaceLayoutManager, LauncherBindableItemsContainer, LauncherOverlayCallbacks {
 
     /** The value that {@link #mTransitionProgress} must be greater than for
      * {@link #transitionStateShouldAllowDrop()} to return true. */
@@ -254,14 +253,12 @@
 
     // State related to Launcher Overlay
     private OverlayEdgeEffect mOverlayEdgeEffect;
-    boolean mOverlayShown = false;
-    private Runnable mOnOverlayHiddenCallback;
+    private boolean mOverlayShown = false;
+    private float mOverlayProgress; // 1 -> overlay completely visible, 0 -> home visible
+    private final List<LauncherOverlayCallbacks> mOverlayCallbacks = new ArrayList<>();
 
     private boolean mForceDrawAdjacentPages = false;
 
-    // Total over scrollX in the overlay direction.
-    private float mOverlayTranslation;
-
     // Handles workspace state transitions
     private final WorkspaceStateTransitionAnimation mStateTransitionAnimation;
 
@@ -1151,9 +1148,15 @@
     }
 
     public void setLauncherOverlay(LauncherOverlay overlay) {
-        mOverlayEdgeEffect = overlay == null ? null : new OverlayEdgeEffect(getContext(), overlay);
-        EdgeEffectCompat newEffect = overlay == null
-                ? new EdgeEffectCompat(getContext()) : mOverlayEdgeEffect;
+        final EdgeEffectCompat newEffect;
+        if (overlay == null) {
+            newEffect = new EdgeEffectCompat(getContext());
+            mOverlayEdgeEffect = null;
+        } else {
+            newEffect = mOverlayEdgeEffect = new OverlayEdgeEffect(getContext(), overlay);
+            overlay.setOverlayCallbacks(this);
+        }
+
         if (mIsRtl) {
             mEdgeGlowRight = newEffect;
         } else {
@@ -1203,132 +1206,46 @@
     @Override
     protected boolean shouldFlingForVelocity(int velocityX) {
         // When the overlay is moving, the fling or settle transition is controlled by the overlay.
-        return Float.compare(Math.abs(mOverlayTranslation), 0) == 0 &&
-                super.shouldFlingForVelocity(velocityX);
+        return Float.compare(Math.abs(mOverlayProgress), 0) == 0
+                && super.shouldFlingForVelocity(velocityX);
     }
 
     /**
      * The overlay scroll is being controlled locally, just update our overlay effect
      */
+    @Override
     public void onOverlayScrollChanged(float scroll) {
-        if (Float.compare(scroll, 1f) == 0) {
+        mOverlayProgress = Utilities.boundToRange(scroll, 0, 1);
+        if (Float.compare(mOverlayProgress, 1f) == 0) {
             if (!mOverlayShown) {
-                mLauncher.getStatsLogManager().logger()
-                        .withSrcState(LAUNCHER_STATE_HOME)
-                        .withDstState(LAUNCHER_STATE_HOME)
-                        .withContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
-                                .setWorkspace(
-                                        LauncherAtom.WorkspaceContainer.newBuilder()
-                                                .setPageIndex(0))
-                                .build())
-                        .log(LAUNCHER_SWIPELEFT);
+                mOverlayShown = true;
+                mLauncher.onOverlayVisibilityChanged(true);
             }
-            mOverlayShown = true;
-
-            // Let the Launcher activity know that the overlay is now visible.
-            mLauncher.onOverlayVisibilityChanged(mOverlayShown);
-
-            // Not announcing the overlay page for accessibility since it announces itself.
-        } else if (Float.compare(scroll, 0f) == 0) {
+        } else if (Float.compare(mOverlayProgress, 0f) == 0) {
             if (mOverlayShown) {
-                // TODO: this is logged unnecessarily on home gesture.
-                mLauncher.getStatsLogManager().logger()
-                        .withSrcState(LAUNCHER_STATE_HOME)
-                        .withDstState(LAUNCHER_STATE_HOME)
-                        .withContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
-                                .setWorkspace(
-                                        LauncherAtom.WorkspaceContainer.newBuilder()
-                                                .setPageIndex(-1))
-                                .build())
-                        .log(LAUNCHER_SWIPERIGHT);
-            } else if (Float.compare(mOverlayTranslation, 0f) != 0) {
-                // When arriving to 0 overscroll from non-zero overscroll, announce page for
-                // accessibility since default announcements were disabled while in overscroll
-                // state.
-                // Not doing this if mOverlayShown because in that case the accessibility service
-                // will announce the launcher window description upon regaining focus after
-                // switching from the overlay screen.
-                announcePageForAccessibility();
+                mOverlayShown = false;
+                mLauncher.onOverlayVisibilityChanged(false);
             }
-            mOverlayShown = false;
-
-            // Let the Launcher activity know that the overlay is no longer visible.
-            mLauncher.onOverlayVisibilityChanged(mOverlayShown);
-
-            tryRunOverlayCallback();
         }
-
-        float offset = 0f;
-
-        scroll = Math.max(scroll - offset, 0);
-        scroll = Math.min(1, scroll / (1 - offset));
-
-        float alpha = 1 - Interpolators.DEACCEL_3.getInterpolation(scroll);
-        float transX = mLauncher.getDragLayer().getMeasuredWidth() * scroll;
-
-        if (mIsRtl) {
-            transX = -transX;
+        int count = mOverlayCallbacks.size();
+        for (int i = 0; i < count; i++) {
+            mOverlayCallbacks.get(i).onOverlayScrollChanged(mOverlayProgress);
         }
-        mOverlayTranslation = transX;
-
-        // TODO(adamcohen): figure out a final effect here. We may need to recommend
-        // different effects based on device performance. On at least one relatively high-end
-        // device I've tried, translating the launcher causes things to get quite laggy.
-        mLauncher.getDragLayer().setTranslationX(transX);
-        mLauncher.getDragLayer().getAlphaProperty(ALPHA_INDEX_OVERLAY).setValue(alpha);
     }
 
     /**
-     * @return false if the callback is still pending
+     * Adds a callback for receiving overlay progress
      */
-    private boolean tryRunOverlayCallback() {
-        if (mOnOverlayHiddenCallback == null) {
-            // Return true as no callback is pending. This is used by OnWindowFocusChangeListener
-            // to remove itself if multiple focus handles were added.
-            return true;
-        }
-        if (mOverlayShown || !hasWindowFocus()) {
-            return false;
-        }
-
-        mOnOverlayHiddenCallback.run();
-        mOnOverlayHiddenCallback = null;
-        return true;
+    public void addOverlayCallback(LauncherOverlayCallbacks callback) {
+        mOverlayCallbacks.add(callback);
+        callback.onOverlayScrollChanged(mOverlayProgress);
     }
 
     /**
-     * Runs the given callback when the minus one overlay is hidden. Specifically, it is run
-     * when launcher's window has focus and the overlay is no longer being shown. If a callback
-     * is already present, the new callback will chain off it so both are run.
-     *
-     * @return Whether the callback was deferred.
+     * Removes a previously added overlay progress callback
      */
-    public boolean runOnOverlayHidden(Runnable callback) {
-        if (mOnOverlayHiddenCallback == null) {
-            mOnOverlayHiddenCallback = callback;
-        } else {
-            // Chain the new callback onto the previous callback(s).
-            Runnable oldCallback = mOnOverlayHiddenCallback;
-            mOnOverlayHiddenCallback = () -> {
-                oldCallback.run();
-                callback.run();
-            };
-        }
-        if (!tryRunOverlayCallback()) {
-            ViewTreeObserver observer = getViewTreeObserver();
-            if (observer != null && observer.isAlive()) {
-                observer.addOnWindowFocusChangeListener(
-                        new ViewTreeObserver.OnWindowFocusChangeListener() {
-                            @Override
-                            public void onWindowFocusChanged(boolean hasFocus) {
-                                if (tryRunOverlayCallback() && observer.isAlive()) {
-                                    observer.removeOnWindowFocusChangeListener(this);
-                                }
-                            }});
-            }
-            return true;
-        }
-        return false;
+    public void removeOverlayCallback(LauncherOverlayCallbacks callback) {
+        mOverlayCallbacks.remove(callback);
     }
 
     @Override
@@ -3470,7 +3387,7 @@
     protected boolean canAnnouncePageDescription() {
         // Disable announcements while overscrolling potentially to overlay screen because if we end
         // up on the overlay screen, it will take care of announcing itself.
-        return Float.compare(mOverlayTranslation, 0f) == 0;
+        return Float.compare(mOverlayProgress, 0f) == 0;
     }
 
     @Override
diff --git a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
index 2ff992e..da86d98 100644
--- a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
@@ -107,8 +107,7 @@
             new RecyclerView.OnScrollListener() {
                 @Override
                 public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
-                    updateHeaderScroll(
-                            ((AllAppsRecyclerView) recyclerView).computeVerticalScrollOffset());
+                    updateHeaderScroll(recyclerView.computeVerticalScrollOffset());
                 }
             };
 
@@ -192,7 +191,6 @@
                 reset(true);
             }
         }
-
     }
 
     @Override
@@ -815,6 +813,10 @@
         }
     }
 
+    protected void onInitializeRecyclerView(RecyclerView rv) {
+        rv.addOnScrollListener(mScrollListener);
+    }
+
     /** Holds a {@link BaseAllAppsAdapter} and related fields. */
     public class AdapterHolder {
         public static final int MAIN = 0;
@@ -851,7 +853,7 @@
             mRecyclerView.setHasFixedSize(true);
             // No animations will occur when changes occur to the items in this RecyclerView.
             mRecyclerView.setItemAnimator(null);
-            mRecyclerView.addOnScrollListener(mScrollListener);
+            onInitializeRecyclerView(mRecyclerView);
             FocusedItemDecorator focusedItemDecorator = new FocusedItemDecorator(mRecyclerView);
             mRecyclerView.addItemDecoration(focusedItemDecorator);
             mAdapter.setIconFocusListener(focusedItemDecorator.getFocusListener());
diff --git a/src/com/android/launcher3/allapps/BaseSearchConfig.java b/src/com/android/launcher3/allapps/BaseSearchConfig.java
index 9f47e8d..3900954 100644
--- a/src/com/android/launcher3/allapps/BaseSearchConfig.java
+++ b/src/com/android/launcher3/allapps/BaseSearchConfig.java
@@ -25,4 +25,11 @@
     public boolean isKeyboardSyncEnabled() {
         return false;
     }
+
+    /**
+     * Returns whether IME is enabled on swipe up.
+     */
+    public boolean isImeEnabledOnSwipeUp() {
+        return false;
+    }
 }
diff --git a/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java b/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java
index 20f5e74..92e29bb 100644
--- a/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java
@@ -16,19 +16,50 @@
 package com.android.launcher3.allapps;
 
 import android.content.Context;
+import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 import android.view.WindowInsets;
 
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.launcher3.ExtendedEditText;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.config.FeatureFlags;
 
 /**
  * AllAppsContainerView with launcher specific callbacks
  */
 public class LauncherAllAppsContainerView extends ActivityAllAppsContainerView<Launcher> {
 
+    private final RecyclerView.OnScrollListener mActivityScrollListener =
+            new RecyclerView.OnScrollListener() {
+                @Override
+                public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
+                    int scrolledOffset = recyclerView.computeVerticalScrollOffset();
+                    ExtendedEditText input = mSearchUiManager.getEditText();
+                    // Scroll up and scroll to top
+                    if (dy < 0 && scrolledOffset == 0 && input != null) {
+                        boolean isImeEnabledOnSwipeUp = Launcher.getLauncher(mActivityContext)
+                                .getSearchConfig().isImeEnabledOnSwipeUp();
+                        if (isImeEnabledOnSwipeUp || !TextUtils.isEmpty(input.getText())) {
+                            input.showKeyboard();
+                        }
+                    }
+                }
+            };
+
+    @Override
+    protected void onInitializeRecyclerView(RecyclerView rv) {
+        super.onInitializeRecyclerView(rv);
+        if (FeatureFlags.SCROLL_TOP_TO_RESET.get()) {
+            rv.addOnScrollListener(mActivityScrollListener);
+        }
+    }
+
     public LauncherAllAppsContainerView(Context context) {
         this(context, null);
     }
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 32463a5..4287779 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -96,6 +96,10 @@
             "ENABLE_QUICK_LAUNCH_V2", false, "Use quick launch v2 "
             + "behavior. Quick search and quick launch v1 would be unavailable if this is enabled");
 
+    public static final BooleanFlag GBOARD_UPDATE_ENTER_KEY = new DeviceFlag(
+            "GBOARD_UPDATE_ENTER_KEY", false, "Update gBoard enter key "
+            + "icon dynamically based on top search content for Quick Launch V2");
+
     public static final BooleanFlag ENABLE_HIDE_HEADER = new DeviceFlag("ENABLE_HIDE_HEADER",
             true, "Hide header on keyboard before typing in all apps");
 
@@ -290,6 +294,10 @@
     public static final BooleanFlag ENABLE_WIDGET_PICKER_DEPTH = new DeviceFlag(
             "ENABLE_WIDGET_PICKER_DEPTH", true, "Enable changing depth in widget picker.");
 
+    public static final BooleanFlag SCROLL_TOP_TO_RESET = new DeviceFlag(
+            "SCROLL_TOP_TO_RESET", false, "Bring up IME and focus on "
+            + "input when scroll to top if 'Always show keyboard' is enabled or in prefix state");
+
     public static final BooleanFlag SHOW_DELIGHTFUL_PAGINATION = getDebugFlag(
             "SHOW_DELIGHTFUL_PAGINATION", false,
             "Enable showing the new 'delightful pagination' which is a brand"
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index 4bea0ad..1ee7fc1 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -47,7 +47,9 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.ShortcutAndWidgetContainer;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.Workspace;
+import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.anim.SpringProperty;
 import com.android.launcher3.celllayout.CellLayoutLayoutParams;
@@ -56,13 +58,14 @@
 import com.android.launcher3.keyboard.ViewGroupFocusHelper;
 import com.android.launcher3.util.TouchController;
 import com.android.launcher3.views.BaseDragLayer;
+import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlayCallbacks;
 
 import java.util.ArrayList;
 
 /**
  * A ViewGroup that coordinates dragging across its descendants
  */
-public class DragLayer extends BaseDragLayer<Launcher> {
+public class DragLayer extends BaseDragLayer<Launcher> implements LauncherOverlayCallbacks {
 
     public static final int ALPHA_INDEX_OVERLAY = 0;
     private static final int ALPHA_CHANNEL_COUNT = 1;
@@ -70,6 +73,8 @@
     public static final int ANIMATION_END_DISAPPEAR = 0;
     public static final int ANIMATION_END_REMAIN_VISIBLE = 2;
 
+    private final boolean mIsRtl;
+
     private DragController mDragController;
 
     // Variables relating to animation of views after drop
@@ -100,6 +105,7 @@
         setChildrenDrawingOrderEnabled(true);
 
         mFocusIndicatorHelper = new ViewGroupFocusHelper(this);
+        mIsRtl = Utilities.isRtl(getResources());
     }
 
     /**
@@ -109,6 +115,7 @@
         mDragController = dragController;
         recreateControllers();
         mWorkspaceDragScrim = new Scrim(this);
+        workspace.addOverlayCallback(this);
     }
 
     @Override
@@ -476,4 +483,16 @@
             controller.onOneHandedModeStateChanged(activated);
         }
     }
+
+    @Override
+    public void onOverlayScrollChanged(float progress) {
+        float alpha = 1 - Interpolators.DEACCEL_3.getInterpolation(progress);
+        float transX = getMeasuredWidth() * progress;
+
+        if (mIsRtl) {
+            transX = -transX;
+        }
+        setTranslationX(transX);
+        getAlphaProperty(ALPHA_INDEX_OVERLAY).setValue(alpha);
+    }
 }
diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java
index fdd30e1..acb7eb3 100644
--- a/src/com/android/launcher3/testing/TestInformationHandler.java
+++ b/src/com/android/launcher3/testing/TestInformationHandler.java
@@ -47,7 +47,9 @@
 import com.android.launcher3.util.ResourceBasedOverride;
 import com.android.launcher3.widget.picker.WidgetsFullSheet;
 
+import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
 import java.util.function.Function;
 import java.util.function.Supplier;
 
@@ -214,8 +216,7 @@
             }
 
             case TestProtocol.REQUEST_HAS_TIS: {
-                response.putBoolean(
-                        TestProtocol.REQUEST_HAS_TIS, false);
+                response.putBoolean(TestProtocol.REQUEST_HAS_TIS, false);
                 return response;
             }
 
@@ -266,17 +267,24 @@
      */
     private static <S, T> Bundle getUIProperty(
             BundleSetter<T> bundleSetter, Function<S, T> provider, Supplier<S> targetSupplier) {
+        return getFromExecutorSync(MAIN_EXECUTOR, () -> {
+            S target = targetSupplier.get();
+            if (target == null) {
+                return null;
+            }
+            T value = provider.apply(target);
+            Bundle response = new Bundle();
+            bundleSetter.set(response, TestProtocol.TEST_INFO_RESPONSE_FIELD, value);
+            return response;
+        });
+    }
+
+    /**
+     * Executes the callback on the executor and waits for the result
+     */
+    protected static <T> T getFromExecutorSync(ExecutorService executor, Callable<T> callback) {
         try {
-            return MAIN_EXECUTOR.submit(() -> {
-                S target = targetSupplier.get();
-                if (target == null) {
-                    return null;
-                }
-                T value = provider.apply(target);
-                Bundle response = new Bundle();
-                bundleSetter.set(response, TestProtocol.TEST_INFO_RESPONSE_FIELD, value);
-                return response;
-            }).get();
+            return executor.submit(callback).get();
         } catch (ExecutionException | InterruptedException e) {
             throw new RuntimeException(e);
         }
diff --git a/src/com/android/launcher3/testing/shared/TestProtocol.java b/src/com/android/launcher3/testing/shared/TestProtocol.java
index 792d475..3fbce88 100644
--- a/src/com/android/launcher3/testing/shared/TestProtocol.java
+++ b/src/com/android/launcher3/testing/shared/TestProtocol.java
@@ -128,6 +128,7 @@
     public static final String REQUEST_GET_OVERVIEW_PAGE_SPACING = "get-overview-page-spacing";
     public static final String REQUEST_ENABLE_ROTATION = "enable_rotation";
     public static final String REQUEST_ENABLE_SUGGESTION = "enable-suggestion";
+    public static final String REQUEST_MODEL_QUEUE_CLEARED = "model-queue-cleared";
 
     public static boolean sDebugTracing = false;
     public static final String REQUEST_ENABLE_DEBUG_TRACING = "enable-debug-tracing";
diff --git a/src_plugins/com/android/systemui/plugins/shared/LauncherExterns.java b/src_plugins/com/android/systemui/plugins/shared/LauncherExterns.java
index 13e4999..173b454 100644
--- a/src_plugins/com/android/systemui/plugins/shared/LauncherExterns.java
+++ b/src_plugins/com/android/systemui/plugins/shared/LauncherExterns.java
@@ -40,10 +40,4 @@
      * Sets the overlay on the target activity
      */
     void setLauncherOverlay(LauncherOverlay overlay);
-
-    /**
-     * Executes the command, next time the overlay is hidden
-     */
-    void runOnOverlayHidden(Runnable runnable);
-
 }
diff --git a/src_plugins/com/android/systemui/plugins/shared/LauncherOverlayManager.java b/src_plugins/com/android/systemui/plugins/shared/LauncherOverlayManager.java
index ac02ba4..582ab23 100644
--- a/src_plugins/com/android/systemui/plugins/shared/LauncherOverlayManager.java
+++ b/src_plugins/com/android/systemui/plugins/shared/LauncherOverlayManager.java
@@ -93,6 +93,6 @@
 
     interface LauncherOverlayCallbacks {
 
-        void onScrollChanged(float progress);
+        void onOverlayScrollChanged(float progress);
     }
 }
diff --git a/tests/AndroidManifest-common.xml b/tests/AndroidManifest-common.xml
index 4af8468..bedf277 100644
--- a/tests/AndroidManifest-common.xml
+++ b/tests/AndroidManifest-common.xml
@@ -24,6 +24,7 @@
     <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
     <uses-permission android:name="android.permission.READ_LOGS"/>
     <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
+    <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
 
     <application android:debuggable="true" android:extractNativeLibs="true">
         <uses-library android:name="android.test.runner"/>
diff --git a/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java b/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java
index 2846cae..9ba3f7b 100644
--- a/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java
+++ b/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java
@@ -116,7 +116,7 @@
         FavoriteItemsTransaction transaction =
                 new FavoriteItemsTransaction(mTargetContext, this);
         mWorkspaceBuilder.buildFromBoard(testCase.mStart, transaction).commit();
-
+        waitForLauncherCondition("Workspace didn't finish loading", l -> !l.isWorkspaceLoading());
         Widget widget = mLauncher.getWorkspace().getWidgetAtCell(mainWidgetCellPos.x,
                 mainWidgetCellPos.y);
         assertNotNull(widget);
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index 01e6ed7..978e84c 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -29,7 +29,6 @@
 
 import android.content.Intent;
 import android.graphics.Point;
-import android.os.SystemClock;
 import android.platform.test.annotations.IwTest;
 
 import androidx.test.filters.LargeTest;
@@ -479,7 +478,7 @@
     @Test
     @PortraitLandscape
     public void testUninstallFromWorkspace() throws Exception {
-        TestUtil.installDummyApp();
+        installDummyAppAndWaitForUIUpdate();
         try {
             verifyAppUninstalledFromAllApps(
                     createShortcutInCenterIfNotExist(DUMMY_APP_NAME).uninstall(), DUMMY_APP_NAME);
@@ -492,10 +491,8 @@
     @PortraitLandscape
     @ScreenRecord // (b/256659409)
     public void testUninstallFromAllApps() throws Exception {
-        TestUtil.installDummyApp();
+        installDummyAppAndWaitForUIUpdate();
         try {
-            // b/256659409
-            SystemClock.sleep(5000);
             Workspace workspace = mLauncher.getWorkspace();
             final HomeAllApps allApps = workspace.switchToAllApps();
             allApps.freeze();
@@ -539,7 +536,7 @@
         Point[] gridPositions = getCornersAndCenterPositions();
         createShortcutIfNotExist(STORE_APP_NAME, gridPositions[0]);
         createShortcutIfNotExist(MAPS_APP_NAME, gridPositions[1]);
-        TestUtil.installDummyApp();
+        installDummyAppAndWaitForUIUpdate();
         try {
             createShortcutIfNotExist(DUMMY_APP_NAME, gridPositions[2]);
             Map<String, Point> initialPositions =
@@ -590,6 +587,17 @@
                 mLauncher.getWorkspace().getHotseatAppIcon(APP_NAME));
     }
 
+    private void installDummyAppAndWaitForUIUpdate() throws IOException {
+        TestUtil.installDummyApp();
+        // Wait for model thread completion as it may be processing
+        // the install event from the SystemService
+        mLauncher.waitForModelQueueCleared();
+        // Wait for Launcher UI thread completion, as it may be processing updating the UI in
+        // response to the model update. Not that `waitForLauncherInitialized` is just a proxy
+        // method, we can use any method which touches Launcher UI thread,
+        mLauncher.waitForLauncherInitialized();
+    }
+
     /**
      * @return List of workspace grid coordinates. Those are not pixels. See {@link
      *     Workspace#getIconGridDimensions()}
diff --git a/tests/src/com/android/launcher3/util/TestUtil.java b/tests/src/com/android/launcher3/util/TestUtil.java
index 67f3902..d7c6c4f 100644
--- a/tests/src/com/android/launcher3/util/TestUtil.java
+++ b/tests/src/com/android/launcher3/util/TestUtil.java
@@ -17,8 +17,13 @@
 
 import static androidx.test.InstrumentationRegistry.getContext;
 import static androidx.test.InstrumentationRegistry.getInstrumentation;
+import static androidx.test.InstrumentationRegistry.getTargetContext;
 
+import android.content.pm.LauncherApps;
 import android.content.res.Resources;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserHandle;
 
 import androidx.test.uiautomator.UiDevice;
 
@@ -27,6 +32,7 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.concurrent.CountDownLatch;
 
 public class TestUtil {
     public static final String DUMMY_PACKAGE = "com.example.android.aardwolf";
@@ -40,24 +46,77 @@
         final String apkFilename = getInstrumentation().getTargetContext().
                 getFilesDir().getPath() + "/dummy_app.apk";
 
-        final FileOutputStream out = new FileOutputStream(apkFilename);
-        byte[] buff = new byte[1024];
-        int read;
+        try (PackageInstallCheck pic = new PackageInstallCheck()) {
+            final FileOutputStream out = new FileOutputStream(apkFilename);
+            byte[] buff = new byte[1024];
+            int read;
 
-        while ((read = in.read(buff)) > 0) {
-            out.write(buff, 0, read);
+            while ((read = in.read(buff)) > 0) {
+                out.write(buff, 0, read);
+            }
+            in.close();
+            out.close();
+
+            final String result = UiDevice.getInstance(getInstrumentation())
+                    .executeShellCommand("pm install " + apkFilename);
+            Assert.assertTrue(
+                    "Failed to install wellbeing test apk; make sure the device is rooted",
+                    "Success".equals(result.replaceAll("\\s+", "")));
+            pic.mAddWait.await();
+        } catch (InterruptedException e) {
+            throw new IOException(e);
         }
-        in.close();
-        out.close();
-
-        final String result = UiDevice.getInstance(getInstrumentation())
-                .executeShellCommand("pm install " + apkFilename);
-        Assert.assertTrue("Failed to install wellbeing test apk; make sure the device is rooted",
-                "Success".equals(result.replaceAll("\\s+", "")));
     }
 
     public static void uninstallDummyApp() throws IOException {
         UiDevice.getInstance(getInstrumentation()).executeShellCommand(
                 "pm uninstall " + DUMMY_PACKAGE);
     }
+
+    private static class PackageInstallCheck extends LauncherApps.Callback
+            implements AutoCloseable {
+
+        final CountDownLatch mAddWait = new CountDownLatch(1);
+        final LauncherApps mLauncherApps;
+
+        PackageInstallCheck() {
+            mLauncherApps = getTargetContext().getSystemService(LauncherApps.class);
+            mLauncherApps.registerCallback(this, new Handler(Looper.getMainLooper()));
+        }
+
+        private void verifyPackage(String packageName) {
+            if (DUMMY_PACKAGE.equals(packageName)) {
+                mAddWait.countDown();
+            }
+        }
+
+        @Override
+        public void onPackageAdded(String packageName, UserHandle user) {
+            verifyPackage(packageName);
+        }
+
+        @Override
+        public void onPackageChanged(String packageName, UserHandle user) {
+            verifyPackage(packageName);
+        }
+
+        @Override
+        public void onPackageRemoved(String packageName, UserHandle user) { }
+
+        @Override
+        public void onPackagesAvailable(String[] packageNames, UserHandle user, boolean replacing) {
+            for (String packageName : packageNames) {
+                verifyPackage(packageName);
+            }
+        }
+
+        @Override
+        public void onPackagesUnavailable(String[] packageNames, UserHandle user,
+                boolean replacing) { }
+
+        @Override
+        public void close() {
+            mLauncherApps.unregisterCallback(this);
+        }
+    }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 449b7b7..1c5c5fa 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -848,6 +848,10 @@
         }
     }
 
+    public void waitForModelQueueCleared() {
+        getTestInfo(TestProtocol.REQUEST_MODEL_QUEUE_CLEARED);
+    }
+
     public void waitForLauncherInitialized() {
         for (int i = 0; i < 100; ++i) {
             if (getTestInfo(