Start expanding QS directly when overscrolling to it.

This removes the janky transition from scrolling to flinging.

Change-Id: I691ac94ec06af7f7431ad162e07c21d2c753e99c
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index c82bdf6..2790540 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -94,6 +94,7 @@
     private int mQsMaxExpansionHeight;
     private int mQsPeekHeight;
     private boolean mStackScrollerOverscrolling;
+    private boolean mQsExpansionFromOverscroll;
     private boolean mQsExpansionEnabled = true;
     private ValueAnimator mQsExpansionAnimator;
     private FlingAnimationUtils mFlingAnimationUtils;
@@ -523,19 +524,25 @@
 
 
     @Override
-    public void onOverscrollTopChanged(float amount) {
+    public void onOverscrollTopChanged(float amount, boolean isRubberbanded) {
         cancelAnimation();
         float rounded = amount >= 1f ? amount : 0f;
-        mStackScrollerOverscrolling = rounded != 0f;
+        mStackScrollerOverscrolling = rounded != 0f && isRubberbanded;
+        mQsExpansionFromOverscroll = rounded != 0f;
         setQsExpansion(mQsMinExpansionHeight + rounded);
         updateQsState();
     }
 
     @Override
     public void flingTopOverscroll(float velocity, boolean open) {
-        mStackScrollerOverscrolling = false;
         setQsExpansion(mQsExpansionHeight);
-        flingSettings(velocity, open);
+        flingSettings(velocity, open, new Runnable() {
+            @Override
+            public void run() {
+                mStackScrollerOverscrolling = false;
+                mQsExpansionFromOverscroll = false;
+            }
+        });
     }
 
     private void onQsExpansionStarted() {
@@ -571,13 +578,12 @@
     private void updateQsState() {
         boolean expandVisually = mQsExpanded || mStackScrollerOverscrolling;
         mHeader.setExpanded(expandVisually, mStackScrollerOverscrolling);
-        mNotificationStackScroller.setEnabled(!mQsExpanded);
+        mNotificationStackScroller.setEnabled(!mQsExpanded || mQsExpansionFromOverscroll);
         mQsPanel.setVisibility(expandVisually ? View.VISIBLE : View.INVISIBLE);
-        mQsContainer.setVisibility(mKeyguardShowing && !expandVisually
-                ? View.INVISIBLE
-                : View.VISIBLE);
+        mQsContainer.setVisibility(
+                mKeyguardShowing && !expandVisually ? View.INVISIBLE : View.VISIBLE);
         mScrollView.setTouchEnabled(mQsExpanded);
-        mNotificationStackScroller.setTouchEnabled(!mQsExpanded);
+        mNotificationStackScroller.setTouchEnabled(!mQsExpanded || mQsExpansionFromOverscroll);
     }
 
     private void setQsExpansion(float height) {
@@ -640,9 +646,17 @@
             mQsExpansionAnimator.cancel();
         }
     }
+
     private void flingSettings(float vel, boolean expand) {
+        flingSettings(vel, expand, null);
+    }
+
+    private void flingSettings(float vel, boolean expand, final Runnable onFinishRunnable) {
         float target = expand ? mQsMaxExpansionHeight : mQsMinExpansionHeight;
         if (target == mQsExpansionHeight) {
+            if (onFinishRunnable != null) {
+                onFinishRunnable.run();
+            }
             return;
         }
         ValueAnimator animator = ValueAnimator.ofFloat(mQsExpansionHeight, target);
@@ -657,6 +671,9 @@
             @Override
             public void onAnimationEnd(Animator animation) {
                 mQsExpansionAnimator = null;
+                if (onFinishRunnable != null) {
+                    onFinishRunnable.run();
+                }
             }
         });
         animator.start();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index c9a1b28..94fdd1f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -988,34 +988,50 @@
      */
     public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
             boolean cancelAnimators) {
+        setOverScrollAmount(amount, onTop, animate, cancelAnimators, isRubberbanded(onTop));
+    }
+
+    /**
+     * Set the effective overScroll amount which will be directly reflected in the layout.
+     *
+     * @param amount The amount to overScroll by.
+     * @param onTop Should the effect be applied on top of the scroller.
+     * @param animate Should an animation be performed.
+     * @param cancelAnimators Should running animations be cancelled.
+     * @param isRubberbanded The value which will be passed to
+     *                     {@link OnOverscrollTopChangedListener#onOverscrollTopChanged}
+     */
+    public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
+            boolean cancelAnimators, boolean isRubberbanded) {
         if (cancelAnimators) {
             mStateAnimator.cancelOverScrollAnimators(onTop);
         }
-        setOverScrollAmountInternal(amount, onTop, animate);
+        setOverScrollAmountInternal(amount, onTop, animate, isRubberbanded);
     }
 
-    private void setOverScrollAmountInternal(float amount, boolean onTop, boolean animate) {
+    private void setOverScrollAmountInternal(float amount, boolean onTop, boolean animate,
+            boolean isRubberbanded) {
         amount = Math.max(0, amount);
         if (animate) {
-            mStateAnimator.animateOverScrollToAmount(amount, onTop);
+            mStateAnimator.animateOverScrollToAmount(amount, onTop, isRubberbanded);
         } else {
             setOverScrolledPixels(amount / getRubberBandFactor(onTop), onTop);
             mAmbientState.setOverScrollAmount(amount, onTop);
             if (onTop) {
-                notifyOverscrollTopListener(amount);
+                notifyOverscrollTopListener(amount, isRubberbanded);
             }
             requestChildrenUpdate();
         }
     }
 
-    private void notifyOverscrollTopListener(float amount) {
+    private void notifyOverscrollTopListener(float amount, boolean isRubberbanded) {
         mExpandHelper.onlyObserveMovements(amount > 1.0f);
         if (mDontReportNextOverScroll) {
             mDontReportNextOverScroll = false;
             return;
         }
         if (mOverscrollTopChangedListener != null) {
-            mOverscrollTopChangedListener.onOverscrollTopChanged(amount);
+            mOverscrollTopChangedListener.onOverscrollTopChanged(amount, isRubberbanded);
         }
     }
 
@@ -1061,9 +1077,9 @@
                 updateChildren();
                 float overScrollTop = getCurrentOverScrollAmount(true);
                 if (mOwnScrollY < 0) {
-                    notifyOverscrollTopListener(-mOwnScrollY);
+                    notifyOverscrollTopListener(-mOwnScrollY, isRubberbanded(true));
                 } else {
-                    notifyOverscrollTopListener(overScrollTop);
+                    notifyOverscrollTopListener(overScrollTop, isRubberbanded(true));
                 }
             }
         } else {
@@ -1288,6 +1304,16 @@
         return RUBBER_BAND_FACTOR_NORMAL;
     }
 
+    /**
+     * Accompanying function for {@link #getRubberBandFactor}: Returns true if the overscroll is
+     * rubberbanded, false if it is technically an overscroll but rather a motion to expand the
+     * overscroll view (e.g. expand QS).
+     */
+    private boolean isRubberbanded(boolean onTop) {
+        return !onTop || mExpandedInThisMotion || mIsExpansionChanging
+                || !mScrolledToTopOnFirstDown;
+    }
+
     private void endDrag() {
         setIsBeingDragged(false);
 
@@ -1880,7 +1906,16 @@
      * A listener that gets notified when the overscroll at the top has changed.
      */
     public interface OnOverscrollTopChangedListener {
-        public void onOverscrollTopChanged(float amount);
+
+        /**
+         * Notifies a listener that the overscroll has changed.
+         *
+         * @param amount the amount of overscroll, in pixels
+         * @param isRubberbanded if true, this is a rubberbanded overscroll; if false, this is an
+         *                     unrubberbanded motion to directly expand overscroll view (e.g expand
+         *                     QS)
+         */
+        public void onOverscrollTopChanged(float amount, boolean isRubberbanded);
 
         /**
          * Notify a listener that the scroller wants to escape from the scrolling motion and
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
index 5efbc99..fb54905 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -719,7 +719,8 @@
         }
     }
 
-    public void animateOverScrollToAmount(float targetAmount, final boolean onTop) {
+    public void animateOverScrollToAmount(float targetAmount, final boolean onTop,
+            final boolean isRubberbanded) {
         final float startOverScrollAmount = mHostLayout.getCurrentOverScrollAmount(onTop);
         if (targetAmount == startOverScrollAmount) {
             return;
@@ -733,7 +734,8 @@
             public void onAnimationUpdate(ValueAnimator animation) {
                 float currentOverScroll = (float) animation.getAnimatedValue();
                 mHostLayout.setOverScrollAmount(
-                        currentOverScroll, onTop, false /* animate */, false /* cancelAnimators */);
+                        currentOverScroll, onTop, false /* animate */, false /* cancelAnimators */,
+                        isRubberbanded);
             }
         });
         overScrollAnimator.setInterpolator(mFastOutSlowInInterpolator);