Merge "Removing logging for verifying internal Launcher state from tests" into main
diff --git a/Android.bp b/Android.bp
index 4354b66..8f2e5a6 100644
--- a/Android.bp
+++ b/Android.bp
@@ -204,6 +204,7 @@
         "com_android_launcher3_flags_lib",
         "com_android_wm_shell_flags_lib",
         "android.appwidget.flags-aconfig-java",
+        "com.android.window.flags.window-aconfig-java",
     ],
     sdk_version: "current",
     min_sdk_version: min_launcher3_sdk_version,
diff --git a/aconfig/Android.bp b/aconfig/Android.bp
index 5413601..bca7494 100644
--- a/aconfig/Android.bp
+++ b/aconfig/Android.bp
@@ -20,7 +20,7 @@
 aconfig_declarations {
     name: "com_android_launcher3_flags",
     package: "com.android.launcher3",
-    container: "system",
+    container: "system_ext",
     srcs: ["**/*.aconfig"],
 }
 
diff --git a/aconfig/launcher.aconfig b/aconfig/launcher.aconfig
index 147cac6..a37141c 100644
--- a/aconfig/launcher.aconfig
+++ b/aconfig/launcher.aconfig
@@ -1,5 +1,5 @@
 package: "com.android.launcher3"
-container: "system"
+container: "system_ext"
 
 flag {
     name: "enable_expanding_pause_work_button"
@@ -186,3 +186,23 @@
     description: "Use an activity for home screen overlay"
     bug: "273828110"
 }
+
+flag {
+    name: "grid_migration_fix"
+    namespace: "launcher"
+    description: "Keep items in place when migrating to a bigger grid"
+    bug: "325286145"
+    metadata {
+      purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
+    name: "narrow_grid_restore"
+    namespace: "launcher"
+    description: "Using only the most recent workspace when restoring to avoid confusion."
+    bug: "325285743"
+    metadata {
+      purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/aconfig/launcher_search.aconfig b/aconfig/launcher_search.aconfig
index 2f2690e..31d8d34 100644
--- a/aconfig/launcher_search.aconfig
+++ b/aconfig/launcher_search.aconfig
@@ -1,5 +1,5 @@
 package: "com.android.launcher3"
-container: "system"
+container: "system_ext"
 
 flag {
     name: "enable_private_space"
@@ -41,4 +41,4 @@
     namespace: "launcher_search"
     description: "This flag disables drag and drop for Private Space Items."
     bug: "289223923"
-}
\ No newline at end of file
+}
diff --git a/quickstep/res/layout/task_menu.xml b/quickstep/res/layout/task_menu.xml
index 622edfe..b6d8786 100644
--- a/quickstep/res/layout/task_menu.xml
+++ b/quickstep/res/layout/task_menu.xml
@@ -35,11 +35,17 @@
         android:paddingBottom="@dimen/task_menu_edge_padding"
         android:textSize="16sp"/>
 
-    <LinearLayout
-        android:id="@+id/menu_option_layout"
+    <ScrollView
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="horizontal"
-        android:showDividers="middle" />
+        android:layout_height="wrap_content">
+
+        <LinearLayout
+            android:id="@+id/menu_option_layout"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal"
+            android:showDividers="middle" />
+
+    </ScrollView>
 
 </com.android.quickstep.views.TaskMenuView>
\ No newline at end of file
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 853ac74..3331321 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -401,6 +401,9 @@
     <dimen name="taskbar_pinning_popup_menu_width">300dp</dimen>
     <dimen name="taskbar_pinning_popup_menu_vertical_margin">16dp</dimen>
 
+    <!--- Floating Ime Inset height-->
+    <dimen name="floating_ime_inset_height">60dp</dimen>
+
     <!-- Recents overview -->
     <dimen name="recents_filter_icon_size">30dp</dimen>
 
diff --git a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
index 842f0ef..15180ef 100644
--- a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
+++ b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
@@ -35,7 +35,7 @@
 import androidx.annotation.UiThread;
 
 import com.android.systemui.animation.RemoteAnimationDelegate;
-import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
+import com.android.systemui.animation.RemoteAnimationRunnerCompat;
 
 import java.lang.ref.WeakReference;
 
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 2a6e37d..75b8796c 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -161,10 +161,10 @@
 import com.android.systemui.animation.DelegateTransitionAnimatorController;
 import com.android.systemui.animation.LaunchableView;
 import com.android.systemui.animation.RemoteAnimationDelegate;
+import com.android.systemui.animation.RemoteAnimationRunnerCompat;
 import com.android.systemui.shared.system.BlurUtils;
 import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
 import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
 import com.android.wm.shell.startingsurface.IStartingWindowListener;
 
 import java.io.PrintWriter;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index d12e187..87662e6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -72,6 +72,8 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
+import androidx.core.graphics.Insets;
+import androidx.core.view.WindowInsetsCompat;
 
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.BubbleTextView;
@@ -418,6 +420,28 @@
     }
 
     /**
+     * Returns if software keyboard is docked or input toolbar is placed at the taskbar area
+     */
+    public boolean isImeDocked() {
+        View dragLayer = getDragLayer();
+        WindowInsets insets = dragLayer.getRootWindowInsets();
+        if (insets == null) {
+            return false;
+        }
+
+        WindowInsetsCompat insetsCompat =
+                WindowInsetsCompat.toWindowInsetsCompat(insets, dragLayer.getRootView());
+
+        if (insetsCompat.isVisible(WindowInsetsCompat.Type.ime())) {
+            Insets imeInsets = insetsCompat.getInsets(WindowInsetsCompat.Type.ime());
+            return imeInsets.bottom >= getResources().getDimensionPixelSize(
+                    R.dimen.floating_ime_inset_height);
+        } else {
+            return false;
+        }
+    }
+
+    /**
      * Show Taskbar upon receiving broadcast
      */
     public void showTaskbarFromBroadcast() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
index f6478df..7f201b4 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
@@ -30,9 +30,12 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewTreeObserver;
+import android.view.WindowInsets;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.core.graphics.Insets;
+import androidx.core.view.WindowInsetsCompat;
 
 import com.android.app.viewcapture.SettingsAwareViewCapture;
 import com.android.launcher3.AbstractFloatingView;
@@ -111,6 +114,18 @@
     }
 
     @Override
+    public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+        if (insets != null) {
+            WindowInsetsCompat insetsCompat = WindowInsetsCompat.toWindowInsetsCompat(insets, this);
+            Insets imeInsets = insetsCompat.getInsets(WindowInsetsCompat.Type.ime());
+            if (imeInsets != null) {
+                mControllerCallbacks.onImeInsetChanged();
+            }
+        }
+        return insets;
+    }
+
+    @Override
     public void recreateControllers() {
         mControllers = mControllerCallbacks.getTouchControllers();
     }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
index 74eda24..e48c20d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
@@ -276,6 +276,13 @@
         }
 
         /**
+         * Called when an IME inset is changed.
+         */
+        public void onImeInsetChanged() {
+            mControllers.taskbarStashController.onImeInsetChanged();
+        }
+
+        /**
          * Called when a child is removed from TaskbarDragLayer.
          */
         public void onDragLayerViewRemoved() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index c4be85f..854b0d0 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -310,8 +310,16 @@
 
         boolean isTransientTaskbar = DisplayController.isTransientTaskbar(mActivity);
         boolean isInSetup = !mActivity.isUserSetupComplete() || setupUIVisible;
-        boolean isStashedInAppAuto =
-                isTransientTaskbar && !mTaskbarSharedState.getTaskbarWasPinned();
+        boolean taskbarWasPinned = mTaskbarSharedState.getTaskbarWasPinned();
+        boolean isStashedInAppAuto = isTransientTaskbar && !taskbarWasPinned;
+
+        // now that we know we need to keep transient taskbar unstashed after unpinning animation
+        // we need to reset the shared state, so everytime user recreates taskbar we don't unstash
+        // transient taskbar by default.
+        if (mTaskbarSharedState.getTaskbarWasPinned()) {
+            mTaskbarSharedState.setTaskbarWasPinned(false);
+        }
+
         if (ENABLE_TASKBAR_NAVBAR_UNIFICATION) {
             isStashedInAppAuto = isStashedInAppAuto && mTaskbarSharedState.taskbarWasStashedAuto;
         }
@@ -324,8 +332,7 @@
         // us that we're paused until a bit later. This avoids flickering upon recreating taskbar.
         updateStateForFlag(FLAG_IN_APP, true);
         applyState(/* duration = */ 0);
-        if (mTaskbarSharedState.getTaskbarWasPinned()
-                || !mTaskbarSharedState.taskbarWasStashedAuto) {
+        if (taskbarWasPinned || !mTaskbarSharedState.taskbarWasStashedAuto) {
             tryStartTaskbarTimeout();
         }
         notifyStashChange(/* visible */ false, /* stashed */ isStashedInApp());
@@ -893,6 +900,10 @@
         }
 
         // Only update the following flags when system gesture is not in progress.
+        setStashedImeState();
+    }
+
+    private void setStashedImeState() {
         boolean shouldStashForIme = shouldStashForIme();
         updateStateForFlag(FLAG_STASHED_IN_TASKBAR_ALL_APPS, false);
         if (hasAnyFlag(FLAG_STASHED_IN_APP_IME) != shouldStashForIme) {
@@ -904,6 +915,13 @@
     }
 
     /**
+     * Should be called when Ime inset is changed to determine if taskbar should be stashed
+     */
+    public void onImeInsetChanged() {
+        setStashedImeState();
+    }
+
+    /**
      * When hiding the IME, delay the unstash animation to align with the end of the transition.
      */
     private long getTaskbarStashStartDelayForIme() {
@@ -952,7 +970,7 @@
      *
      * <p>Do not stash if in small screen, with 3 button nav, and in landscape (or seascape).
      * <p>Do not stash if taskbar is transient.
-     * <p>Do not stash if hardware keyboard is attached and taskbar is pinned.
+     * <p>Do not stash if hardware keyboard is attached and taskbar is pinned and IME is docked
      */
     private boolean shouldStashForIme() {
         if (DisplayController.isTransientTaskbar(mActivity)) {
@@ -963,8 +981,10 @@
                 && mActivity.getDeviceProfile().isLandscape) {
             return false;
         }
-        // Do not stash if pinned taskbar and hardware keyboard is attached.
-        if (mActivity.isHardwareKeyboard() && DisplayController.isPinnedTaskbar(mActivity)) {
+
+        // Do not stash if pinned taskbar, hardware keyboard is attached and no IME is docked
+        if (mActivity.isHardwareKeyboard() && DisplayController.isPinnedTaskbar(mActivity)
+                && !mActivity.isImeDocked()) {
             return false;
         }
 
diff --git a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
index 5c4eb9d..dcc3b05 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
@@ -21,6 +21,7 @@
 import android.app.Person;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ActivityInfo;
 import android.content.pm.LauncherActivityInfo;
 import android.content.pm.LauncherApps;
 import android.content.pm.LauncherUserInfo;
@@ -156,6 +157,13 @@
         }
     }
 
+    /**
+     * Checks if an activity is flagged as non-resizeable.
+     */
+    public static boolean isNonResizeableActivity(LauncherActivityInfo lai) {
+        return lai.getActivityInfo().resizeMode == ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+    }
+
     private static class NoopDrawable extends ColorDrawable {
         @Override
         public int getIntrinsicHeight() {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
index dbaeafb..2eced74 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
@@ -112,7 +112,8 @@
         boolean exitingOverview = !FeatureFlags.enableSplitContextually() && !toState.overviewUi;
         if (mRecentsView.isSplitSelectionActive() && exitingOverview) {
             setter.add(mRecentsView.getSplitSelectController().getSplitAnimationController()
-                    .createPlaceholderDismissAnim(mLauncher, LAUNCHER_SPLIT_SELECTION_EXIT_HOME));
+                    .createPlaceholderDismissAnim(mLauncher, LAUNCHER_SPLIT_SELECTION_EXIT_HOME,
+                            setter.getDuration()));
             setter.setViewAlpha(
                     mRecentsView.getSplitInstructionsView(),
                     0,
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 7558b00..2c45129 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -200,7 +200,7 @@
 import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
-import java.util.function.Consumer;
+import java.util.function.BiConsumer;
 import java.util.function.Predicate;
 import java.util.stream.Stream;
 
@@ -599,9 +599,10 @@
 
         ArrayList<TouchController> list = new ArrayList<>();
         list.add(getDragController());
-        Consumer<AnimatorSet> splitAnimator = animatorSet ->
+        BiConsumer<AnimatorSet, Long> splitAnimator = (animatorSet, duration) ->
                 animatorSet.play(mSplitSelectStateController.getSplitAnimationController()
-                        .createPlaceholderDismissAnim(this, LAUNCHER_SPLIT_SELECTION_EXIT_HOME));
+                        .createPlaceholderDismissAnim(this, LAUNCHER_SPLIT_SELECTION_EXIT_HOME,
+                                duration));
         switch (mode) {
             case NO_BUTTON:
                 list.add(new NoButtonQuickSwitchTouchController(this));
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
index 82a9c05..e8b5081 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
@@ -54,7 +54,7 @@
 import com.android.quickstep.util.OverviewToHomeAnim;
 import com.android.quickstep.views.RecentsView;
 
-import java.util.function.Consumer;
+import java.util.function.BiConsumer;
 
 /**
  * Handles swiping up on the nav bar to go home from launcher, e.g. overview or all apps.
@@ -67,7 +67,7 @@
     private static final float OVERVIEW_TO_HOME_SCRIM_MULTIPLIER = 0.5f;
 
     private final Launcher mLauncher;
-    private final Consumer<AnimatorSet> mCancelSplitRunnable;
+    private final BiConsumer<AnimatorSet, Long> mCancelSplitRunnable;
     private final SingleAxisSwipeDetector mSwipeDetector;
     private final float mPullbackDistance;
 
@@ -81,7 +81,7 @@
      *                            Animation should be added to the provided AnimatorSet
      */
     public NavBarToHomeTouchController(Launcher launcher,
-            Consumer<AnimatorSet> cancelSplitRunnable) {
+            BiConsumer<AnimatorSet, Long> cancelSplitRunnable) {
         mLauncher = launcher;
         mCancelSplitRunnable = cancelSplitRunnable;
         mSwipeDetector = new SingleAxisSwipeDetector(mLauncher, this,
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
index fc3eeba..26e994f 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
@@ -56,7 +56,7 @@
 import com.android.quickstep.views.RecentsView;
 import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
 
-import java.util.function.Consumer;
+import java.util.function.BiConsumer;
 
 /**
  * Touch controller which handles swipe and hold from the nav bar to go to Overview. Swiping above
@@ -72,7 +72,7 @@
     private static final float TRANSLATION_ANIM_VELOCITY_DP_PER_MS = 0.8f;
 
     private final VibratorWrapper mVibratorWrapper;
-    private final Consumer<AnimatorSet> mCancelSplitRunnable;
+    private final BiConsumer<AnimatorSet, Long> mCancelSplitRunnable;
     private final RecentsView mRecentsView;
     private final MotionPauseDetector mMotionPauseDetector;
     private final float mMotionPauseMinDisplacement;
@@ -93,7 +93,7 @@
      *                            Animation should be added to the provided AnimatorSet
      */
     public NoButtonNavbarToOverviewTouchController(Launcher l,
-            Consumer<AnimatorSet> cancelSplitRunnable) {
+            BiConsumer<AnimatorSet, Long> cancelSplitRunnable) {
         super(l);
         mRecentsView = l.getOverviewPanel();
         mMotionPauseDetector = new MotionPauseDetector(l);
@@ -208,9 +208,10 @@
             // Normally we compute the duration based on the velocity and distance to the given
             // state, but since the hint state tracks the entire screen without a clear endpoint, we
             // need to manually set the duration to a reasonable value.
-            animator.setDuration(HINT_STATE.getTransitionDuration(mLauncher, true /* isToState */));
+            long duration = HINT_STATE.getTransitionDuration(mLauncher, true /* isToState */);
+            animator.setDuration(duration);
             AnimatorSet animatorSet = new AnimatorSet();
-            mCancelSplitRunnable.accept(animatorSet);
+            mCancelSplitRunnable.accept(animatorSet, duration);
             animatorSet.start();
         }
         if (FeatureFlags.ENABLE_PREMIUM_HAPTICS_ALL_APPS.get() &&
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
index ef5096b..e9f2d4f 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
@@ -21,7 +21,6 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
-import android.os.SystemClock;
 import android.os.VibrationEffect;
 import android.view.MotionEvent;
 import android.view.View;
@@ -80,6 +79,7 @@
     private float mDisplacementShift;
     private float mProgressMultiplier;
     private float mEndDisplacement;
+    private boolean mDraggingEnabled = true;
     private FlingBlockCheck mFlingBlockCheck = new FlingBlockCheck();
     private Float mOverrideVelocity = null;
 
@@ -270,6 +270,8 @@
 
     @Override
     public void onDragStart(boolean start, float startDisplacement) {
+        if (!mDraggingEnabled) return;
+
         RecentsPagedOrientationHandler orientationHandler =
                 mRecentsView.getPagedOrientationHandler();
         if (mCurrentAnimation == null) {
@@ -285,6 +287,8 @@
 
     @Override
     public boolean onDrag(float displacement) {
+        if (!mDraggingEnabled) return true;
+
         RecentsPagedOrientationHandler orientationHandler =
                 mRecentsView.getPagedOrientationHandler();
         float totalDisplacement = displacement + mDisplacementShift;
@@ -317,12 +321,9 @@
                 mOverrideVelocity = -mTaskBeingDragged.getResources().getDimension(velocityDimenId);
 
                 // Once halfway through task dismissal interpolation, switch from reversible
-                // dragging-task animation to playing the remaining task translation animations
-                final long now = SystemClock.uptimeMillis();
-                MotionEvent upAction = MotionEvent.obtain(now, now,
-                        MotionEvent.ACTION_UP, 0.0f, 0.0f, 0);
-                mDetector.onTouchEvent(upAction);
-                upAction.recycle();
+                // dragging-task animation to playing the remaining task translation animations,
+                // while this is in progress disable dragging.
+                mDraggingEnabled = false;
             }
         } else {
             mCurrentAnimation.setPlayFraction(
@@ -343,7 +344,7 @@
                 R.dimen.max_task_dismiss_drag_velocity);
         velocity = Utilities.boundToRange(velocity, -maxTaskDismissDragVelocity,
                 maxTaskDismissDragVelocity);
-        boolean fling = mDetector.isFling(velocity);
+        boolean fling = mDraggingEnabled && mDetector.isFling(velocity);
         final boolean goingToEnd;
         boolean blockedFling = fling && mFlingBlockCheck.isBlocked();
         if (blockedFling) {
@@ -371,19 +372,21 @@
                 MIN_TASK_DISMISS_ANIMATION_DURATION, MAX_TASK_DISMISS_ANIMATION_DURATION);
 
         mCurrentAnimation.setEndAction(this::clearState);
-        mCurrentAnimation.startWithVelocity(mActivity, goingToEnd,
-                velocity * orientationHandler.getSecondaryTranslationDirectionFactor(),
+        mCurrentAnimation.startWithVelocity(mActivity, goingToEnd, Math.abs(velocity),
                 mEndDisplacement, animationDuration);
         if (goingUp && goingToEnd && !mIsDismissHapticRunning) {
             VibratorWrapper.INSTANCE.get(mActivity).vibrate(TASK_DISMISS_VIBRATION_PRIMITIVE,
                     TASK_DISMISS_VIBRATION_PRIMITIVE_SCALE, TASK_DISMISS_VIBRATION_FALLBACK);
             mIsDismissHapticRunning = true;
         }
+
+        mDraggingEnabled = true;
     }
 
     private void clearState() {
         mDetector.finishedScrolling();
         mDetector.setDetectableScrollConditions(0, false);
+        mDraggingEnabled = true;
         mTaskBeingDragged = null;
         mCurrentAnimation = null;
         mIsDismissHapticRunning = false;
diff --git a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
index 8535a33..c3a4351 100644
--- a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
+++ b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
@@ -187,7 +187,10 @@
                 // Allow null-pointer to catch illegal states.
                 runOnTISBinder(tisBinder -> tisBinder.getTaskbarManager().recreateTaskbar());
                 return response;
-
+            case TestProtocol.REQUEST_TASKBAR_IME_DOCKED:
+                return getTISBinderUIProperty(Bundle::putBoolean, tisBinder ->
+                        tisBinder.getTaskbarManager()
+                                .getCurrentActivityContext().isImeDocked());
             case TestProtocol.REQUEST_UNSTASH_BUBBLE_BAR_IF_STASHED:
                 runOnTISBinder(tisBinder -> {
                     // Allow null-pointer to catch illegal states.
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index a1a3145..4e62d60 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -115,7 +115,6 @@
         ActiveGestureLog.INSTANCE.addLog(
                 /* event= */ "startRecentsAnimation",
                 /* gestureEvent= */ START_RECENTS_ANIMATION);
-        mRecentsAnimationStartPending = true;
         // Notify if recents animation is still running
         if (mController != null) {
             String msg = "New recents animation started before old animation completed";
@@ -300,10 +299,16 @@
                 options.setTransientLaunch();
             }
             options.setSourceInfo(ActivityOptions.SourceInfo.TYPE_RECENTS_ANIMATION, eventTime);
-            SystemUiProxy.INSTANCE.getNoCreate().startRecentsActivity(intent, options, mCallbacks);
+            mRecentsAnimationStartPending = SystemUiProxy.INSTANCE.getNoCreate()
+                    .startRecentsActivity(intent, options, mCallbacks);
         } else {
-            UI_HELPER_EXECUTOR.execute(() -> ActivityManagerWrapper.getInstance()
-                    .startRecentsActivity(intent, eventTime, mCallbacks, null, null));
+            UI_HELPER_EXECUTOR.execute(
+                    () -> ActivityManagerWrapper.getInstance().startRecentsActivity(
+                            intent,
+                            eventTime,
+                            mCallbacks,
+                            result -> mRecentsAnimationStartPending = result,
+                            MAIN_EXECUTOR.getHandler()));
         }
         gestureState.setState(STATE_RECENTS_ANIMATION_INITIALIZED);
         return mCallbacks;
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index 03e6c99..e30ea7a 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -82,9 +82,9 @@
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskThumbnailView;
 import com.android.quickstep.views.TaskView;
+import com.android.systemui.animation.RemoteAnimationTargetCompat;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
 import java.util.ArrayList;
 import java.util.List;
diff --git a/quickstep/src/com/android/quickstep/util/AppPairsController.java b/quickstep/src/com/android/quickstep/util/AppPairsController.java
index ce8df9b..ef6e085 100644
--- a/quickstep/src/com/android/quickstep/util/AppPairsController.java
+++ b/quickstep/src/com/android/quickstep/util/AppPairsController.java
@@ -113,7 +113,7 @@
     public void saveAppPair(GroupedTaskView gtv) {
         TaskView.TaskIdAttributeContainer[] attributes = gtv.getTaskIdAttributeContainers();
         WorkspaceItemInfo recentsInfo1 = attributes[0].getItemInfo();
-        WorkspaceItemInfo recentsInfo2 = attributes[0].getItemInfo();
+        WorkspaceItemInfo recentsInfo2 = attributes[1].getItemInfo();
         WorkspaceItemInfo app1 = lookupLaunchableItem(recentsInfo1.getComponentKey());
         WorkspaceItemInfo app2 = lookupLaunchableItem(recentsInfo2.getComponentKey());
 
diff --git a/quickstep/src/com/android/quickstep/util/OverviewToHomeAnim.java b/quickstep/src/com/android/quickstep/util/OverviewToHomeAnim.java
index 6d9ecd9..132d1c1 100644
--- a/quickstep/src/com/android/quickstep/util/OverviewToHomeAnim.java
+++ b/quickstep/src/com/android/quickstep/util/OverviewToHomeAnim.java
@@ -30,7 +30,7 @@
 import com.android.launcher3.statemanager.StateManager;
 import com.android.launcher3.states.StateAnimationConfig;
 
-import java.util.function.Consumer;
+import java.util.function.BiConsumer;
 
 /**
  * Runs an animation from overview to home. Currently, this animation is just a wrapper around the
@@ -44,14 +44,14 @@
     private final Launcher mLauncher;
     private final Runnable mOnReachedHome;
     @Nullable
-    private final Consumer<AnimatorSet> mSplitCancelConsumer;
+    private final BiConsumer<AnimatorSet, Long> mSplitCancelConsumer;
 
     // Only run mOnReachedHome when both of these are true.
     private boolean mIsHomeStaggeredAnimFinished;
     private boolean mIsOverviewHidden;
 
     public OverviewToHomeAnim(Launcher launcher, Runnable onReachedHome,
-            @Nullable Consumer<AnimatorSet> splitCancelConsumer) {
+            @Nullable BiConsumer<AnimatorSet, Long> splitCancelConsumer) {
         mLauncher = launcher;
         mOnReachedHome = onReachedHome;
         mSplitCancelConsumer = splitCancelConsumer;
@@ -103,7 +103,7 @@
 
         if (mSplitCancelConsumer != null) {
             // Clear split state when swiping to home
-            mSplitCancelConsumer.accept(anim);
+            mSplitCancelConsumer.accept(anim, config.duration);
         }
         anim.play(stateAnim);
         stateManager.setCurrentAnimation(anim, NORMAL);
diff --git a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
index 1eb71fc..b7b1d8f 100644
--- a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
+++ b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
@@ -242,7 +242,7 @@
             return
         }
 
-        val anim = createPlaceholderDismissAnim(launcher, splitDismissEvent)
+        val anim = createPlaceholderDismissAnim(launcher, splitDismissEvent, null /*duration*/)
         anim.start()
     }
 
@@ -251,8 +251,10 @@
      * for why split is being dismissed
      */
     fun createPlaceholderDismissAnim(launcher: StatefulActivity<*>,
-                                     splitDismissEvent: EventEnum) : AnimatorSet {
+                                     splitDismissEvent: EventEnum,
+                                     duration: Long?) : AnimatorSet {
         val animatorSet = AnimatorSet()
+        duration?.let { animatorSet.duration = it }
         val recentsView : RecentsView<*, *> = launcher.getOverviewPanel()
         val floatingTask: FloatingTaskView = splitSelectStateController.firstFloatingTaskView
                 ?: return animatorSet
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index f06418b..8e2520e 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -101,9 +101,9 @@
 import com.android.quickstep.views.GroupedTaskView;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.SplitInstructionsView;
+import com.android.systemui.animation.RemoteAnimationRunnerCompat;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
 import com.android.wm.shell.common.split.SplitScreenConstants.PersistentSnapPosition;
 import com.android.wm.shell.splitscreen.ISplitSelectListener;
 
diff --git a/quickstep/src/com/android/quickstep/views/DesktopTaskView.java b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
index 4c7d5c4..f2c9f27 100644
--- a/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
@@ -28,7 +28,6 @@
 import android.graphics.drawable.LayerDrawable;
 import android.graphics.drawable.ShapeDrawable;
 import android.graphics.drawable.shapes.RoundRectShape;
-import android.os.SystemProperties;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.SparseArray;
@@ -52,7 +51,7 @@
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.QuickStepContract;
-import com.android.wm.shell.Flags;
+import com.android.window.flags.Flags;
 
 import kotlin.Unit;
 
@@ -69,11 +68,6 @@
 // TODO(b/249371338): TaskView needs to be refactored to have better support for N tasks.
 public class DesktopTaskView extends TaskView {
 
-    private static final boolean DESKTOP_MODE_SUPPORTED = SystemProperties.getBoolean(
-            "persist.wm.debug.desktop_mode_2", false);
-
-    private static final boolean ENABLE_DESKTOP_WINDOWING = Flags.enableDesktopWindowing();
-
     private static final String TAG = DesktopTaskView.class.getSimpleName();
 
     private static final boolean DEBUG = false;
@@ -96,13 +90,7 @@
 
     /** Check whether desktop windowing is enabled */
     public static boolean isDesktopModeSupported() {
-        // Check for aconfig flag first
-        if (ENABLE_DESKTOP_WINDOWING) {
-            return true;
-        }
-        // Fall back to sysprop flag
-        // TODO(b/304778354): remove sysprop once desktop aconfig flag supports dynamic overriding
-        return DESKTOP_MODE_SUPPORTED;
+        return Flags.enableDesktopWindowingMode();
     }
 
     public DesktopTaskView(Context context) {
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 7bfa5ed..ea33b4d 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -493,9 +493,10 @@
     private final Rect mClearAllButtonDeadZoneRect = new Rect();
     private final Rect mTaskViewDeadZoneRect = new Rect();
     /**
-     * Reflects if Recents is currently in the middle of a gesture
+     * Reflects if Recents is currently in the middle of a gesture, and if so, which tasks are
+     * running. If a gesture is not in progress, this will be null.
      */
-    private boolean mGestureActive;
+    private @Nullable Task[] mActiveGestureRunningTasks;
 
     // Keeps track of the previously known visible tasks for purposes of loading/unloading task data
     private final SparseBooleanArray mHasVisibleTaskData = new SparseBooleanArray();
@@ -1841,7 +1842,15 @@
             if (newRunningTaskView != null) {
                 mRunningTaskViewId = newRunningTaskView.getTaskViewId();
             } else {
-                mRunningTaskViewId = INVALID_TASK_ID;
+                if (mActiveGestureRunningTasks != null) {
+                    // This will update mRunningTaskViewId and create a stub view if necessary.
+                    // We try to avoid this because it can cause a scroll jump, but it is needed
+                    // for cases where the running task isn't included in this load plan (e.g. if
+                    // the current running task is excludedFromRecents.)
+                    showCurrentTask(mActiveGestureRunningTasks);
+                } else {
+                    mRunningTaskViewId = INVALID_TASK_ID;
+                }
             }
         }
 
@@ -1909,10 +1918,17 @@
     }
 
     private void removeTasksViewsAndClearAllButton() {
+        // This handles an edge case where applyLoadPlan happens during a gesture when the
+        // only Task is one with excludeFromRecents, in which case we should not remove it.
+        final int stubRunningTaskIndex = isGestureActive() ? getRunningTaskIndex() : -1;
+
         for (int i = getTaskViewCount() - 1; i >= 0; i--) {
+            if (i == stubRunningTaskIndex) {
+                continue;
+            }
             removeView(requireTaskViewAt(i));
         }
-        if (indexOfChild(mClearAllButton) != -1) {
+        if (getTaskViewCount() == 0 && indexOfChild(mClearAllButton) != -1) {
             removeView(mClearAllButton);
         }
     }
@@ -2375,7 +2391,7 @@
                         // Ignore thumbnail update if it's current running task during the gesture
                         // We snapshot at end of gesture, it will update then
                         int changes = dataChanges;
-                        if (taskView == getRunningTaskView() && mGestureActive) {
+                        if (taskView == getRunningTaskView() && isGestureActive()) {
                             changes &= ~TaskView.FLAG_UPDATE_THUMBNAIL;
                         }
                         taskView.onTaskListVisibilityChanged(true /* visible */, changes);
@@ -2587,7 +2603,7 @@
      */
     public void onGestureAnimationStart(
             Task[] runningTasks, RotationTouchHelper rotationTouchHelper) {
-        mGestureActive = true;
+        mActiveGestureRunningTasks = runningTasks;
         // This needs to be called before the other states are set since it can create the task view
         if (mOrientationState.setGestureActive(true)) {
             setLayoutRotation(rotationTouchHelper.getCurrentActiveRotation(),
@@ -2597,13 +2613,17 @@
             updateSizeAndPadding();
         }
 
-        showCurrentTask(runningTasks);
+        showCurrentTask(mActiveGestureRunningTasks);
         setEnableFreeScroll(false);
         setEnableDrawingLiveTile(false);
         setRunningTaskHidden(true);
         setTaskIconScaledDown(true);
     }
 
+    private boolean isGestureActive() {
+        return mActiveGestureRunningTasks != null;
+    }
+
     /**
      * Called only when a swipe-up gesture from an app has completed. Only called after
      * {@link #onGestureAnimationStart} and {@link #onGestureAnimationEnd()}.
@@ -2712,7 +2732,7 @@
      * Called when a gesture from an app has finished, and the animation to the target has ended.
      */
     public void onGestureAnimationEnd() {
-        mGestureActive = false;
+        mActiveGestureRunningTasks = null;
         if (mOrientationState.setGestureActive(false)) {
             updateOrientationHandler(/* forceRecreateDragLayerControllers = */ false);
         }
diff --git a/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java b/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
index a11a913..be07e8a 100644
--- a/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
+++ b/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
@@ -21,11 +21,9 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.content.Context;
-import android.os.Bundle;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
 import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
@@ -141,32 +139,6 @@
         mLauncher.getStateManager().goToState(LauncherState.NORMAL);
     }
 
-    @Override
-    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfo(info);
-        if (!FeatureFlags.enableSplitContextually()) {
-            return;
-        }
-
-        info.addAction(new AccessibilityNodeInfo.AccessibilityAction(
-                R.string.toast_split_select_cont_desc,
-                getResources().getString(R.string.toast_split_select_cont_desc)
-        ));
-    }
-
-    @Override
-    public boolean performAccessibilityAction(int action, Bundle arguments) {
-        if (!FeatureFlags.enableSplitContextually()) {
-            return super.performAccessibilityAction(action, arguments);
-        }
-
-        if (action == R.string.toast_split_select_cont_desc) {
-            exitSplitSelection();
-            return true;
-        }
-        return super.performAccessibilityAction(action, arguments);
-    }
-
     void ensureProperRotation() {
         ((RecentsView) mLauncher.getOverviewPanel()).getPagedOrientationHandler()
                 .setSplitInstructionsParams(
diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuView.java b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
index a5e8989..2bd48d5 100644
--- a/quickstep/src/com/android/quickstep/views/TaskMenuView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
@@ -18,6 +18,8 @@
 
 import static com.android.app.animation.Interpolators.EMPHASIZED;
 import static com.android.launcher3.Flags.enableOverviewIconMenu;
+import static com.android.launcher3.testing.shared.TestProtocol.TEST_TAPL_OVERVIEW_ACTIONS_MENU_FAILURE;
+import static com.android.launcher3.testing.shared.TestProtocol.testLogD;
 import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
 import static com.android.quickstep.views.TaskThumbnailView.DIM_ALPHA;
@@ -136,6 +138,17 @@
         };
     }
 
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        if (!enableOverviewIconMenu()) {
+            int maxMenuHeight = calculateMaxHeight();
+            if (MeasureSpec.getSize(heightMeasureSpec) > maxMenuHeight) {
+                heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxMenuHeight, MeasureSpec.AT_MOST);
+            }
+        }
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+    }
+
     public void onRotationChanged() {
         if (mOpenCloseAnimator != null && mOpenCloseAnimator.isRunning()) {
             mOpenCloseAnimator.end();
@@ -359,6 +372,8 @@
         mOpenCloseAnimator.addListener(new AnimationSuccessListener() {
             @Override
             public void onAnimationStart(Animator animation) {
+                testLogD(TEST_TAPL_OVERVIEW_ACTIONS_MENU_FAILURE,
+                        "TaskMenuView.java.animateOpenOrClosed: onAnimationStart");
                 setVisibility(VISIBLE);
                 if (closing && mOnClosingStartCallback != null) {
                     mOnClosingStartCallback.run();
@@ -367,6 +382,8 @@
 
             @Override
             public void onAnimationSuccess(Animator animator) {
+                testLogD(TEST_TAPL_OVERVIEW_ACTIONS_MENU_FAILURE,
+                        "TaskMenuView.java.animateOpenOrClosed: onAnimationSuccess");
                 if (closing) {
                     closeComplete();
                 }
@@ -393,6 +410,18 @@
         return new RoundedRectRevealOutlineProvider(radius, radius, fromRect, toRect);
     }
 
+    /**
+     * Calculates max height based on how much space we have available.
+     * If not enough space then the view will scroll. The maximum menu size will sit inside the task
+     * with a margin on the top and bottom.
+     */
+    private int calculateMaxHeight() {
+        float taskBottom = mTaskView.getHeight() + mTaskView.getPersistentTranslationY();
+        float taskInsetMargin = getResources().getDimension(R.dimen.task_card_margin);
+
+        return (int) (taskBottom - taskInsetMargin - getTranslationY());
+    }
+
     private void setOnClosingStartCallback(Runnable onClosingStartCallback) {
         mOnClosingStartCallback = onClosingStartCallback;
     }
diff --git a/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java b/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java
index 9ad360f..6a48b77 100644
--- a/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java
+++ b/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java
@@ -25,8 +25,6 @@
 
 import com.android.launcher3.Launcher;
 import com.android.launcher3.tapl.LaunchedAppState;
-import com.android.launcher3.tapl.LauncherInstrumentation;
-import com.android.launcher3.tapl.LauncherInstrumentation.ContainerType;
 import com.android.launcher3.ui.AbstractLauncherUiTest;
 import com.android.quickstep.views.RecentsView;
 
@@ -55,31 +53,6 @@
         }
     }
 
-    @Override
-    protected void checkLauncherState(Launcher launcher, ContainerType expectedContainerType,
-            boolean isResumed, boolean isStarted) {
-        if (ENABLE_SHELL_TRANSITIONS || !isInLiveTileMode(launcher, expectedContainerType)) {
-            super.checkLauncherState(launcher, expectedContainerType, isResumed, isStarted);
-        } else {
-            assertTrue("[Live Tile] hasBeenResumed() == isStarted(), hasBeenResumed(): "
-                            + isResumed, isResumed != isStarted);
-        }
-    }
-
-    @Override
-    protected void checkLauncherStateInOverview(Launcher launcher,
-            ContainerType expectedContainerType, boolean isStarted, boolean isResumed) {
-        if (ENABLE_SHELL_TRANSITIONS || !isInLiveTileMode(launcher, expectedContainerType)) {
-            super.checkLauncherStateInOverview(launcher, expectedContainerType, isStarted,
-                    isResumed);
-        } else {
-            assertTrue(
-                    "[Live Tile] Launcher is not started or has been resumed in state: "
-                            + expectedContainerType,
-                    isStarted && !isResumed);
-        }
-    }
-
     protected void assertTestActivityIsRunning(int activityNumber, String message) {
         assertTrue(message, mDevice.wait(
                 Until.hasObject(By.pkg(getAppPackageName()).text("TestActivity" + activityNumber)),
@@ -94,15 +67,4 @@
                 isInLaunchedApp(launcher)));
         return launchedAppState;
     }
-
-    private boolean isInLiveTileMode(Launcher launcher,
-            LauncherInstrumentation.ContainerType expectedContainerType) {
-        if (expectedContainerType != LauncherInstrumentation.ContainerType.OVERVIEW) {
-            return false;
-        }
-
-        RecentsView recentsView = launcher.getOverviewPanel();
-        return recentsView.getSizeStrategy().isInLiveTileMode()
-                && recentsView.getRunningTaskViewId() != -1;
-    }
 }
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index c0b31b7..45a9527 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -373,7 +373,7 @@
             boolean isTransientTaskbar = mLauncher.isTransientTaskbar();
             // Expect task bar invisible when the launched app was the IME activity.
             LaunchedAppState launchedAppState = getAndAssertLaunchedApp();
-            if (!isTransientTaskbar && isHardwareKeyboard()) {
+            if (!isTransientTaskbar && isHardwareKeyboard() && !mLauncher.isImeDocked()) {
                 launchedAppState.assertTaskbarVisible();
             } else {
                 launchedAppState.assertTaskbarHidden();
@@ -503,6 +503,7 @@
 
     @Test
     @PortraitLandscape
+    @ScreenRecord // b/326839375
     public void testOverviewDeadzones() throws Exception {
         startTestAppsWithCheck();
 
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index c101762..fa0daa4 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -31,6 +31,7 @@
     <!-- Hotseat -->
     <dimen name="dynamic_grid_hotseat_bottom_tall_padding">0dp</dimen>
     <dimen name="spring_loaded_hotseat_top_margin">76dp</dimen>
+    <dimen name="spring_loaded_hotseat_top_margin_wide_portrait">64dp</dimen>
 
     <dimen name="dynamic_grid_hotseat_side_padding">0dp</dimen>
 
@@ -47,6 +48,7 @@
     <dimen name="drop_target_vertical_gap">20dp</dimen>
     <dimen name="drop_target_top_margin">32dp</dimen>
     <dimen name="drop_target_bottom_margin">16dp</dimen>
+    <dimen name="drop_target_bottom_margin_wide_portrait">32dp</dimen>
 
     <!-- App Widget resize frame -->
     <!-- Button drop target bar -->
@@ -400,6 +402,9 @@
     <dimen name="taskbar_button_margin_split">0dp</dimen>
     <dimen name="taskbar_button_margin_6_5">0dp</dimen>
 
+    <!--- Floating Ime Inset height-->
+    <dimen name="floating_ime_inset_height">0dp</dimen>
+
     <!-- Bubble bar (placeholders to compile in Launcher3 without Quickstep) -->
     <dimen name="bubblebar_hotseat_adjustment_threshold">0dp</dimen>
 
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 379cdda..ab572aa 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -46,6 +46,10 @@
     <string name="save_app_pair">Save app pair</string>
     <!-- App pair default title -->
     <string name="app_pair_default_title"><xliff:g id="app1" example="Chrome">%1$s</xliff:g> | <xliff:g id="app2" example="YouTube">%2$s</xliff:g></string>
+    <!-- Displayed when an app pair can't launch at this screen size [CHAR_LIMIT=none] -->
+    <string name="app_pair_unlaunchable_at_screen_size">This pair isn\'t supported at this screen size</string>
+    <!-- Displayed when an app pair can't launch at this screen size, but user can unfold device to restore functionality [CHAR_LIMIT=none] -->
+    <string name="app_pair_needs_unfold">Unfold your device to use this pair</string>
 
     <!-- Widgets -->
     <!-- Message to tell the user to press and hold on a widget to add it [CHAR_LIMIT=50] -->
@@ -322,6 +326,8 @@
     <string name="app_downloading_title"><xliff:g id="name" example="Messenger">%1$s</xliff:g> downloading, <xliff:g id="progress" example="30%">%2$s</xliff:g> complete</string>
     <!-- Title for an app whose download has been started. -->
     <string name="app_waiting_download_title"><xliff:g id="name" example="Messenger">%1$s</xliff:g> waiting to install</string>
+    <!-- Title for an app which is archived. -->
+    <string name="app_archived_title"><xliff:g id="name" example="Messenger">%1$s</xliff:g> is archived. Tap to download.</string>
 
 
     <!-- Title shown on the alert dialog prompting the user to update the application in market
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 259ddbd..4f071fe 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -1010,7 +1010,9 @@
     }
 
     private void setDownloadStateContentDescription(ItemInfoWithIcon info, int progressLevel) {
-        if ((info.runtimeStatusFlags & ItemInfoWithIcon.FLAG_SHOW_DOWNLOAD_PROGRESS_MASK)
+        if ((info.runtimeStatusFlags & ItemInfoWithIcon.FLAG_ARCHIVED) != 0 && progressLevel == 0) {
+            setContentDescription(getContext().getString(R.string.app_archived_title, info.title));
+        } else if ((info.runtimeStatusFlags & ItemInfoWithIcon.FLAG_SHOW_DOWNLOAD_PROGRESS_MASK)
                 != 0) {
             String percentageString = NumberFormat.getPercentInstance()
                     .format(progressLevel * 0.01);
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 4dd2fe3..4b4bdc2 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -83,6 +83,7 @@
 
     // Minimum aspect ratio beyond which an extra top padding may be applied to a bottom sheet.
     private static final float MIN_ASPECT_RATIO_FOR_EXTRA_TOP_PADDING = 1.5f;
+    private static final float MAX_ASPECT_RATIO_FOR_ALTERNATE_EDIT_STATE = 1.5f;
 
     public static final PointF DEFAULT_SCALE = new PointF(1.0f, 1.0f);
     public static final ViewScaleProvider DEFAULT_PROVIDER = itemInfo -> DEFAULT_SCALE;
@@ -503,8 +504,17 @@
         }
 
         dropTargetBarSizePx = res.getDimensionPixelSize(R.dimen.dynamic_grid_drop_target_size);
-        dropTargetBarTopMarginPx = res.getDimensionPixelSize(R.dimen.drop_target_top_margin);
-        dropTargetBarBottomMarginPx = res.getDimensionPixelSize(R.dimen.drop_target_bottom_margin);
+        // Some foldable portrait modes are too wide in terms of aspect ratio so we need to tweak
+        // the dimensions for edit state.
+        final boolean shouldApplyWidePortraitDimens = isTablet
+                && !isLandscape
+                && aspectRatio < MAX_ASPECT_RATIO_FOR_ALTERNATE_EDIT_STATE;
+        dropTargetBarTopMarginPx = shouldApplyWidePortraitDimens
+                ? 0
+                : res.getDimensionPixelSize(R.dimen.drop_target_top_margin);
+        dropTargetBarBottomMarginPx = shouldApplyWidePortraitDimens
+                ? res.getDimensionPixelSize(R.dimen.drop_target_bottom_margin_wide_portrait)
+                : res.getDimensionPixelSize(R.dimen.drop_target_bottom_margin);
         dropTargetDragPaddingPx = res.getDimensionPixelSize(R.dimen.drop_target_drag_padding);
         dropTargetTextSizePx = res.getDimensionPixelSize(R.dimen.drop_target_text_size);
         dropTargetHorizontalPaddingPx = res.getDimensionPixelSize(
@@ -595,8 +605,9 @@
             }
         }
 
-        springLoadedHotseatBarTopMarginPx = res.getDimensionPixelSize(
-                R.dimen.spring_loaded_hotseat_top_margin);
+        springLoadedHotseatBarTopMarginPx = shouldApplyWidePortraitDimens
+                ? res.getDimensionPixelSize(R.dimen.spring_loaded_hotseat_top_margin_wide_portrait)
+                : res.getDimensionPixelSize(R.dimen.spring_loaded_hotseat_top_margin);
 
         if (mIsResponsiveGrid) {
             updateHotseatSizes(mResponsiveWorkspaceCellSpec.getIconSize());
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 1ab6222..c1ebbe5 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -263,7 +263,7 @@
 import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.plugins.shared.LauncherOverlayManager;
 import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlayTouchProxy;
-import com.android.wm.shell.Flags;
+import com.android.window.flags.Flags;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -319,10 +319,6 @@
     private static final FloatProperty<Hotseat> HOTSEAT_WIDGET_SCALE =
             HOTSEAT_SCALE_PROPERTY_FACTORY.get(SCALE_INDEX_WIDGET_TRANSITION);
 
-    private static final boolean ENABLE_DESKTOP_WINDOWING = Flags.enableDesktopWindowing();
-    private static final boolean DESKTOP_MODE_SUPPORTED =
-            "1".equals(Utilities.getSystemProperty("persist.wm.debug.desktop_mode_2", "0"));
-
     private final ModelCallbacks mModelCallbacks = createModelCallbacks();
 
     private final KeyboardShortcutsDelegate mKeyboardShortcutsDelegate =
@@ -2724,8 +2720,7 @@
     }
 
     private void updateDisallowBack() {
-        // TODO(b/304778354): remove sysprop once desktop aconfig flag supports dynamic overriding
-        if (ENABLE_DESKTOP_WINDOWING || DESKTOP_MODE_SUPPORTED) {
+        if (Flags.enableDesktopWindowingMode()) {
             // Do not disable back in launcher when prototype behavior is enabled
             return;
         }
diff --git a/src/com/android/launcher3/allapps/PrivateProfileManager.java b/src/com/android/launcher3/allapps/PrivateProfileManager.java
index 6422943..1ebd49e 100644
--- a/src/com/android/launcher3/allapps/PrivateProfileManager.java
+++ b/src/com/android/launcher3/allapps/PrivateProfileManager.java
@@ -60,9 +60,10 @@
  */
 public class PrivateProfileManager extends UserProfileManager {
 
-    private static final String SAFETY_CENTER_INTENT = Intent.ACTION_SAFETY_CENTER;
-    private static final String PS_SETTINGS_FRAGMENT_KEY = ":settings:fragment_args_key";
-    private static final String PS_SETTINGS_FRAGMENT_VALUE = "AndroidPrivateSpace_personal";
+    // TODO (b/324573634): Fix the intent string.
+    public static final Intent PRIVATE_SPACE_INTENT = new
+            Intent("com.android.settings.action.PRIVATE_SPACE_SETUP_FLOW");
+
     private final ActivityAllAppsContainerView<?> mAllApps;
     private final Predicate<UserHandle> mPrivateProfileMatcher;
     private Set<String> mPreInstalledSystemPackages = new HashSet<>();
@@ -158,18 +159,23 @@
         }
     }
 
-    /** Opens the Private Space Settings Entry Point. */
+    /** Opens the Private Space Settings Page. */
     public void openPrivateSpaceSettings() {
-        Intent psSettingsIntent = new Intent(SAFETY_CENTER_INTENT);
-        psSettingsIntent.putExtra(PS_SETTINGS_FRAGMENT_KEY, PS_SETTINGS_FRAGMENT_VALUE);
-        mAllApps.getContext().startActivity(psSettingsIntent);
+        if (mPrivateSpaceSettingsAvailable) {
+            mAllApps.getContext().startActivity(PRIVATE_SPACE_INTENT);
+        }
     }
 
-    /** Whether Private Space Settings Entry Point is available on the device. */
+    /** Returns whether or not Private Space Settings Page is available. */
     public boolean isPrivateSpaceSettingsAvailable() {
         return mPrivateSpaceSettingsAvailable;
     }
 
+    /** Sets whether Private Space Settings Page is available. */
+    public boolean setPrivateSpaceSettingsAvailable(boolean value) {
+        return mPrivateSpaceSettingsAvailable = value;
+    }
+
     /** Initializes binder call based properties in non-main thread.
      * <p>
      * This can cause the Private Space container items to not load/respond correctly sometimes,
@@ -183,19 +189,14 @@
         Preconditions.assertNonUiThread();
         setPreInstalledSystemPackages();
         setAppInstallerIntent();
-        setPrivateSpaceSettingsAvailable();
+        initializePrivateSpaceSettingsState();
     }
 
-    private void setPrivateSpaceSettingsAvailable() {
-        if (mPrivateSpaceSettingsAvailable) {
-            return;
-        }
+    private void initializePrivateSpaceSettingsState() {
         Preconditions.assertNonUiThread();
-        Intent psSettingsIntent = new Intent(SAFETY_CENTER_INTENT);
-        psSettingsIntent.putExtra(PS_SETTINGS_FRAGMENT_KEY, PS_SETTINGS_FRAGMENT_VALUE);
         ResolveInfo resolveInfo = mAllApps.getContext().getPackageManager()
-                .resolveActivity(psSettingsIntent, PackageManager.MATCH_SYSTEM_ONLY);
-        mPrivateSpaceSettingsAvailable = resolveInfo != null;
+                .resolveActivity(PRIVATE_SPACE_INTENT, PackageManager.MATCH_SYSTEM_ONLY);
+        setPrivateSpaceSettingsAvailable(resolveInfo != null);
     }
 
     private void setPreInstalledSystemPackages() {
diff --git a/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewController.java b/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewController.java
index 91fcf80..6067454 100644
--- a/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewController.java
+++ b/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewController.java
@@ -17,6 +17,7 @@
 package com.android.launcher3.allapps;
 
 import static android.view.View.GONE;
+import static android.view.View.INVISIBLE;
 import static android.view.View.VISIBLE;
 
 import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
@@ -49,6 +50,7 @@
 import com.android.launcher3.allapps.UserProfileManager.UserProfileState;
 import com.android.launcher3.anim.AnimatedPropertySetter;
 import com.android.launcher3.anim.PropertySetter;
+import com.android.launcher3.views.RecyclerViewFastScroller;
 
 import java.util.List;
 
@@ -220,11 +222,15 @@
     private ValueAnimator animateCollapseAnimation(ViewGroup lockButton) {
         float from = 1;
         float to = 0;
+        RecyclerViewFastScroller scrollBar = mAllApps.getActiveRecyclerView().getScrollbar();
         ValueAnimator collapseAnim = ValueAnimator.ofFloat(from, to);
         collapseAnim.setDuration(EXPAND_COLLAPSE_DURATION);
         collapseAnim.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationStart(Animator animation) {
+                if (scrollBar != null) {
+                    scrollBar.setVisibility(INVISIBLE);
+                }
                 // scroll up
                 collapse();
                 // Animate the collapsing of the text.
@@ -233,6 +239,10 @@
             @Override
             public void onAnimationEnd(Animator animation) {
                 super.onAnimationEnd(animation);
+                if (scrollBar != null) {
+                    scrollBar.setThumbOffsetY(-1);
+                    scrollBar.setVisibility(VISIBLE);
+                }
                 mPrivateProfileManager.lockPrivateProfile();
             }
         });
diff --git a/src/com/android/launcher3/apppairs/AppPairIcon.java b/src/com/android/launcher3/apppairs/AppPairIcon.java
index 9b85a65..48d0fbd 100644
--- a/src/com/android/launcher3/apppairs/AppPairIcon.java
+++ b/src/com/android/launcher3/apppairs/AppPairIcon.java
@@ -17,6 +17,7 @@
 package com.android.launcher3.apppairs;
 
 import android.content.Context;
+import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -33,11 +34,13 @@
 import com.android.launcher3.Reorderable;
 import com.android.launcher3.dragndrop.DraggableView;
 import com.android.launcher3.model.data.FolderInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.util.MultiTranslateDelegate;
 import com.android.launcher3.views.ActivityContext;
 
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.function.Predicate;
 
 /**
  * A {@link android.widget.FrameLayout} used to represent an app pair icon on the workspace.
@@ -48,6 +51,11 @@
 public class AppPairIcon extends FrameLayout implements DraggableView, Reorderable {
     private static final String TAG = "AppPairIcon";
 
+    /**
+     * Indicates that the app pair is currently launchable on the current screen.
+     */
+    private boolean mIsLaunchableAtScreenSize = true;
+
     // A view that holds the app pair icon graphic.
     private AppPairIconGraphic mIconGraphic;
     // A view that holds the app pair's title.
@@ -86,6 +94,13 @@
         icon.setOnClickListener(activity.getItemOnClickListener());
         icon.mInfo = appPairInfo;
 
+        if (icon.mInfo.contents.size() != 2) {
+            Log.wtf(TAG, "AppPair contents not 2, size: " + icon.mInfo.contents.size());
+            return icon;
+        }
+
+        icon.checkScreenSize();
+
         // Set up icon drawable area
         icon.mIconGraphic = icon.findViewById(R.id.app_pair_icon_graphic);
         icon.mIconGraphic.init(activity.getDeviceProfile(), icon);
@@ -109,11 +124,6 @@
      * Returns a formatted accessibility title for app pairs.
      */
     public String getAccessibilityTitle(FolderInfo appPairInfo) {
-        if (appPairInfo.contents.size() != 2) {
-            Log.wtf(TAG, "AppPair contents not 2, size: " + appPairInfo.contents.size());
-            return "";
-        }
-
         CharSequence app1 = appPairInfo.contents.get(0).title;
         CharSequence app2 = appPairInfo.contents.get(1).title;
         return getContext().getString(R.string.app_pair_name_format, app1, app2);
@@ -167,4 +177,40 @@
     public View getIconDrawableArea() {
         return mIconGraphic;
     }
+
+    public boolean isLaunchableAtScreenSize() {
+        return mIsLaunchableAtScreenSize;
+    }
+
+    /**
+     * Checks if the app pair is launchable in the current device configuration.
+     *
+     * App pairs can be "disabled" in two ways:
+     * 1) One of the member WorkspaceItemInfos is disabled (i.e. the app software itself is paused
+     * by the user or can't be launched).
+     * 2) This specific instance of an app pair can't be launched due to screen size requirements.
+     *
+     * This method checks and updates #2. Both #1 and #2 are checked when app pairs are drawn
+     * {@link AppPairIconGraphic#dispatchDraw(Canvas)} or clicked on
+     * {@link com.android.launcher3.touch.ItemClickHandler#onClickAppPairIcon(View)}
+     */
+    public void checkScreenSize() {
+        DeviceProfile dp = ActivityContext.lookupContext(getContext()).getDeviceProfile();
+        // If user is on a small screen, we can't launch if either of the apps is non-resizeable
+        mIsLaunchableAtScreenSize =
+                dp.isTablet || getInfo().contents.stream().noneMatch(
+                        wii -> wii.hasStatusFlag(WorkspaceItemInfo.FLAG_NON_RESIZEABLE));
+    }
+
+    /**
+     * Called when WorkspaceItemInfos get updated, and the app pair icon may need to be redrawn.
+     */
+    public void maybeRedrawForWorkspaceUpdate(Predicate<WorkspaceItemInfo> itemCheck) {
+        // If either of the app pair icons return true on the predicate (i.e. in the list of
+        // updated apps), redraw the icon graphic (icon background and both icons).
+        if (getInfo().contents.stream().anyMatch(itemCheck)) {
+            checkScreenSize();
+            mIconGraphic.invalidate();
+        }
+    }
 }
diff --git a/src/com/android/launcher3/apppairs/AppPairIconBackground.java b/src/com/android/launcher3/apppairs/AppPairIconBackground.java
index 4e60ece..b5011f1 100644
--- a/src/com/android/launcher3/apppairs/AppPairIconBackground.java
+++ b/src/com/android/launcher3/apppairs/AppPairIconBackground.java
@@ -157,7 +157,7 @@
 
     @Override
     public void setAlpha(int i) {
-        // Required by Drawable but not used.
+        mBackgroundPaint.setAlpha(i);
     }
 
     @Override
diff --git a/src/com/android/launcher3/apppairs/AppPairIconGraphic.kt b/src/com/android/launcher3/apppairs/AppPairIconGraphic.kt
index ab5ba54..365edf8 100644
--- a/src/com/android/launcher3/apppairs/AppPairIconGraphic.kt
+++ b/src/com/android/launcher3/apppairs/AppPairIconGraphic.kt
@@ -46,6 +46,9 @@
         private const val CENTER_CHANNEL_SCALE = 1 / 30f
         private const val BIG_RADIUS_SCALE = 1 / 5f
         private const val SMALL_RADIUS_SCALE = 1 / 15f
+        // Disabled alpha is 38%, or 97/255
+        private const val DISABLED_ALPHA = 97
+        private const val ENABLED_ALPHA = 255
     }
 
     // App pair icons are slightly smaller than regular icons, so we pad the icon by this much on
@@ -133,7 +136,13 @@
 
     override fun dispatchDraw(canvas: Canvas) {
         super.dispatchDraw(canvas)
+
+        val drawAlpha =
+            if (!parentIcon.isLaunchableAtScreenSize || parentIcon.info.isDisabled) DISABLED_ALPHA
+            else ENABLED_ALPHA
+
         // Draw background
+        appPairBackground.alpha = drawAlpha
         appPairBackground.draw(canvas)
 
         // Make sure icons are loaded and fresh
@@ -147,6 +156,7 @@
         } else {
             canvas.translate(width / 2f - memberIconSize / 2f, innerPadding)
         }
+        appIcon1?.alpha = drawAlpha
         appIcon1?.draw(canvas)
         canvas.restore()
 
@@ -164,6 +174,7 @@
                 height - (innerPadding + memberIconSize)
             )
         }
+        appIcon2?.alpha = drawAlpha
         appIcon2?.draw(canvas)
         canvas.restore()
     }
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index 4d06c2e..0ba468d 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -48,6 +48,7 @@
 import com.android.launcher3.pm.PackageInstallInfo;
 import com.android.launcher3.pm.UserCache;
 import com.android.launcher3.shortcuts.ShortcutRequest;
+import com.android.launcher3.uioverrides.ApiWrapper;
 import com.android.launcher3.util.FlagOp;
 import com.android.launcher3.util.IntSet;
 import com.android.launcher3.util.ItemInfoMatcher;
@@ -284,6 +285,12 @@
                                 }
                             }
                             if (si.itemType == Favorites.ITEM_TYPE_APPLICATION) {
+                                if (activities != null && !activities.isEmpty()) {
+                                    si.status = ApiWrapper
+                                            .isNonResizeableActivity(activities.get(0))
+                                            ? si.status | WorkspaceItemInfo.FLAG_NON_RESIZEABLE
+                                            : si.status & ~WorkspaceItemInfo.FLAG_NON_RESIZEABLE;
+                                }
                                 iconCache.getTitleAndIcon(si, si.usingLowResIcon());
                                 infoUpdated = true;
                             }
diff --git a/src/com/android/launcher3/model/WorkspaceItemProcessor.kt b/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
index e38af01..59f56df 100644
--- a/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
+++ b/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
@@ -38,6 +38,7 @@
 import com.android.launcher3.model.data.WorkspaceItemInfo
 import com.android.launcher3.pm.PackageInstallInfo
 import com.android.launcher3.shortcuts.ShortcutKey
+import com.android.launcher3.uioverrides.ApiWrapper
 import com.android.launcher3.util.ComponentKey
 import com.android.launcher3.util.PackageManagerHelper
 import com.android.launcher3.util.PackageUserKey
@@ -321,6 +322,9 @@
             }
             val activityInfo = c.launcherActivityInfo
             if (activityInfo != null) {
+                if (ApiWrapper.isNonResizeableActivity(activityInfo)) {
+                    info.status = info.status or WorkspaceItemInfo.FLAG_NON_RESIZEABLE
+                }
                 info.setProgressLevel(
                     PackageManagerHelper.getLoadingProgress(activityInfo),
                     PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING
diff --git a/src/com/android/launcher3/model/data/WorkspaceItemInfo.java b/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
index 435d223..9917ad7 100644
--- a/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
+++ b/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
@@ -75,6 +75,12 @@
     public static final int FLAG_START_FOR_RESULT = 1 << 4;
 
     /**
+     * The app is flagged non-resizeable, meaning that it does not support multi-window on small
+     * screens.
+     */
+    public static final int FLAG_NON_RESIZEABLE = 1 << 5;
+
+    /**
      * The intent used to start the application.
      */
     @NonNull
diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java
index 4abefc7..111931e 100644
--- a/src/com/android/launcher3/touch/ItemClickHandler.java
+++ b/src/com/android/launcher3/touch/ItemClickHandler.java
@@ -41,6 +41,7 @@
 
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.BuildConfig;
+import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.R;
@@ -149,7 +150,15 @@
     private static void onClickAppPairIcon(View v) {
         Launcher launcher = Launcher.getLauncher(v.getContext());
         AppPairIcon appPairIcon = (AppPairIcon) v;
-        if (appPairIcon.getInfo().isDisabled()) {
+        if (!appPairIcon.isLaunchableAtScreenSize()) {
+            // Display a message for app pairs that are disabled due to screen size
+            boolean isFoldable = InvariantDeviceProfile.INSTANCE.get(launcher)
+                    .supportedProfiles.stream().anyMatch(dp -> dp.isTwoPanels);
+            Toast.makeText(launcher, isFoldable
+                            ? R.string.app_pair_needs_unfold
+                            : R.string.app_pair_unlaunchable_at_screen_size,
+                    Toast.LENGTH_SHORT).show();
+        } else if (appPairIcon.getInfo().isDisabled()) {
             WorkspaceItemInfo app1 = appPairIcon.getInfo().contents.get(0);
             WorkspaceItemInfo app2 = appPairIcon.getInfo().contents.get(1);
             // Show the user why the app pair is disabled.
diff --git a/src/com/android/launcher3/util/LauncherBindableItemsContainer.java b/src/com/android/launcher3/util/LauncherBindableItemsContainer.java
index f73940b..69786bb 100644
--- a/src/com/android/launcher3/util/LauncherBindableItemsContainer.java
+++ b/src/com/android/launcher3/util/LauncherBindableItemsContainer.java
@@ -19,6 +19,7 @@
 import android.view.View;
 
 import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.apppairs.AppPairIcon;
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.graphics.PreloadIconDrawable;
@@ -58,6 +59,8 @@
                                 : null);
             } else if (info instanceof FolderInfo && v instanceof FolderIcon) {
                 ((FolderIcon) v).updatePreviewItems(updates::contains);
+            } else if (info instanceof FolderInfo && v instanceof AppPairIcon appPairIcon) {
+                appPairIcon.maybeRedrawForWorkspaceUpdate(updates::contains);
             }
 
             // Iterate all items
@@ -86,6 +89,8 @@
                 ((PendingAppWidgetHostView) v).applyState();
             } else if (v instanceof FolderIcon && info instanceof FolderInfo) {
                 ((FolderIcon) v).updatePreviewItems(updates::contains);
+            } else if (info instanceof FolderInfo && v instanceof AppPairIcon appPairIcon) {
+                appPairIcon.maybeRedrawForWorkspaceUpdate(updates::contains);
             }
             // process all the shortcuts
             return false;
diff --git a/src/com/android/launcher3/util/SplitConfigurationOptions.java b/src/com/android/launcher3/util/SplitConfigurationOptions.java
index f4a0225..837d7bc 100644
--- a/src/com/android/launcher3/util/SplitConfigurationOptions.java
+++ b/src/com/android/launcher3/util/SplitConfigurationOptions.java
@@ -169,6 +169,15 @@
             dividerWidthPercent = visualDividerBounds.width() / totalWidth;
             dividerHeightPercent = visualDividerBounds.height() / totalHeight;
         }
+
+        @Override
+        public String toString() {
+            return "LeftTop: " + leftTopBounds + ", taskId: " + leftTopTaskId + "\n"
+                    + "RightBottom: " + rightBottomBounds + ", taskId: " + rightBottomTaskId +  "\n"
+                    + "Divider: " + visualDividerBounds + "\n"
+                    + "AppsVertical? " + appsStackedVertically + "\n"
+                    + "snapPosition: " + snapPosition;
+        }
     }
 
     public static class SplitStageInfo {
diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java
index 230a651..31f5d65 100644
--- a/src/com/android/launcher3/views/ActivityContext.java
+++ b/src/com/android/launcher3/views/ActivityContext.java
@@ -322,8 +322,8 @@
     }
 
     /**
-     * Returns if the software keyboard is hidden. Hardware keyboards do not display on screen by
-     * default.
+     * Returns if the software keyboard (including input toolbar) is hidden. Hardware
+     * keyboards do not display on screen by default.
      */
     default boolean isSoftwareKeyboardHidden() {
         if (isHardwareKeyboard()) {
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
index f10ab48..ef3ccf0 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
@@ -124,6 +124,7 @@
                 widget.applyFromCellItem(widgetItem, 1f,
                         bitmap -> holder.onPreviewLoaded(Pair.create(widgetItem, bitmap)),
                         holder.previewCache.get(widgetItem));
+                widget.requestLayout();
             }
         }
     }
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java b/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java
index b9f9ac5..efde7d8 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java
@@ -107,6 +107,15 @@
                         .authority(context.getPackageName()).build());
     }
 
+    /**
+     * Checks if an activity is flagged as non-resizeable.
+     */
+    public static boolean isNonResizeableActivity(LauncherActivityInfo lai) {
+        // Overridden in quickstep
+        return false;
+    }
+
+
     private static class NoopDrawable extends ColorDrawable {
         @Override
         public int getIntrinsicHeight() {
diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelPortrait.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelPortrait.txt
index 93ab6f2..3169f41 100644
--- a/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelPortrait.txt
+++ b/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelPortrait.txt
@@ -79,7 +79,7 @@
 	hotseatBarEndOffset: 0.0px (0.0dp)
 	hotseatQsbSpace: 0.0px (0.0dp)
 	hotseatQsbHeight: 0.0px (0.0dp)
-	springLoadedHotseatBarTopMarginPx: 171.0px (65.14286dp)
+	springLoadedHotseatBarTopMarginPx: 168.0px (64.0dp)
 	getHotseatLayoutPadding(context).top: 0.0px (0.0dp)
 	getHotseatLayoutPadding(context).bottom: 108.0px (41.142857dp)
 	getHotseatLayoutPadding(context).left: 98.0px (37.333332dp)
@@ -118,12 +118,12 @@
 	overviewPageSpacing: 0.0px (0.0dp)
 	overviewRowSpacing: 0.0px (0.0dp)
 	overviewGridSideMargin: 0.0px (0.0dp)
-	dropTargetBarTopMarginPx: 168.0px (64.0dp)
+	dropTargetBarTopMarginPx: 0.0px (0.0dp)
 	dropTargetBarSizePx: 147.0px (56.0dp)
-	dropTargetBarBottomMarginPx: 42.0px (16.0dp)
-	getCellLayoutSpringLoadShrunkTop(): 490.0px (186.66667dp)
-	getCellLayoutSpringLoadShrunkBottom(): 1770.0px (674.2857dp)
+	dropTargetBarBottomMarginPx: 84.0px (32.0dp)
+	getCellLayoutSpringLoadShrunkTop(): 364.0px (138.66667dp)
+	getCellLayoutSpringLoadShrunkBottom(): 1773.0px (675.4286dp)
 	workspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)
-	getWorkspaceSpringLoadScale(): 0.7437536px (0.2833347dp)
+	getWorkspaceSpringLoadScale(): 0.81871px (0.31188953dp)
 	getCellLayoutHeight(): 1721.0px (655.619dp)
 	getCellLayoutWidth(): 899.0px (342.4762dp)
diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelPortrait3Button.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelPortrait3Button.txt
index ec9a10e..9d3d7bc 100644
--- a/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelPortrait3Button.txt
+++ b/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelPortrait3Button.txt
@@ -79,7 +79,7 @@
 	hotseatBarEndOffset: 0.0px (0.0dp)
 	hotseatQsbSpace: 0.0px (0.0dp)
 	hotseatQsbHeight: 0.0px (0.0dp)
-	springLoadedHotseatBarTopMarginPx: 171.0px (65.14286dp)
+	springLoadedHotseatBarTopMarginPx: 168.0px (64.0dp)
 	getHotseatLayoutPadding(context).top: 0.0px (0.0dp)
 	getHotseatLayoutPadding(context).bottom: 108.0px (41.142857dp)
 	getHotseatLayoutPadding(context).left: 98.0px (37.333332dp)
@@ -118,12 +118,12 @@
 	overviewPageSpacing: 0.0px (0.0dp)
 	overviewRowSpacing: 0.0px (0.0dp)
 	overviewGridSideMargin: 0.0px (0.0dp)
-	dropTargetBarTopMarginPx: 168.0px (64.0dp)
+	dropTargetBarTopMarginPx: 0.0px (0.0dp)
 	dropTargetBarSizePx: 147.0px (56.0dp)
-	dropTargetBarBottomMarginPx: 42.0px (16.0dp)
-	getCellLayoutSpringLoadShrunkTop(): 490.0px (186.66667dp)
-	getCellLayoutSpringLoadShrunkBottom(): 1770.0px (674.2857dp)
+	dropTargetBarBottomMarginPx: 84.0px (32.0dp)
+	getCellLayoutSpringLoadShrunkTop(): 364.0px (138.66667dp)
+	getCellLayoutSpringLoadShrunkBottom(): 1773.0px (675.4286dp)
 	workspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)
-	getWorkspaceSpringLoadScale(): 0.7437536px (0.2833347dp)
+	getWorkspaceSpringLoadScale(): 0.81871px (0.31188953dp)
 	getCellLayoutHeight(): 1721.0px (655.619dp)
 	getCellLayoutWidth(): 899.0px (342.4762dp)
diff --git a/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java b/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java
index 1c274bf..bc3a136 100644
--- a/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java
+++ b/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java
@@ -101,6 +101,7 @@
     public static final String REQUEST_TASKBAR_FROM_NAV_THRESHOLD = "taskbar-from-nav-threshold";
     public static final String REQUEST_STASHED_TASKBAR_SCALE = "taskbar-stash-handle-scale";
     public static final String REQUEST_RECREATE_TASKBAR = "recreate-taskbar";
+    public static final String REQUEST_TASKBAR_IME_DOCKED = "taskbar-ime-docked";
     public static final String REQUEST_APP_LIST_FREEZE_FLAGS = "app-list-freeze-flags";
     public static final String REQUEST_APPS_LIST_SCROLL_Y = "apps-list-scroll-y";
     public static final String REQUEST_TASKBAR_APPS_LIST_SCROLL_Y = "taskbar-apps-list-scroll-y";
@@ -172,6 +173,9 @@
     public static final String OVERVIEW_OVER_HOME = "b/279059025";
     public static final String UIOBJECT_STALE_ELEMENT = "b/319501259";
     public static final String GET_FROM_RECENTS_FAILURE = "b/321775748";
+    public static final String SUCCESSFUL_GESTURE_MISMATCH_EVENTS = "b/324940434";
+    public static final String TEST_DRAG_APP_ICON_TO_MULTIPLE_WORKSPACES_FAILURE = "b/326908466";
+    public static final String TEST_TAPL_OVERVIEW_ACTIONS_MENU_FAILURE = "b/326073471";
 
     public static final String REQUEST_EMULATE_DISPLAY = "emulate-display";
     public static final String REQUEST_STOP_EMULATE_DISPLAY = "stop-emulate-display";
diff --git a/tests/multivalentTests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/multivalentTests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index bd79e68..1f824b8 100644
--- a/tests/multivalentTests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/multivalentTests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -55,14 +55,11 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.statemanager.StateManager;
 import com.android.launcher3.tapl.HomeAllApps;
 import com.android.launcher3.tapl.HomeAppIcon;
 import com.android.launcher3.tapl.LauncherInstrumentation;
-import com.android.launcher3.tapl.LauncherInstrumentation.ContainerType;
 import com.android.launcher3.tapl.TestHelpers;
 import com.android.launcher3.testcomponent.TestCommandReceiver;
-import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.util.LooperExecutor;
 import com.android.launcher3.util.SimpleBroadcastReceiver;
 import com.android.launcher3.util.TestUtil;
@@ -204,10 +201,6 @@
             mLauncher.setSystemHealthSupplier(startTime -> TestCommandReceiver.callCommand(
                             TestCommandReceiver.GET_SYSTEM_HEALTH_MESSAGE, startTime.toString())
                     .getString("result"));
-            mLauncher.setOnSettledStateAction(
-                    containerType -> executeOnLauncher(
-                            launcher ->
-                                    checkLauncherIntegrity(launcher, containerType)));
         }
         mLauncher.enableDebugTracing();
         // Avoid double-reporting of Launcher crashes.
@@ -642,86 +635,6 @@
         return launcher.getAppsView().getActiveRecyclerView().computeVerticalScrollOffset();
     }
 
-    private void checkLauncherIntegrity(
-            Launcher launcher, ContainerType expectedContainerType) {
-        if (launcher != null) {
-            final StateManager<LauncherState> stateManager = launcher.getStateManager();
-            final LauncherState stableState = stateManager.getCurrentStableState();
-
-            assertTrue("Stable state != state: " + stableState.getClass().getSimpleName() + ", "
-                            + stateManager.getState().getClass().getSimpleName(),
-                    stableState == stateManager.getState());
-
-            final boolean isResumed = launcher.hasBeenResumed();
-            final boolean isStarted = launcher.isStarted();
-            checkLauncherState(launcher, expectedContainerType, isResumed, isStarted);
-
-            final int ordinal = stableState.ordinal;
-
-            switch (expectedContainerType) {
-                case WORKSPACE:
-                case WIDGETS: {
-                    assertTrue(
-                            "Launcher is not resumed in state: " + expectedContainerType,
-                            isResumed);
-                    assertTrue(TestProtocol.stateOrdinalToString(ordinal),
-                            ordinal == TestProtocol.NORMAL_STATE_ORDINAL);
-                    break;
-                }
-                case HOME_ALL_APPS: {
-                    assertTrue(
-                            "Launcher is not resumed in state: " + expectedContainerType,
-                            isResumed);
-                    assertTrue(TestProtocol.stateOrdinalToString(ordinal),
-                            ordinal == TestProtocol.ALL_APPS_STATE_ORDINAL);
-                    break;
-                }
-                case OVERVIEW: {
-                    verifyOverviewState(launcher, expectedContainerType, isStarted, isResumed,
-                            ordinal, TestProtocol.OVERVIEW_STATE_ORDINAL);
-                    break;
-                }
-                case SPLIT_SCREEN_SELECT: {
-                    verifyOverviewState(launcher, expectedContainerType, isStarted, isResumed,
-                            ordinal, TestProtocol.OVERVIEW_SPLIT_SELECT_ORDINAL);
-                    break;
-                }
-                case TASKBAR_ALL_APPS:
-                case LAUNCHED_APP: {
-                    assertTrue("Launcher is resumed in state: " + expectedContainerType,
-                            !isResumed);
-                    assertTrue(TestProtocol.stateOrdinalToString(ordinal),
-                            ordinal == TestProtocol.NORMAL_STATE_ORDINAL);
-                    break;
-                }
-                default:
-                    throw new IllegalArgumentException(
-                            "Illegal container: " + expectedContainerType);
-            }
-        } else {
-            assertTrue(
-                    "Container type is not LAUNCHED_APP, TASKBAR_ALL_APPS "
-                            + "or FALLBACK_OVERVIEW: " + expectedContainerType,
-                    expectedContainerType == ContainerType.LAUNCHED_APP
-                            || expectedContainerType == ContainerType.TASKBAR_ALL_APPS
-                            || expectedContainerType == ContainerType.FALLBACK_OVERVIEW);
-        }
-    }
-
-    protected void checkLauncherState(Launcher launcher, ContainerType expectedContainerType,
-            boolean isResumed, boolean isStarted) {
-        assertTrue("hasBeenResumed() != isStarted(), hasBeenResumed(): " + isResumed,
-                isResumed == isStarted);
-        assertTrue("hasBeenResumed() != isUserActive(), hasBeenResumed(): " + isResumed,
-                isResumed == launcher.isUserActive());
-    }
-
-    protected void checkLauncherStateInOverview(Launcher launcher,
-            ContainerType expectedContainerType, boolean isStarted, boolean isResumed) {
-        assertTrue("Launcher is not resumed in state: " + expectedContainerType,
-                isResumed);
-    }
-
     protected void onLauncherActivityClose(Launcher launcher) {
     }
 
@@ -750,10 +663,4 @@
         }
         return homeAppIcon;
     }
-
-    private void verifyOverviewState(Launcher launcher, ContainerType expectedContainerType,
-            boolean isStarted, boolean isResumed, int ordinal, int expectedOrdinal) {
-        checkLauncherStateInOverview(launcher, expectedContainerType, isStarted, isResumed);
-        assertEquals(TestProtocol.stateOrdinalToString(ordinal), ordinal, expectedOrdinal);
-    }
 }
diff --git a/tests/multivalentTests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/multivalentTests/tapl/com/android/launcher3/tapl/AllApps.java
index 2dbc29d..9591891 100644
--- a/tests/multivalentTests/tapl/com/android/launcher3/tapl/AllApps.java
+++ b/tests/multivalentTests/tapl/com/android/launcher3/tapl/AllApps.java
@@ -261,7 +261,7 @@
             int attempts = 0;
             final Rect margins = new Rect(
                     /* left= */ 0,
-                    getTopVisibleIconBounds(allAppsContainer).bottom,
+                    mHeight / 2,
                     /* right= */ 0,
                     /* bottom= */ getAppsListRecyclerBottomPadding());
 
diff --git a/tests/multivalentTests/tapl/com/android/launcher3/tapl/AppIcon.java b/tests/multivalentTests/tapl/com/android/launcher3/tapl/AppIcon.java
index 156568b..7c6d684 100644
--- a/tests/multivalentTests/tapl/com/android/launcher3/tapl/AppIcon.java
+++ b/tests/multivalentTests/tapl/com/android/launcher3/tapl/AppIcon.java
@@ -16,6 +16,9 @@
 
 package com.android.launcher3.tapl;
 
+import static com.android.launcher3.testing.shared.TestProtocol.TEST_DRAG_APP_ICON_TO_MULTIPLE_WORKSPACES_FAILURE;
+
+import android.util.Log;
 import android.widget.TextView;
 
 import androidx.annotation.NonNull;
@@ -49,7 +52,7 @@
     /**
      * Find an app icon with the given name.
      *
-     * @param appName app icon to look for
+     * @param appName  app icon to look for
      * @param launcher (optional) - only match ui elements from Launcher's package
      */
     static BySelector getAppIconSelector(String appName, LauncherInstrumentation launcher) {
@@ -95,6 +98,8 @@
 
     @Override
     protected void waitForLongPressConfirmation() {
+        Log.d(TEST_DRAG_APP_ICON_TO_MULTIPLE_WORKSPACES_FAILURE,
+                "AppIcon.waitForLongPressConfirmation, resName: popupContainer");
         mLauncher.waitForLauncherObject("popup_container");
     }
 
diff --git a/tests/multivalentTests/tapl/com/android/launcher3/tapl/Launchable.java b/tests/multivalentTests/tapl/com/android/launcher3/tapl/Launchable.java
index ed47334..9d3bc6e 100644
--- a/tests/multivalentTests/tapl/com/android/launcher3/tapl/Launchable.java
+++ b/tests/multivalentTests/tapl/com/android/launcher3/tapl/Launchable.java
@@ -17,8 +17,10 @@
 package com.android.launcher3.tapl;
 
 import static com.android.launcher3.testing.shared.TestProtocol.SPRING_LOADED_STATE_ORDINAL;
+import static com.android.launcher3.testing.shared.TestProtocol.TEST_DRAG_APP_ICON_TO_MULTIPLE_WORKSPACES_FAILURE;
 
 import android.graphics.Point;
+import android.util.Log;
 import android.view.MotionEvent;
 
 import androidx.test.uiautomator.UiObject2;
@@ -113,11 +115,15 @@
                 iconCenter.y - getStartDragThreshold());
 
         if (runToSpringLoadedState) {
+            Log.d(TEST_DRAG_APP_ICON_TO_MULTIPLE_WORKSPACES_FAILURE,
+                    "Launchable.startDrag: actionName: long-pressing and triggering drag start"
+                            + " iconCenter: " + iconCenter + " dragStartCenter: "
+                            + dragStartCenter);
             mLauncher.runToState(() -> movePointerForStartDrag(
-                    downTime,
-                    iconCenter,
-                    dragStartCenter,
-                    expectLongClickEvents),
+                            downTime,
+                            iconCenter,
+                            dragStartCenter,
+                            expectLongClickEvents),
                     SPRING_LOADED_STATE_ORDINAL, "long-pressing and triggering drag start");
         } else {
             movePointerForStartDrag(
diff --git a/tests/multivalentTests/tapl/com/android/launcher3/tapl/LaunchedAppState.java b/tests/multivalentTests/tapl/com/android/launcher3/tapl/LaunchedAppState.java
index 200f2ff..b5414b7 100644
--- a/tests/multivalentTests/tapl/com/android/launcher3/tapl/LaunchedAppState.java
+++ b/tests/multivalentTests/tapl/com/android/launcher3/tapl/LaunchedAppState.java
@@ -24,7 +24,9 @@
 import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_SHELL_DRAG_READY;
 import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_STASHED_TASKBAR_SCALE;
 import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_TASKBAR_FROM_NAV_THRESHOLD;
+import static com.android.launcher3.testing.shared.TestProtocol.SUCCESSFUL_GESTURE_MISMATCH_EVENTS;
 import static com.android.launcher3.testing.shared.TestProtocol.TEST_INFO_RESPONSE_FIELD;
+import static com.android.launcher3.testing.shared.TestProtocol.testLogD;
 
 import android.graphics.Point;
 import android.graphics.Rect;
@@ -139,6 +141,8 @@
 
             return new Taskbar(mLauncher);
         } finally {
+            testLogD(SUCCESSFUL_GESTURE_MISMATCH_EVENTS,
+                    "swipeUpToUnstashTaskbar: completed gesture");
             mLauncher.getTestInfo(REQUEST_DISABLE_BLOCK_TIMEOUT);
         }
     }
diff --git a/tests/multivalentTests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/multivalentTests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 326802f..053b360 100644
--- a/tests/multivalentTests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/multivalentTests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -30,7 +30,10 @@
 import static com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL;
 import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_GET_SPLIT_SELECTION_ACTIVE;
 import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_NUM_ALL_APPS_COLUMNS;
+import static com.android.launcher3.testing.shared.TestProtocol.SUCCESSFUL_GESTURE_MISMATCH_EVENTS;
+import static com.android.launcher3.testing.shared.TestProtocol.TEST_DRAG_APP_ICON_TO_MULTIPLE_WORKSPACES_FAILURE;
 import static com.android.launcher3.testing.shared.TestProtocol.TEST_INFO_RESPONSE_FIELD;
+import static com.android.launcher3.testing.shared.TestProtocol.testLogD;
 
 import android.app.ActivityManager;
 import android.app.Instrumentation;
@@ -94,7 +97,6 @@
 import java.util.Optional;
 import java.util.concurrent.TimeoutException;
 import java.util.function.BooleanSupplier;
-import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Supplier;
 import java.util.regex.Pattern;
@@ -200,8 +202,6 @@
 
     private boolean mIgnoreTaskbarVisibility = false;
 
-    private Consumer<ContainerType> mOnSettledStateAction;
-
     private LogEventChecker mEventChecker;
 
     // UI anomaly checker provided by the test.
@@ -655,10 +655,6 @@
         this.mSystemHealthSupplier = supplier;
     }
 
-    public void setOnSettledStateAction(Consumer<ContainerType> onSettledStateAction) {
-        mOnSettledStateAction = onSettledStateAction;
-    }
-
     public void onTestStart() {
         mTestStartTime = System.currentTimeMillis();
     }
@@ -869,8 +865,6 @@
 
         final UiObject2 container = verifyVisibleObjects(containerType);
 
-        if (mOnSettledStateAction != null) mOnSettledStateAction.accept(containerType);
-
         return container;
     }
 
@@ -1172,7 +1166,11 @@
                 log("Hierarchy before clicking home:");
                 dumpViewHierarchy();
                 action = "clicking home button";
-
+                Log.d(TEST_DRAG_APP_ICON_TO_MULTIPLE_WORKSPACES_FAILURE,
+                        "LauncherInstrumentation.goHome: isThreeFingerTrackpadGesture: "
+                                + isThreeFingerTrackpadGesture
+                                + "getNavigationModel() == NavigationModel.ZERO_BUTTON: " + (
+                                getNavigationModel() == NavigationModel.ZERO_BUTTON));
                 runToState(
                         getHomeButton()::click,
                         NORMAL_STATE_ORDINAL,
@@ -1216,7 +1214,8 @@
             waitForNavigationUiObject("back").click();
         }
         if (launcherVisible) {
-            if (getContext().getApplicationInfo().isOnBackInvokedCallbackEnabled()) {
+            if (InstrumentationRegistry.getTargetContext().getApplicationInfo()
+                    .isOnBackInvokedCallbackEnabled()) {
                 expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ON_BACK_INVOKED);
             } else {
                 expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_KEY_BACK_DOWN);
@@ -1518,6 +1517,8 @@
 
     @NonNull
     UiObject2 waitForLauncherObject(String resName) {
+        Log.d(TEST_DRAG_APP_ICON_TO_MULTIPLE_WORKSPACES_FAILURE,
+                "LauncherInstrumentation.waitForLauncherObject");
         return waitForObjectBySelector(getLauncherObjectSelector(resName));
     }
 
@@ -1547,12 +1548,16 @@
 
     @NonNull
     List<UiObject2> waitForObjectsBySelector(BySelector selector) {
+        Log.d(TEST_DRAG_APP_ICON_TO_MULTIPLE_WORKSPACES_FAILURE,
+                "LauncherInstrumentation.waitForObjectsBySelector");
         final List<UiObject2> objects = mDevice.wait(Until.findObjects(selector), WAIT_TIME_MS);
         assertNotNull("Can't find any view in Launcher, selector: " + selector, objects);
         return objects;
     }
 
     private UiObject2 waitForObjectBySelector(BySelector selector) {
+        Log.d(TEST_DRAG_APP_ICON_TO_MULTIPLE_WORKSPACES_FAILURE,
+                "LauncherInstrumentation.waitForObjectBySelector");
         final UiObject2 object = mDevice.wait(Until.findObject(selector), WAIT_TIME_MS);
         assertNotNull("Can't find a view in Launcher, selector: " + selector, object);
         return object;
@@ -1595,6 +1600,9 @@
 
     void runToState(Runnable command, int expectedState, boolean requireEvent, String actionName) {
         if (requireEvent) {
+            Log.d(TEST_DRAG_APP_ICON_TO_MULTIPLE_WORKSPACES_FAILURE,
+                    "LauncherInstrumentation.runToState: command: " + command + " expectedState: "
+                            + expectedState + " actionName: " + actionName + "requireEvent: true");
             runToState(command, expectedState, actionName);
         } else {
             command.run();
@@ -1972,11 +1980,15 @@
                     mPointerCount = 1;
                     pointerCount = mPointerCount;
                 }
+                Log.d(TEST_DRAG_APP_ICON_TO_MULTIPLE_WORKSPACES_FAILURE,
+                        "LauncherInstrumentation.sendPointer: ACTION_DOWN");
                 break;
             case MotionEvent.ACTION_UP:
                 if (hasTIS && gestureScope == GestureScope.EXPECT_PILFER) {
                     expectEvent(TestProtocol.SEQUENCE_PILFER, EVENT_PILFER_POINTERS);
                 }
+                Log.d(TEST_DRAG_APP_ICON_TO_MULTIPLE_WORKSPACES_FAILURE,
+                        "LauncherInstrumentation.sendPointer: ACTION_UP");
                 break;
             case MotionEvent.ACTION_POINTER_DOWN:
                 mPointerCount++;
@@ -2241,6 +2253,11 @@
                 .getBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD);
     }
 
+    public boolean isImeDocked() {
+        return getTestInfo(TestProtocol.REQUEST_TASKBAR_IME_DOCKED).getBoolean(
+                TestProtocol.TEST_INFO_RESPONSE_FIELD);
+    }
+
     /** Enables transient taskbar for testing purposes only. */
     public void enableTransientTaskbar(boolean enable) {
         getTestInfo(enable
@@ -2312,9 +2329,13 @@
             }
 
             if (mEventChecker != null) {
+                testLogD(SUCCESSFUL_GESTURE_MISMATCH_EVENTS, "eventsCheck: mEventChecker exists");
                 mEventChecker = null;
                 if (mCheckEventsForSuccessfulGestures) {
                     final String message = eventChecker.verify(WAIT_TIME_MS, true);
+                    testLogD(SUCCESSFUL_GESTURE_MISMATCH_EVENTS,
+                            "mCheckEventsForSuccessfulGestures = true | eventsCheck: message="
+                                    + message);
                     if (message != null) {
                         dumpDiagnostics(message);
                         checkForAnomaly();
diff --git a/tests/multivalentTests/tapl/com/android/launcher3/tapl/LogEventChecker.java b/tests/multivalentTests/tapl/com/android/launcher3/tapl/LogEventChecker.java
index 672c6e0..70d63bd 100644
--- a/tests/multivalentTests/tapl/com/android/launcher3/tapl/LogEventChecker.java
+++ b/tests/multivalentTests/tapl/com/android/launcher3/tapl/LogEventChecker.java
@@ -18,6 +18,8 @@
 import static com.android.launcher3.testing.shared.TestProtocol.SEQUENCE_MAIN;
 import static com.android.launcher3.testing.shared.TestProtocol.SEQUENCE_PILFER;
 import static com.android.launcher3.testing.shared.TestProtocol.SEQUENCE_TIS;
+import static com.android.launcher3.testing.shared.TestProtocol.SUCCESSFUL_GESTURE_MISMATCH_EVENTS;
+import static com.android.launcher3.testing.shared.TestProtocol.testLogD;
 
 import android.os.SystemClock;
 
@@ -88,6 +90,7 @@
     }
 
     String verify(long waitForExpectedCountMs, boolean successfulGesture) {
+        testLogD(SUCCESSFUL_GESTURE_MISMATCH_EVENTS, "LogEventChecker.java - verify");
         final ListMap<String> actualEvents = finishSync(waitForExpectedCountMs);
         if (actualEvents == null) return "null event sequences because launcher likely died";
 
diff --git a/tests/multivalentTests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/multivalentTests/tapl/com/android/launcher3/tapl/Workspace.java
index 506e563..4e92634 100644
--- a/tests/multivalentTests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/multivalentTests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -25,6 +25,7 @@
 import static com.android.launcher3.testing.shared.TestProtocol.ALL_APPS_STATE_ORDINAL;
 import static com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL;
 import static com.android.launcher3.testing.shared.TestProtocol.OVERVIEW_STATE_ORDINAL;
+import static com.android.launcher3.testing.shared.TestProtocol.TEST_DRAG_APP_ICON_TO_MULTIPLE_WORKSPACES_FAILURE;
 import static com.android.launcher3.testing.shared.TestProtocol.UIOBJECT_STALE_ELEMENT;
 
 import static junit.framework.TestCase.assertNotNull;
@@ -626,6 +627,8 @@
         try (LauncherInstrumentation.Closable ignored = launcher.addContextLayer(
                 "want to drag icon to workspace")) {
             final long downTime = SystemClock.uptimeMillis();
+            Log.d(TEST_DRAG_APP_ICON_TO_MULTIPLE_WORKSPACES_FAILURE,
+                    "Workspace.dragIconToWorkspace: starting drag | downtime: " + downTime);
             Point dragStart = launchable.startDrag(
                     downTime,
                     expectLongClickEvents,
diff --git a/tests/multivalentTests/tapl/com/android/launcher3/tapl/WorkspaceDragSource.java b/tests/multivalentTests/tapl/com/android/launcher3/tapl/WorkspaceDragSource.java
index e5a2a2e..b42d43b 100644
--- a/tests/multivalentTests/tapl/com/android/launcher3/tapl/WorkspaceDragSource.java
+++ b/tests/multivalentTests/tapl/com/android/launcher3/tapl/WorkspaceDragSource.java
@@ -15,7 +15,10 @@
  */
 package com.android.launcher3.tapl;
 
+import static com.android.launcher3.testing.shared.TestProtocol.TEST_DRAG_APP_ICON_TO_MULTIPLE_WORKSPACES_FAILURE;
+
 import android.graphics.Point;
+import android.util.Log;
 
 import java.util.function.Supplier;
 
@@ -76,6 +79,9 @@
              LauncherInstrumentation.Closable c = launcher.addContextLayer(
                      String.format("want to drag the icon to cell(%d, %d)", cellX, cellY))) {
             final Supplier<Point> dest = () -> Workspace.getCellCenter(launcher, cellX, cellY);
+            Log.d(TEST_DRAG_APP_ICON_TO_MULTIPLE_WORKSPACES_FAILURE,
+                    "WorkspaceDragSource.dragToWorkspace: dragging icon to workspace | dest: "
+                            + dest.get());
             Workspace.dragIconToWorkspace(
                     launcher,
                     launchable,
diff --git a/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java b/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java
index ea7feb5..0907f8f 100644
--- a/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java
+++ b/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java
@@ -76,9 +76,6 @@
             new UserIconInfo(MAIN_HANDLE, UserIconInfo.TYPE_MAIN);
     private static final UserIconInfo PRIVATE_ICON_INFO =
             new UserIconInfo(PRIVATE_HANDLE, UserIconInfo.TYPE_PRIVATE);
-    private static final String SAFETY_CENTER_INTENT = Intent.ACTION_SAFETY_CENTER;
-    private static final String PS_SETTINGS_FRAGMENT_KEY = ":settings:fragment_args_key";
-    private static final String PS_SETTINGS_FRAGMENT_VALUE = "AndroidPrivateSpace_personal";
 
     private PrivateProfileManager mPrivateProfileManager;
     @Mock
@@ -180,9 +177,9 @@
 
     @Test
     public void openPrivateSpaceSettings_triggersSecurityAndPrivacyIntent() {
-        Intent expectedIntent = new Intent(SAFETY_CENTER_INTENT);
-        expectedIntent.putExtra(PS_SETTINGS_FRAGMENT_KEY, PS_SETTINGS_FRAGMENT_VALUE);
+        Intent expectedIntent = PrivateProfileManager.PRIVATE_SPACE_INTENT;
         ArgumentCaptor<Intent> acIntent = ArgumentCaptor.forClass(Intent.class);
+        mPrivateProfileManager.setPrivateSpaceSettingsAvailable(true);
 
         mPrivateProfileManager.openPrivateSpaceSettings();
 
@@ -190,9 +187,6 @@
         Intent actualIntent = acIntent.getValue();
         assertEquals("Intent Action is different", expectedIntent.getAction(),
                 actualIntent.getAction());
-        assertEquals("Settings Fragment is incorrect in Intent",
-                expectedIntent.getStringExtra(PS_SETTINGS_FRAGMENT_KEY),
-                actualIntent.getStringExtra(PS_SETTINGS_FRAGMENT_KEY));
     }
 
     private static void awaitTasksCompleted() throws Exception {
diff --git a/tests/src/com/android/launcher3/dragging/TaplDragTest.java b/tests/src/com/android/launcher3/dragging/TaplDragTest.java
index 615f679..d1227d8 100644
--- a/tests/src/com/android/launcher3/dragging/TaplDragTest.java
+++ b/tests/src/com/android/launcher3/dragging/TaplDragTest.java
@@ -15,6 +15,7 @@
  */
 package com.android.launcher3.dragging;
 
+import static com.android.launcher3.testing.shared.TestProtocol.TEST_DRAG_APP_ICON_TO_MULTIPLE_WORKSPACES_FAILURE;
 import static com.android.launcher3.util.TestConstants.AppNames.GMAIL_APP_NAME;
 import static com.android.launcher3.util.TestConstants.AppNames.MAPS_APP_NAME;
 import static com.android.launcher3.util.TestConstants.AppNames.PHOTOS_APP_NAME;
@@ -233,6 +234,11 @@
         final HomeAppIcon launcherTestAppIcon = createShortcutInCenterIfNotExist(TEST_APP_NAME);
         for (Point target : targets) {
             startTime = SystemClock.uptimeMillis();
+            Log.d(TEST_DRAG_APP_ICON_TO_MULTIPLE_WORKSPACES_FAILURE,
+                    "TaplDragTest.java.testDragAppIconToMultipleWorkspaceCells: shortcut name: "
+                            + launcherTestAppIcon.getIconName()
+                            + " | target cell coordinates: (" + target.x + ", " + target.y
+                            + ") | start time: " + startTime);
             launcherTestAppIcon.dragToWorkspace(target.x, target.y);
             endTime = SystemClock.uptimeMillis();
             elapsedTime = endTime - startTime;
diff --git a/tests/src/com/android/launcher3/model/FolderIconLoadTest.kt b/tests/src/com/android/launcher3/model/FolderIconLoadTest.kt
index 60a4d2d..2118ed6 100644
--- a/tests/src/com/android/launcher3/model/FolderIconLoadTest.kt
+++ b/tests/src/com/android/launcher3/model/FolderIconLoadTest.kt
@@ -24,11 +24,13 @@
 import com.android.launcher3.util.LauncherModelHelper
 import com.android.launcher3.util.LauncherModelHelper.*
 import com.android.launcher3.util.TestUtil
+import com.android.launcher3.util.rule.TestStabilityRule
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
 import java.util.concurrent.CountDownLatch
 import org.junit.After
 import org.junit.Before
+import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 
@@ -56,6 +58,8 @@
             TEST_ACTIVITY14
         )
 
+    @get:Rule(order = 0) val testStabilityRule = TestStabilityRule()
+
     @Before
     fun setUp() {
         modelHelper = LauncherModelHelper()
@@ -87,6 +91,9 @@
 
     @Test
     @Throws(Exception::class)
+    @TestStabilityRule.Stability(
+        flavors = TestStabilityRule.LOCAL or TestStabilityRule.PLATFORM_POSTSUBMIT
+    ) // b/319923578
     fun folderLoadedWithHighRes_max_3x3() {
         val idp = LauncherAppState.getIDP(modelHelper.sandboxContext)
         idp.numFolderColumns = intArrayOf(3, 3, 3, 3)
@@ -100,6 +107,9 @@
 
     @Test
     @Throws(Exception::class)
+    @TestStabilityRule.Stability(
+        flavors = TestStabilityRule.LOCAL or TestStabilityRule.PLATFORM_POSTSUBMIT
+    ) // b/319923578
     fun folderLoadedWithHighRes_max_4x4() {
         val idp = LauncherAppState.getIDP(modelHelper.sandboxContext)
         idp.numFolderColumns = intArrayOf(4, 4, 4, 4)
@@ -113,6 +123,10 @@
 
     @Test
     @Throws(Exception::class)
+    // Stress tests are long. We permanently demote them from presubmit to match the presubmit SLO.
+    @TestStabilityRule.Stability(
+        flavors = TestStabilityRule.LOCAL or TestStabilityRule.PLATFORM_POSTSUBMIT
+    ) // b/319923578
     fun folderLoadedWithHighRes_differentFolderConfigurations() {
         val idp = LauncherAppState.getIDP(modelHelper.sandboxContext)
         idp.numFolderColumns = intArrayOf(4, 3, 4, 4)
diff --git a/tests/src/com/android/launcher3/ui/TaplWorkProfileTest.java b/tests/src/com/android/launcher3/ui/TaplWorkProfileTest.java
index 802e564..a9947a0 100644
--- a/tests/src/com/android/launcher3/ui/TaplWorkProfileTest.java
+++ b/tests/src/com/android/launcher3/ui/TaplWorkProfileTest.java
@@ -133,6 +133,7 @@
     }
 
     @Test
+    @com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord // b/325383911
     public void workTabExists() {
         assumeTrue(mWorkProfileSetupSuccessful);
         waitForWorkTabSetup();