Prevent x-axis window movement during the initial swipe up to show transient taskbar

- If gesture starts and isLikelyToStartANewTask=true, we do not clamp
- If gesture starts and isLikelyToStartANewTask=false AND transient taskbar is not
  already showing, we clamp the scrolling

Bug: 258851206
Test: swipe up to show taskbar, no x-axis movement
      swipe left/right still works as expected

Change-Id: Iac194df63e03b4a28b49008983c88c165847aa31
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index cd60879..c0b6657 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -289,6 +289,8 @@
     <dimen name="transient_taskbar_key_shadow_distance">10dp</dimen>
     <dimen name="transient_taskbar_stashed_size">32dp</dimen>
     <dimen name="transient_taskbar_icon_spacing">10dp</dimen>
+    <!-- An additional touch slop to prevent x-axis movement during the swipe up to show taskbar -->
+    <dimen name="transient_taskbar_clamped_offset_bound">16dp</dimen>
     <!-- Taskbar swipe up thresholds -->
     <dimen name="taskbar_app_window_threshold">150dp</dimen>
     <dimen name="taskbar_home_overview_threshold">225dp</dimen>
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index b09e531..32997b6 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -750,6 +750,21 @@
         }
     }
 
+    /**
+     * Sets whether or not we should clamp the scroll offset.
+     * This is used to avoid x-axis movement when swiping up transient taskbar.
+     * @param clampScrollOffset When true, we clamp the scroll to 0 before the clamp threshold is
+     *                          met.
+     */
+    public void setClampScrollOffset(boolean clampScrollOffset) {
+        if (mRecentsView == null) {
+            mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT,
+                    () -> mRecentsView.setClampScrollOffset(clampScrollOffset));
+            return;
+        }
+        mRecentsView.setClampScrollOffset(clampScrollOffset);
+    }
+
     public void setIsLikelyToStartNewTask(boolean isLikelyToStartNewTask) {
         setIsLikelyToStartNewTask(
                 isLikelyToStartNewTask,
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index b3d3c3d..61383a5 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -382,6 +382,9 @@
         mInteractionHandler.onGestureStarted(isLikelyToStartNewTask);
 
         mInteractionHandler.setTaskbarAlreadyOpen(mTaskbarAlreadyOpen);
+        if (mIsTransientTaskbar && !mTaskbarAlreadyOpen && !isLikelyToStartNewTask) {
+            mInteractionHandler.setClampScrollOffset(true);
+        }
     }
 
     private void startTouchTrackingForWindowAnimation(long touchTimeMs) {
@@ -477,6 +480,9 @@
     @UiThread
     private void onInteractionGestureFinished() {
         Preconditions.assertUIThread();
+        if (mInteractionHandler != null) {
+            mInteractionHandler.setClampScrollOffset(false);
+        }
         removeListener();
         mInteractionHandler = null;
         cleanupAfterGesture();
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 80b41a7..5a26f32 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -498,6 +498,9 @@
     private boolean mOverviewFullscreenEnabled;
     private boolean mOverviewSelectEnabled;
 
+    private boolean mShouldClampScrollOffset;
+    private int mClampedScrollOffsetBound;
+
     private float mAdjacentPageHorizontalOffset = 0;
     protected float mTaskViewsSecondaryTranslation = 0;
     protected float mTaskViewsPrimarySplitTranslation = 0;
@@ -752,6 +755,8 @@
         mSplitPlaceholderInset = getResources().getDimensionPixelSize(
                 R.dimen.split_placeholder_inset);
         mSquaredTouchSlop = squaredTouchSlop(context);
+        mClampedScrollOffsetBound = getResources().getDimensionPixelSize(
+                R.dimen.transient_taskbar_clamped_offset_bound);
 
         mEmptyIcon = context.getDrawable(R.drawable.ic_empty_recents);
         mEmptyIcon.setCallback(this);
@@ -5055,9 +5060,35 @@
     }
 
     /**
+     * Sets whether or not we should clamp the scroll offset.
+     * This is used to avoid x-axis movement when swiping up transient taskbar.
+     * Should only be set at the beginning and end of the gesture, otherwise a jump may occur.
+     * @param clampScrollOffset When true, we clamp the scroll to 0 before the clamp threshold is
+     *                          met.
+     */
+    public void setClampScrollOffset(boolean clampScrollOffset) {
+        mShouldClampScrollOffset = clampScrollOffset;
+    }
+
+    /**
      * Returns how many pixels the page is offset on the currently laid out dominant axis.
      */
     public int getScrollOffset(int pageIndex) {
+        int unboundedOffset = getUnclampedScrollOffset(pageIndex);
+        if (!mShouldClampScrollOffset) {
+            return unboundedOffset;
+        }
+        if (Math.abs(unboundedOffset) < mClampedScrollOffsetBound) {
+            return 0;
+        }
+        return unboundedOffset
+                - Math.round(Math.signum(unboundedOffset) * mClampedScrollOffsetBound);
+    }
+
+    /**
+     * Returns how many pixels the page is offset on the currently laid out dominant axis.
+     */
+    private int getUnclampedScrollOffset(int pageIndex) {
         if (pageIndex == -1) {
             return 0;
         }
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 3b92ac4..50b7563 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -368,6 +368,7 @@
     <dimen name="transient_taskbar_shadow_blur">0dp</dimen>
     <dimen name="transient_taskbar_key_shadow_distance">0dp</dimen>
     <dimen name="transient_taskbar_stashed_size">0dp</dimen>
+    <dimen name="transient_taskbar_clamped_offset_bound">0dp</dimen>
     <!-- Note that this applies to both sides of all icons, so visible space is double this. -->
     <dimen name="transient_taskbar_icon_spacing">0dp</dimen>
     <!-- Note that this applies to both sides of all icons, so visible space is double this. -->