Fix NestedScrollView intercepting touches when it shouldn't

Caused by NestedScrollView not calling springBack() like it's
framework cousin. This means that the scroller still thinks it
is running and makes NSV intercept the next DOWN -> UP touch
event cycle.

BUG: 23340105
Change-Id: I5a036b27c6c2161f7932ee6c3ad3ec180b1e12f3
diff --git a/v4/api/current.txt b/v4/api/current.txt
index 309a7ff..d57b279 100644
--- a/v4/api/current.txt
+++ b/v4/api/current.txt
@@ -3274,6 +3274,7 @@
     method public boolean isOverScrolled();
     method public void notifyHorizontalEdgeReached(int, int, int);
     method public void notifyVerticalEdgeReached(int, int, int);
+    method public boolean springBack(int, int, int, int, int, int);
     method public void startScroll(int, int, int, int);
     method public void startScroll(int, int, int, int, int);
   }
diff --git a/v4/gingerbread/android/support/v4/widget/ScrollerCompatGingerbread.java b/v4/gingerbread/android/support/v4/widget/ScrollerCompatGingerbread.java
index 429e864..61c9a03 100644
--- a/v4/gingerbread/android/support/v4/widget/ScrollerCompatGingerbread.java
+++ b/v4/gingerbread/android/support/v4/widget/ScrollerCompatGingerbread.java
@@ -87,4 +87,9 @@
     public static int getFinalY(Object scroller) {
         return ((OverScroller) scroller).getFinalY();
     }
+
+    public static boolean springBack(Object scroller, int startX, int startY, int minX, int maxX,
+            int minY, int maxY) {
+        return ((OverScroller) scroller).springBack(startX, startY, minX, maxX, minY, maxY);
+    }
 }
diff --git a/v4/java/android/support/v4/widget/NestedScrollView.java b/v4/java/android/support/v4/widget/NestedScrollView.java
index 788761a..1bcc7af 100644
--- a/v4/java/android/support/v4/widget/NestedScrollView.java
+++ b/v4/java/android/support/v4/widget/NestedScrollView.java
@@ -657,6 +657,9 @@
                 mIsBeingDragged = false;
                 mActivePointerId = INVALID_POINTER;
                 recycleVelocityTracker();
+                if (mScroller.springBack(getScrollX(), getScrollY(), 0, 0, 0, getScrollRange())) {
+                    ViewCompat.postInvalidateOnAnimation(this);
+                }
                 stopNestedScroll();
                 break;
             case MotionEventCompat.ACTION_POINTER_UP:
@@ -795,6 +798,9 @@
 
                     if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
                         flingWithNestedDispatch(-initialVelocity);
+                    } else if (mScroller.springBack(getScrollX(), getScrollY(), 0, 0, 0,
+                            getScrollRange())) {
+                        ViewCompat.postInvalidateOnAnimation(this);
                     }
 
                     mActivePointerId = INVALID_POINTER;
@@ -803,6 +809,10 @@
                 break;
             case MotionEvent.ACTION_CANCEL:
                 if (mIsBeingDragged && getChildCount() > 0) {
+                    if (mScroller.springBack(getScrollX(), getScrollY(), 0, 0, 0,
+                            getScrollRange())) {
+                        ViewCompat.postInvalidateOnAnimation(this);
+                    }
                     mActivePointerId = INVALID_POINTER;
                     endDrag();
                 }
@@ -942,6 +952,10 @@
             clampedY = true;
         }
 
+        if (clampedY) {
+            mScroller.springBack(newScrollX, newScrollY, 0, 0, 0, getScrollRange());
+        }
+
         onOverScrolled(newScrollX, newScrollY, clampedX, clampedY);
 
         return clampedX || clampedY;
diff --git a/v4/java/android/support/v4/widget/ScrollerCompat.java b/v4/java/android/support/v4/widget/ScrollerCompat.java
index afbf897..f1b89da 100644
--- a/v4/java/android/support/v4/widget/ScrollerCompat.java
+++ b/v4/java/android/support/v4/widget/ScrollerCompat.java
@@ -54,6 +54,8 @@
         boolean isOverScrolled(Object scroller);
         int getFinalX(Object scroller);
         int getFinalY(Object scroller);
+        boolean springBack(Object scroller, int startX, int startY, int minX, int maxX,
+                int minY, int maxY);
     }
 
     static final int CHASE_FRAME_TIME = 16; // ms per target frame
@@ -145,6 +147,12 @@
         public int getFinalY(Object scroller) {
             return ((Scroller) scroller).getFinalY();
         }
+
+        @Override
+        public boolean springBack(Object scroller, int startX, int startY, int minX, int maxX,
+                int minY, int maxY) {
+            return false;
+        }
     }
 
     static class ScrollerCompatImplGingerbread implements ScrollerCompatImpl {
@@ -233,6 +241,13 @@
         public int getFinalY(Object scroller) {
             return ScrollerCompatGingerbread.getFinalY(scroller);
         }
+
+        @Override
+        public boolean springBack(Object scroller, int startX, int startY, int minX, int maxX,
+                int minY, int maxY) {
+            return ScrollerCompatGingerbread.springBack(scroller, startX, startY, minX, maxX,
+                    minY, maxY);
+        }
     }
 
     static class ScrollerCompatImplIcs extends ScrollerCompatImplGingerbread {
@@ -423,6 +438,22 @@
     }
 
     /**
+     * Call this when you want to 'spring back' into a valid coordinate range.
+     *
+     * @param startX Starting X coordinate
+     * @param startY Starting Y coordinate
+     * @param minX Minimum valid X value
+     * @param maxX Maximum valid X value
+     * @param minY Minimum valid Y value
+     * @param maxY Minimum valid Y value
+     * @return true if a springback was initiated, false if startX and startY were
+     *          already within the valid range.
+     */
+    public boolean springBack(int startX, int startY, int minX, int maxX, int minY, int maxY) {
+        return mImpl.springBack(mScroller, startX, startY, minX, maxX, minY, maxY);
+    }
+
+    /**
      * Stops the animation. Aborting the animation causes the scroller to move to the final x and y
      * position.
      */