Merge "Handle DOWN/MOVE/UP gestures with fling. Bug: 5265814"
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 05d4f05..d7fb7a0 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -2771,15 +2771,21 @@
         }
     }
 
-    private boolean startScrollIfNeeded(int deltaY) {
+    private boolean startScrollIfNeeded(int y) {
         // Check if we have moved far enough that it looks more like a
         // scroll than a tap
+        final int deltaY = y - mMotionY;
         final int distance = Math.abs(deltaY);
         final boolean overscroll = mScrollY != 0;
         if (overscroll || distance > mTouchSlop) {
             createScrollingCache();
-            mTouchMode = overscroll ? TOUCH_MODE_OVERSCROLL : TOUCH_MODE_SCROLL;
-            mMotionCorrection = deltaY;
+            if (overscroll) {
+                mTouchMode = TOUCH_MODE_OVERSCROLL;
+                mMotionCorrection = 0;
+            } else {
+                mTouchMode = TOUCH_MODE_SCROLL;
+                mMotionCorrection = deltaY > 0 ? mTouchSlop : -mTouchSlop;
+            }
             final Handler handler = getHandler();
             // Handler should not be null unless the AbsListView is not attached to a
             // window, which would make it very hard to scroll it... but the monkeys
@@ -2799,12 +2805,176 @@
             if (parent != null) {
                 parent.requestDisallowInterceptTouchEvent(true);
             }
+            scrollIfNeeded(y);
             return true;
         }
 
         return false;
     }
 
+    private void scrollIfNeeded(int y) {
+        final int rawDeltaY = y - mMotionY;
+        final int deltaY = rawDeltaY - mMotionCorrection;
+        int incrementalDeltaY = mLastY != Integer.MIN_VALUE ? y - mLastY : deltaY;
+
+        if (mTouchMode == TOUCH_MODE_SCROLL) {
+            if (PROFILE_SCROLLING) {
+                if (!mScrollProfilingStarted) {
+                    Debug.startMethodTracing("AbsListViewScroll");
+                    mScrollProfilingStarted = true;
+                }
+            }
+
+            if (mScrollStrictSpan == null) {
+                // If it's non-null, we're already in a scroll.
+                mScrollStrictSpan = StrictMode.enterCriticalSpan("AbsListView-scroll");
+            }
+
+            if (y != mLastY) {
+                // We may be here after stopping a fling and continuing to scroll.
+                // If so, we haven't disallowed intercepting touch events yet.
+                // Make sure that we do so in case we're in a parent that can intercept.
+                if ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) == 0 &&
+                        Math.abs(rawDeltaY) > mTouchSlop) {
+                    final ViewParent parent = getParent();
+                    if (parent != null) {
+                        parent.requestDisallowInterceptTouchEvent(true);
+                    }
+                }
+
+                final int motionIndex;
+                if (mMotionPosition >= 0) {
+                    motionIndex = mMotionPosition - mFirstPosition;
+                } else {
+                    // If we don't have a motion position that we can reliably track,
+                    // pick something in the middle to make a best guess at things below.
+                    motionIndex = getChildCount() / 2;
+                }
+
+                int motionViewPrevTop = 0;
+                View motionView = this.getChildAt(motionIndex);
+                if (motionView != null) {
+                    motionViewPrevTop = motionView.getTop();
+                }
+
+                // No need to do all this work if we're not going to move anyway
+                boolean atEdge = false;
+                if (incrementalDeltaY != 0) {
+                    atEdge = trackMotionScroll(deltaY, incrementalDeltaY);
+                }
+
+                // Check to see if we have bumped into the scroll limit
+                motionView = this.getChildAt(motionIndex);
+                if (motionView != null) {
+                    // Check if the top of the motion view is where it is
+                    // supposed to be
+                    final int motionViewRealTop = motionView.getTop();
+                    if (atEdge) {
+                        // Apply overscroll
+
+                        int overscroll = -incrementalDeltaY -
+                                (motionViewRealTop - motionViewPrevTop);
+                        overScrollBy(0, overscroll, 0, mScrollY, 0, 0,
+                                0, mOverscrollDistance, true);
+                        if (Math.abs(mOverscrollDistance) == Math.abs(mScrollY)) {
+                            // Don't allow overfling if we're at the edge.
+                            if (mVelocityTracker != null) {
+                                mVelocityTracker.clear();
+                            }
+                        }
+
+                        final int overscrollMode = getOverScrollMode();
+                        if (overscrollMode == OVER_SCROLL_ALWAYS ||
+                                (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS &&
+                                        !contentFits())) {
+                            mDirection = 0; // Reset when entering overscroll.
+                            mTouchMode = TOUCH_MODE_OVERSCROLL;
+                            if (rawDeltaY > 0) {
+                                mEdgeGlowTop.onPull((float) overscroll / getHeight());
+                                if (!mEdgeGlowBottom.isFinished()) {
+                                    mEdgeGlowBottom.onRelease();
+                                }
+                            } else if (rawDeltaY < 0) {
+                                mEdgeGlowBottom.onPull((float) overscroll / getHeight());
+                                if (!mEdgeGlowTop.isFinished()) {
+                                    mEdgeGlowTop.onRelease();
+                                }
+                            }
+                        }
+                    }
+                    mMotionY = y;
+                    invalidate();
+                }
+                mLastY = y;
+            }
+        } else if (mTouchMode == TOUCH_MODE_OVERSCROLL) {
+            if (y != mLastY) {
+                final int oldScroll = mScrollY;
+                final int newScroll = oldScroll - incrementalDeltaY;
+                int newDirection = y > mLastY ? 1 : -1;
+
+                if (mDirection == 0) {
+                    mDirection = newDirection;
+                }
+
+                int overScrollDistance = -incrementalDeltaY;
+                if ((newScroll < 0 && oldScroll >= 0) || (newScroll > 0 && oldScroll <= 0)) {
+                    overScrollDistance = -oldScroll;
+                    incrementalDeltaY += overScrollDistance;
+                } else {
+                    incrementalDeltaY = 0;
+                }
+
+                if (overScrollDistance != 0) {
+                    overScrollBy(0, overScrollDistance, 0, mScrollY, 0, 0,
+                            0, mOverscrollDistance, true);
+                    final int overscrollMode = getOverScrollMode();
+                    if (overscrollMode == OVER_SCROLL_ALWAYS ||
+                            (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS &&
+                                    !contentFits())) {
+                        if (rawDeltaY > 0) {
+                            mEdgeGlowTop.onPull((float) overScrollDistance / getHeight());
+                            if (!mEdgeGlowBottom.isFinished()) {
+                                mEdgeGlowBottom.onRelease();
+                            }
+                        } else if (rawDeltaY < 0) {
+                            mEdgeGlowBottom.onPull((float) overScrollDistance / getHeight());
+                            if (!mEdgeGlowTop.isFinished()) {
+                                mEdgeGlowTop.onRelease();
+                            }
+                        }
+                        invalidate();
+                    }
+                }
+
+                if (incrementalDeltaY != 0) {
+                    // Coming back to 'real' list scrolling
+                    mScrollY = 0;
+                    invalidateParentIfNeeded();
+
+                    // No need to do all this work if we're not going to move anyway
+                    if (incrementalDeltaY != 0) {
+                        trackMotionScroll(incrementalDeltaY, incrementalDeltaY);
+                    }
+
+                    mTouchMode = TOUCH_MODE_SCROLL;
+
+                    // We did not scroll the full amount. Treat this essentially like the
+                    // start of a new touch scroll
+                    final int motionPosition = findClosestMotionRow(y);
+
+                    mMotionCorrection = 0;
+                    View motionView = getChildAt(motionPosition - mFirstPosition);
+                    mMotionViewOriginalTop = motionView != null ? motionView.getTop() : 0;
+                    mMotionY = y;
+                    mMotionPosition = motionPosition;
+                }
+                mLastY = y;
+                mDirection = newDirection;
+            }
+        }
+    }
+
     public void onTouchModeChanged(boolean isInTouchMode) {
         if (isInTouchMode) {
             // Get rid of the selection when we enter touch mode
@@ -2856,7 +3026,6 @@
         final int action = ev.getAction();
 
         View v;
-        int deltaY;
 
         initVelocityTrackerIfNotExists();
         mVelocityTracker.addMovement(ev);
@@ -2935,183 +3104,19 @@
                 mActivePointerId = ev.getPointerId(pointerIndex);
             }
             final int y = (int) ev.getY(pointerIndex);
-            deltaY = y - mMotionY;
             switch (mTouchMode) {
             case TOUCH_MODE_DOWN:
             case TOUCH_MODE_TAP:
             case TOUCH_MODE_DONE_WAITING:
                 // Check if we have moved far enough that it looks more like a
                 // scroll than a tap
-                startScrollIfNeeded(deltaY);
+                startScrollIfNeeded(y);
                 break;
             case TOUCH_MODE_SCROLL:
-                if (PROFILE_SCROLLING) {
-                    if (!mScrollProfilingStarted) {
-                        Debug.startMethodTracing("AbsListViewScroll");
-                        mScrollProfilingStarted = true;
-                    }
-                }
-
-                if (mScrollStrictSpan == null) {
-                    // If it's non-null, we're already in a scroll.
-                    mScrollStrictSpan = StrictMode.enterCriticalSpan("AbsListView-scroll");
-                }
-
-                if (y != mLastY) {
-                    // We may be here after stopping a fling and continuing to scroll.
-                    // If so, we haven't disallowed intercepting touch events yet.
-                    // Make sure that we do so in case we're in a parent that can intercept.
-                    if ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) == 0 &&
-                            Math.abs(deltaY) > mTouchSlop) {
-                        final ViewParent parent = getParent();
-                        if (parent != null) {
-                            parent.requestDisallowInterceptTouchEvent(true);
-                        }
-                    }
-
-                    final int rawDeltaY = deltaY;
-                    deltaY -= mMotionCorrection;
-                    int incrementalDeltaY = mLastY != Integer.MIN_VALUE ? y - mLastY : deltaY;
-
-                    final int motionIndex;
-                    if (mMotionPosition >= 0) {
-                        motionIndex = mMotionPosition - mFirstPosition;
-                    } else {
-                        // If we don't have a motion position that we can reliably track,
-                        // pick something in the middle to make a best guess at things below.
-                        motionIndex = getChildCount() / 2;
-                    }
-
-                    int motionViewPrevTop = 0;
-                    View motionView = this.getChildAt(motionIndex);
-                    if (motionView != null) {
-                        motionViewPrevTop = motionView.getTop();
-                    }
-
-                    // No need to do all this work if we're not going to move anyway
-                    boolean atEdge = false;
-                    if (incrementalDeltaY != 0) {
-                        atEdge = trackMotionScroll(deltaY, incrementalDeltaY);
-                    }
-
-                    // Check to see if we have bumped into the scroll limit
-                    motionView = this.getChildAt(motionIndex);
-                    if (motionView != null) {
-                        // Check if the top of the motion view is where it is
-                        // supposed to be
-                        final int motionViewRealTop = motionView.getTop();
-                        if (atEdge) {
-                            // Apply overscroll
-
-                            int overscroll = -incrementalDeltaY -
-                                    (motionViewRealTop - motionViewPrevTop);
-                            overScrollBy(0, overscroll, 0, mScrollY, 0, 0,
-                                    0, mOverscrollDistance, true);
-                            if (Math.abs(mOverscrollDistance) == Math.abs(mScrollY)) {
-                                // Don't allow overfling if we're at the edge.
-                                if (mVelocityTracker != null) {
-                                    mVelocityTracker.clear();
-                                }
-                            }
-
-                            final int overscrollMode = getOverScrollMode();
-                            if (overscrollMode == OVER_SCROLL_ALWAYS ||
-                                    (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS &&
-                                            !contentFits())) {
-                                mDirection = 0; // Reset when entering overscroll.
-                                mTouchMode = TOUCH_MODE_OVERSCROLL;
-                                if (rawDeltaY > 0) {
-                                    mEdgeGlowTop.onPull((float) overscroll / getHeight());
-                                    if (!mEdgeGlowBottom.isFinished()) {
-                                        mEdgeGlowBottom.onRelease();
-                                    }
-                                } else if (rawDeltaY < 0) {
-                                    mEdgeGlowBottom.onPull((float) overscroll / getHeight());
-                                    if (!mEdgeGlowTop.isFinished()) {
-                                        mEdgeGlowTop.onRelease();
-                                    }
-                                }
-                            }
-                        }
-                        mMotionY = y;
-                        invalidate();
-                    }
-                    mLastY = y;
-                }
-                break;
-
             case TOUCH_MODE_OVERSCROLL:
-                if (y != mLastY) {
-                    final int rawDeltaY = deltaY;
-                    deltaY -= mMotionCorrection;
-                    int incrementalDeltaY = mLastY != Integer.MIN_VALUE ? y - mLastY : deltaY;
-
-                    final int oldScroll = mScrollY;
-                    final int newScroll = oldScroll - incrementalDeltaY;
-                    int newDirection = y > mLastY ? 1 : -1;
-
-                    if (mDirection == 0) {
-                        mDirection = newDirection;
-                    }
-
-                    int overScrollDistance = -incrementalDeltaY;
-                    if ((newScroll < 0 && oldScroll >= 0) || (newScroll > 0 && oldScroll <= 0)) {
-                        overScrollDistance = -oldScroll;
-                        incrementalDeltaY += overScrollDistance;
-                    } else {
-                        incrementalDeltaY = 0;
-                    }
-
-                    if (overScrollDistance != 0) {
-                        overScrollBy(0, overScrollDistance, 0, mScrollY, 0, 0,
-                                0, mOverscrollDistance, true);
-                        final int overscrollMode = getOverScrollMode();
-                        if (overscrollMode == OVER_SCROLL_ALWAYS ||
-                                (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS &&
-                                        !contentFits())) {
-                            if (rawDeltaY > 0) {
-                                mEdgeGlowTop.onPull((float) overScrollDistance / getHeight());
-                                if (!mEdgeGlowBottom.isFinished()) {
-                                    mEdgeGlowBottom.onRelease();
-                                }
-                            } else if (rawDeltaY < 0) {
-                                mEdgeGlowBottom.onPull((float) overScrollDistance / getHeight());
-                                if (!mEdgeGlowTop.isFinished()) {
-                                    mEdgeGlowTop.onRelease();
-                                }
-                            }
-                            invalidate();
-                        }
-                    }
-
-                    if (incrementalDeltaY != 0) {
-                        // Coming back to 'real' list scrolling
-                        mScrollY = 0;
-                        invalidateParentIfNeeded();
-
-                        // No need to do all this work if we're not going to move anyway
-                        if (incrementalDeltaY != 0) {
-                            trackMotionScroll(incrementalDeltaY, incrementalDeltaY);
-                        }
-
-                        mTouchMode = TOUCH_MODE_SCROLL;
-
-                        // We did not scroll the full amount. Treat this essentially like the
-                        // start of a new touch scroll
-                        final int motionPosition = findClosestMotionRow(y);
-
-                        mMotionCorrection = 0;
-                        View motionView = getChildAt(motionPosition - mFirstPosition);
-                        mMotionViewOriginalTop = motionView != null ? motionView.getTop() : 0;
-                        mMotionY = y;
-                        mMotionPosition = motionPosition;
-                    }
-                    mLastY = y;
-                    mDirection = newDirection;
-                }
+                scrollIfNeeded(y);
                 break;
             }
-
             break;
         }
 
@@ -3542,7 +3547,7 @@
                 final int y = (int) ev.getY(pointerIndex);
                 initVelocityTrackerIfNotExists();
                 mVelocityTracker.addMovement(ev);
-                if (startScrollIfNeeded(y - mMotionY)) {
+                if (startScrollIfNeeded(y)) {
                     return true;
                 }
                 break;