Hide Clear all button during quick switch

- Add support for mMinScrollX to PagedView
- Add RECENTS_CLEAR_ALL_BUTTON as a state-specified visible element
- In BackgroundAppState, set Clear all invisible and bound RecentsView
  scroll to the last task

Test:
- Open an app, quick switch until reaching the end, ensure Clear all
  does not show up and an overscroll effect is performed
- Enter overview, scroll to the end and ensure Clear all shows up
- Same tests in RTL and 3rd party launcher

Bug: 130160876
Change-Id: I5fb958744d0055b83ced1f8b0d7face0e06a0cc5
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index 2267412..11a1885 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -15,6 +15,8 @@
  */
 package com.android.launcher3.uioverrides;
 
+import static com.android.launcher3.LauncherState.RECENTS_CLEAR_ALL_BUTTON;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
 
 import android.animation.ValueAnimator;
@@ -22,15 +24,17 @@
 import android.os.Build;
 import android.util.FloatProperty;
 
+import androidx.annotation.NonNull;
+
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.LauncherStateManager.AnimationConfig;
 import com.android.launcher3.anim.AnimatorSetBuilder;
+import com.android.launcher3.anim.PropertySetter;
+import com.android.quickstep.views.ClearAllButton;
 import com.android.quickstep.views.LauncherRecentsView;
 import com.android.quickstep.views.RecentsView;
 
-import androidx.annotation.NonNull;
-
 /**
  * State handler for handling UI changes for {@link LauncherRecentsView}. In addition to managing
  * the basic view properties, this class also manages changes in the task visuals.
@@ -50,6 +54,7 @@
             mRecentsView.updateEmptyMessage();
             mRecentsView.resetTaskVisuals();
         }
+        setAlphas(PropertySetter.NO_ANIM_PROPERTY_SETTER, state.getVisibleElements(mLauncher));
     }
 
     @Override
@@ -71,6 +76,14 @@
             builder.play(updateAnim);
             mRecentsView.updateEmptyMessage();
         }
+
+        setAlphas(config.getPropertySetter(builder), toState.getVisibleElements(mLauncher));
+    }
+
+    private void setAlphas(PropertySetter propertySetter, int visibleElements) {
+        boolean hasClearAllButton = (visibleElements & RECENTS_CLEAR_ALL_BUTTON) != 0;
+        propertySetter.setFloat(mRecentsView.getClearAllButton(), ClearAllButton.VISIBILITY_ALPHA,
+                hasClearAllButton ? 1f : 0f, LINEAR);
     }
 
     @Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
index 462e630..140e45c 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
@@ -18,11 +18,10 @@
 import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
 
 import android.os.RemoteException;
+
 import com.android.launcher3.Launcher;
 import com.android.launcher3.allapps.AllAppsTransitionController;
 import com.android.quickstep.RecentsModel;
-import com.android.quickstep.SysUINavigationMode;
-import com.android.quickstep.SysUINavigationMode.Mode;
 import com.android.quickstep.util.LayoutUtils;
 import com.android.quickstep.views.RecentsView;
 import com.android.systemui.shared.recents.ISystemUiProxy;
@@ -75,4 +74,8 @@
         return new ScaleAndTranslation(scale, 0f, 0f);
     }
 
+    @Override
+    public int getVisibleElements(Launcher launcher) {
+        return super.getVisibleElements(launcher) & ~RECENTS_CLEAR_ALL_BUTTON;
+    }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
index 043fd55..cda03dc 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -116,9 +116,9 @@
     @Override
     public int getVisibleElements(Launcher launcher) {
         if (launcher.getDeviceProfile().isVerticalBarLayout()) {
-            return VERTICAL_SWIPE_INDICATOR;
+            return VERTICAL_SWIPE_INDICATOR | RECENTS_CLEAR_ALL_BUTTON;
         } else {
-            return HOTSEAT_SEARCH_BOX | VERTICAL_SWIPE_INDICATOR |
+            return HOTSEAT_SEARCH_BOX | VERTICAL_SWIPE_INDICATOR | RECENTS_CLEAR_ALL_BUTTON |
                     (launcher.getAppsView().getFloatingHeaderView().hasVisibleContent()
                             ? ALL_APPS_HEADER_EXTRA : HOTSEAT_ICONS);
         }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityControllerHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityControllerHelper.java
index 21e98f2..f12efc8 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityControllerHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityControllerHelper.java
@@ -26,11 +26,13 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
 import com.android.quickstep.util.LayoutUtils;
 import com.android.quickstep.util.RemoteAnimationTargetSet;
 import com.android.quickstep.views.RecentsView;
@@ -39,9 +41,6 @@
 import java.util.function.BiPredicate;
 import java.util.function.Consumer;
 
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
 /**
  * {@link ActivityControlHelper} for recents when the default launcher is different than the
  * currently running one and apps should interact with the {@link RecentsActivity} as opposed
@@ -72,7 +71,9 @@
 
     @Override
     public void onSwipeUpComplete(RecentsActivity activity) {
-        // TODO:
+        RecentsView recentsView = activity.getOverviewPanel();
+        recentsView.getClearAllButton().setVisibilityAlpha(1);
+        recentsView.setDisallowScrollToClearAll(false);
     }
 
     @Override
@@ -121,6 +122,8 @@
 
         RecentsView rv = activity.getOverviewPanel();
         rv.setContentAlpha(0);
+        rv.getClearAllButton().setVisibilityAlpha(0);
+        rv.setDisallowScrollToClearAll(true);
 
         return new AnimationFactory() {
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
index 365e171..b4bd414 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -744,14 +744,10 @@
         float endShift;
         final float startShift;
         Interpolator interpolator = DEACCEL;
-        int nextPage = 0;
-        int taskToLaunch = 0;
         final boolean goingToNewTask;
         if (mRecentsView != null) {
-            nextPage = mRecentsView.getNextPage();
-            final int lastTaskIndex = mRecentsView.getTaskViewCount() - 1;
             final int runningTaskIndex = mRecentsView.getRunningTaskIndex();
-            taskToLaunch = nextPage <= lastTaskIndex ? nextPage : lastTaskIndex;
+            final int taskToLaunch = mRecentsView.getNextPage();
             goingToNewTask = runningTaskIndex >= 0 && taskToLaunch != runningTaskIndex;
         } else {
             goingToNewTask = false;
@@ -817,11 +813,6 @@
             }
         }
 
-        if (mRecentsView != null && !endTarget.isLauncher && taskToLaunch != nextPage) {
-            // Scrolled to Clear all button, snap back to last task and launch it.
-            mRecentsView.snapToPage(taskToLaunch, Math.toIntExact(duration), interpolator);
-        }
-
         if (endTarget == HOME) {
             setShelfState(ShelfAnimState.CANCEL, LINEAR, 0);
             duration = Math.max(MIN_OVERSHOOT_DURATION, duration);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/ClearAllButton.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/ClearAllButton.java
index fbecd84..9db0c09 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/ClearAllButton.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/ClearAllButton.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.util.AttributeSet;
+import android.util.Property;
 import android.widget.Button;
 
 import com.android.launcher3.Utilities;
@@ -26,8 +27,22 @@
 
 public class ClearAllButton extends Button implements PageCallbacks {
 
+    public static final Property<ClearAllButton, Float> VISIBILITY_ALPHA =
+            new Property<ClearAllButton, Float>(Float.class, "visibilityAlpha") {
+                @Override
+                public Float get(ClearAllButton view) {
+                    return view.mVisibilityAlpha;
+                }
+
+                @Override
+                public void set(ClearAllButton view, Float visibilityAlpha) {
+                    view.setVisibilityAlpha(visibilityAlpha);
+                }
+            };
+
     private float mScrollAlpha = 1;
     private float mContentAlpha = 1;
+    private float mVisibilityAlpha = 1;
 
     private final boolean mIsRtl;
 
@@ -58,6 +73,13 @@
         }
     }
 
+    public void setVisibilityAlpha(float alpha) {
+        if (mVisibilityAlpha != alpha) {
+            mVisibilityAlpha = alpha;
+            updateAlpha();
+        }
+    }
+
     @Override
     public void onPageScroll(ScrollState scrollState) {
         float width = getWidth();
@@ -72,7 +94,7 @@
     }
 
     private void updateAlpha() {
-        final float alpha = mScrollAlpha * mContentAlpha;
+        final float alpha = mScrollAlpha * mContentAlpha * mVisibilityAlpha;
         setAlpha(alpha);
         setClickable(alpha == 1);
     }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
index c31e829..d6f2235 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -19,6 +19,7 @@
 import static com.android.launcher3.LauncherState.ALL_APPS_HEADER_EXTRA;
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.LauncherState.RECENTS_CLEAR_ALL_BUTTON;
 import static com.android.launcher3.QuickstepAppTransitionManagerImpl.ALL_APPS_PROGRESS_OFF_SCREEN;
 import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
@@ -204,4 +205,15 @@
         // the home predictions should have been updated when the activity was resumed.
         PredictionUiStateManager.INSTANCE.get(getContext()).switchClient(Client.HOME);
     }
+
+    @Override
+    public void setOverviewStateEnabled(boolean enabled) {
+        super.setOverviewStateEnabled(enabled);
+        if (enabled) {
+            LauncherState state = mActivity.getStateManager().getState();
+            boolean hasClearAllButton = (state.getVisibleElements(mActivity)
+                    & RECENTS_CLEAR_ALL_BUTTON) != 0;
+            setDisallowScrollToClearAll(!hasClearAllButton);
+        }
+    }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
index 72d60da..ce8b9cd 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
@@ -18,6 +18,7 @@
 
 import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS;
 import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_ICON_PARAMS;
+import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
 import static com.android.launcher3.anim.Interpolators.ACCEL;
 import static com.android.launcher3.anim.Interpolators.ACCEL_2;
 import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
@@ -27,7 +28,6 @@
 import static com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController.SUCCESS_TRANSITION_PROGRESS;
 import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW;
 import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
-import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
 import static com.android.quickstep.util.ClipAnimationHelper.TransformParams;
 
 import android.animation.Animator;
@@ -82,8 +82,6 @@
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.PropertyListBuilder;
 import com.android.launcher3.anim.SpringObjectAnimator;
-import com.android.launcher3.appprediction.PredictionUiStateManager;
-import com.android.launcher3.appprediction.PredictionUiStateManager.Client;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
@@ -92,7 +90,6 @@
 import com.android.launcher3.util.PendingAnimation;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.ViewPool;
-import com.android.quickstep.OverviewCallbacks;
 import com.android.quickstep.RecentsAnimationWrapper;
 import com.android.quickstep.RecentsModel;
 import com.android.quickstep.RecentsModel.TaskThumbnailChangeListener;
@@ -172,6 +169,7 @@
     private final ViewPool<TaskView> mTaskViewPool;
 
     private boolean mDwbToastShown;
+    private boolean mDisallowScrollToClearAll;
 
     /**
      * TODO: Call reloadIdNeeded in onTaskStackChanged.
@@ -1609,4 +1607,33 @@
 
         mRecentsAnimationWrapper.finish(toRecents, onFinishComplete);
     }
+
+    public void setDisallowScrollToClearAll(boolean disallowScrollToClearAll) {
+        if (mDisallowScrollToClearAll != disallowScrollToClearAll) {
+            mDisallowScrollToClearAll = disallowScrollToClearAll;
+            updateMinAndMaxScrollX();
+        }
+    }
+
+    @Override
+    protected int computeMinScrollX() {
+        if (mIsRtl && mDisallowScrollToClearAll) {
+            // We aren't showing the clear all button, so use the leftmost task as the min scroll.
+            return getScrollForPage(getTaskViewCount() - 1);
+        }
+        return super.computeMinScrollX();
+    }
+
+    @Override
+    protected int computeMaxScrollX() {
+        if (!mIsRtl && mDisallowScrollToClearAll) {
+            // We aren't showing the clear all button, so use the rightmost task as the max scroll.
+            return getScrollForPage(getTaskViewCount() - 1);
+        }
+        return super.computeMaxScrollX();
+    }
+
+    public ClearAllButton getClearAllButton() {
+        return mClearAllButton;
+    }
 }
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 124574c..51079b0 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -18,6 +18,7 @@
 import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
 import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS;
 import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
+
 import static com.android.launcher3.TestProtocol.ALL_APPS_STATE_ORDINAL;
 import static com.android.launcher3.TestProtocol.BACKGROUND_APP_STATE_ORDINAL;
 import static com.android.launcher3.TestProtocol.NORMAL_STATE_ORDINAL;
@@ -30,7 +31,6 @@
 
 import android.view.animation.Interpolator;
 
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.states.SpringLoadedState;
 import com.android.launcher3.uioverrides.UiFactory;
 import com.android.launcher3.uioverrides.states.AllAppsState;
@@ -58,6 +58,7 @@
     public static final int ALL_APPS_HEADER_EXTRA = 1 << 3; // e.g. app predictions
     public static final int ALL_APPS_CONTENT = 1 << 4;
     public static final int VERTICAL_SWIPE_INDICATOR = 1 << 5;
+    public static final int RECENTS_CLEAR_ALL_BUTTON = 1 << 6;
 
     protected static final int FLAG_MULTI_PAGE = 1 << 0;
     protected static final int FLAG_DISABLE_ACCESSIBILITY = 1 << 1;
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index ed77786..abb45e5 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -98,6 +98,7 @@
 
     @ViewDebug.ExportedProperty(category = "launcher")
     protected int mNextPage = INVALID_PAGE;
+    protected int mMinScrollX;
     protected int mMaxScrollX;
     protected OverScroller mScroller;
     private Interpolator mDefaultInterpolator;
@@ -266,11 +267,40 @@
     }
 
     private int validateNewPage(int newPage) {
+        newPage = ensureWithinScrollBounds(newPage);
         // Ensure that it is clamped by the actual set of children in all cases
         return Utilities.boundToRange(newPage, 0, getPageCount() - 1);
     }
 
     /**
+     * @return The closest page to the provided page that is within mMinScrollX and mMaxScrollX.
+     */
+    private int ensureWithinScrollBounds(int page) {
+        int dir = !mIsRtl ? 1 : - 1;
+        int currScroll = getScrollForPage(page);
+        int prevScroll;
+        while (currScroll < mMinScrollX) {
+            page += dir;
+            prevScroll = currScroll;
+            currScroll = getScrollForPage(page);
+            if (currScroll <= prevScroll) {
+                Log.e(TAG, "validateNewPage: failed to find a page > mMinScrollX");
+                break;
+            }
+        }
+        while (currScroll > mMaxScrollX) {
+            page -= dir;
+            prevScroll = currScroll;
+            currScroll = getScrollForPage(page);
+            if (currScroll >= prevScroll) {
+                Log.e(TAG, "validateNewPage: failed to find a page < mMaxScrollX");
+                break;
+            }
+        }
+        return page;
+    }
+
+    /**
      * Sets the current page.
      */
     public void setCurrentPage(int currentPage) {
@@ -348,29 +378,29 @@
     public void scrollTo(int x, int y) {
         mUnboundedScrollX = x;
 
-        boolean isXBeforeFirstPage = mIsRtl ? (x > mMaxScrollX) : (x < 0);
-        boolean isXAfterLastPage = mIsRtl ? (x < 0) : (x > mMaxScrollX);
+        boolean isXBeforeFirstPage = mIsRtl ? (x > mMaxScrollX) : (x < mMinScrollX);
+        boolean isXAfterLastPage = mIsRtl ? (x < mMinScrollX) : (x > mMaxScrollX);
 
         if (!isXBeforeFirstPage && !isXAfterLastPage) {
             mSpringOverScrollX = 0;
         }
 
         if (isXBeforeFirstPage) {
-            super.scrollTo(mIsRtl ? mMaxScrollX : 0, y);
+            super.scrollTo(mIsRtl ? mMaxScrollX : mMinScrollX, y);
             if (mAllowOverScroll) {
                 mWasInOverscroll = true;
                 if (mIsRtl) {
                     overScroll(x - mMaxScrollX);
                 } else {
-                    overScroll(x);
+                    overScroll(x - mMinScrollX);
                 }
             }
         } else if (isXAfterLastPage) {
-            super.scrollTo(mIsRtl ? 0 : mMaxScrollX, y);
+            super.scrollTo(mIsRtl ? mMinScrollX : mMaxScrollX, y);
             if (mAllowOverScroll) {
                 mWasInOverscroll = true;
                 if (mIsRtl) {
-                    overScroll(x);
+                    overScroll(x - mMinScrollX);
                 } else {
                     overScroll(x - mMaxScrollX);
                 }
@@ -557,12 +587,12 @@
                     // Wait until all transitions are complete.
                     if (!transition.isRunning()) {
                         transition.removeTransitionListener(this);
-                        updateMaxScrollX();
+                        updateMinAndMaxScrollX();
                     }
                 }
             });
         } else {
-            updateMaxScrollX();
+            updateMinAndMaxScrollX();
         }
 
         if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < childCount) {
@@ -627,12 +657,13 @@
         return 0;
     }
 
-    private void updateMaxScrollX() {
+    protected void updateMinAndMaxScrollX() {
+        mMinScrollX = computeMinScrollX();
         mMaxScrollX = computeMaxScrollX();
     }
 
-    public int getMaxScrollX() {
-        return mMaxScrollX;
+    protected int computeMinScrollX() {
+        return 0;
     }
 
     protected int computeMaxScrollX() {
@@ -1010,12 +1041,8 @@
             return;
         }
 
-        if (overScrollAmount < 0) {
-            super.scrollTo(overScrollAmount, getScrollY());
-        } else {
-            int x = Math.max(0, Math.min(getScrollX(), mMaxScrollX));
-            super.scrollTo(x + overScrollAmount, getScrollY());
-        }
+        int x = Utilities.boundToRange(getScrollX(), mMinScrollX, mMaxScrollX);
+        super.scrollTo(x + overScrollAmount, getScrollY());
         invalidate();
     }
 
@@ -1030,7 +1057,7 @@
 
         if (mFreeScroll && !mScroller.isFinished()) {
             if (amount < 0) {
-                super.scrollTo(amount, getScrollY());
+                super.scrollTo(mMinScrollX + amount, getScrollY());
             } else {
                 super.scrollTo(mMaxScrollX + amount, getScrollY());
             }
@@ -1169,12 +1196,12 @@
                     int initialScrollX = getScrollX();
 
                     if (((initialScrollX >= mMaxScrollX) && (isVelocityXLeft || !isFling)) ||
-                            ((initialScrollX <= 0) && (!isVelocityXLeft || !isFling))) {
-                        mScroller.springBack(getScrollX(), 0, mMaxScrollX);
+                            ((initialScrollX <= mMinScrollX) && (!isVelocityXLeft || !isFling))) {
+                        mScroller.springBack(getScrollX(), mMinScrollX, mMaxScrollX);
                     } else {
                         mScroller.setInterpolator(mDefaultInterpolator);
                         mScroller.fling(initialScrollX, -velocityX,
-                                0, mMaxScrollX,
+                                mMinScrollX, mMaxScrollX,
                                 Math.round(getWidth() * 0.5f * OVERSCROLL_DAMP_FACTOR));
 
                         int finalX = mScroller.getFinalPos();
@@ -1182,12 +1209,13 @@
 
                         int firstPageScroll = getScrollForPage(!mIsRtl ? 0 : getPageCount() - 1);
                         int lastPageScroll = getScrollForPage(!mIsRtl ? getPageCount() - 1 : 0);
-                        if (finalX > 0 && finalX < mMaxScrollX) {
+                        if (finalX > mMinScrollX && finalX < mMaxScrollX) {
                             // If scrolling ends in the half of the added space that is closer to
                             // the end, settle to the end. Otherwise snap to the nearest page.
                             // If flinging past one of the ends, don't change the velocity as it
                             // will get stopped at the end anyway.
-                            int pageSnappedX = finalX < firstPageScroll / 2 ? 0
+                            int pageSnappedX = finalX < (firstPageScroll + mMinScrollX) / 2
+                                    ? mMinScrollX
                                     : finalX > (lastPageScroll + mMaxScrollX) / 2
                                             ? mMaxScrollX
                                             : getScrollForPage(mNextPage);
@@ -1347,7 +1375,7 @@
     }
 
     protected boolean isInOverScroll() {
-        return (getScrollX() > mMaxScrollX || getScrollX() < 0);
+        return (getScrollX() > mMaxScrollX || getScrollX() < mMinScrollX);
     }
 
     protected int getPageSnapDuration() {
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index efb50f0..8849768 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -956,7 +956,8 @@
 
     private boolean isScrollingOverlay() {
         return mLauncherOverlay != null &&
-                ((mIsRtl && getUnboundedScrollX() > mMaxScrollX) || (!mIsRtl && getUnboundedScrollX() < 0));
+                ((mIsRtl && getUnboundedScrollX() > mMaxScrollX)
+                        || (!mIsRtl && getUnboundedScrollX() < mMinScrollX));
     }
 
     @Override