Clear all notifications improvements

- Fix a bug with visibility.
- Improve fling behavior such that the shade open animation still
  looks like that we are decelerating towards the last card.

Change-Id: I1ad167ce0001ff6850f49e819bab944943fa529d
diff --git a/packages/SystemUI/res/layout/status_bar_expanded_header.xml b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
index 0eec094..b260a4a 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
@@ -91,6 +91,7 @@
         android:id="@+id/date_group"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
+        android:layout_marginBottom="@dimen/clock_collapsed_bottom_margin"
         android:layout_alignParentBottom="true">
         <com.android.systemui.statusbar.policy.DateView android:id="@+id/date_collapsed"
             android:layout_width="wrap_content"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DismissView.java b/packages/SystemUI/src/com/android/systemui/statusbar/DismissView.java
index 6cc1a57..d60c17f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DismissView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DismissView.java
@@ -30,6 +30,9 @@
 
     private Button mClearAllText;
     private boolean mIsVisible;
+    private boolean mAnimating;
+    private boolean mWillBeGone;
+
     private final Interpolator mAppearInterpolator = new PathInterpolator(0f, 0.2f, 1f, 1f);
     private final Interpolator mDisappearInterpolator = new PathInterpolator(0f, 0f, 0.8f, 1f);
 
@@ -63,6 +66,10 @@
         animateText(nowVisible, onFinishedRunnable);
     }
 
+    public boolean isVisible() {
+        return mIsVisible || mAnimating;
+    }
+
     /**
      * Animate the text to a new visibility.
      *
@@ -70,7 +77,7 @@
      * @param onFinishedRunnable A runnable which should be run when the animation is
      *        finished.
      */
-    public void animateText(boolean nowVisible, Runnable onFinishedRunnable) {
+    private void animateText(boolean nowVisible, final Runnable onFinishedRunnable) {
         if (nowVisible != mIsVisible) {
             // Animate text
             float endValue = nowVisible ? 1.0f : 0.0f;
@@ -80,12 +87,21 @@
             } else {
                 interpolator = mDisappearInterpolator;
             }
+            mAnimating = true;
             mClearAllText.animate()
                     .alpha(endValue)
                     .setInterpolator(interpolator)
-                    .withEndAction(onFinishedRunnable)
                     .setDuration(260)
-                    .withLayer();
+                    .withLayer()
+                    .withEndAction(new Runnable() {
+                        @Override
+                        public void run() {
+                            mAnimating = false;
+                            if (onFinishedRunnable != null) {
+                                onFinishedRunnable.run();
+                            }
+                        }
+                    });
             mIsVisible = nowVisible;
         } else {
             if (onFinishedRunnable != null) {
@@ -126,4 +142,12 @@
     public void cancelAnimation() {
         mClearAllText.animate().cancel();
     }
+
+    public boolean willBeGone() {
+        return mWillBeGone;
+    }
+
+    public void setWillBeGone(boolean willBeGone) {
+        mWillBeGone = willBeGone;
+    }
 }
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 909972a..b80a33b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -1289,6 +1289,22 @@
     }
 
     @Override
+    protected boolean fullyExpandedClearAllVisible() {
+        return mNotificationStackScroller.isDismissViewNotGone()
+                && mNotificationStackScroller.isScrolledToBottom();
+    }
+
+    @Override
+    protected boolean isClearAllVisible() {
+        return mNotificationStackScroller.isDismissViewVisible();
+    }
+
+    @Override
+    protected int getClearAllHeight() {
+        return mNotificationStackScroller.getDismissViewHeight();
+    }
+
+    @Override
     protected boolean isTrackingBlocked() {
         return mConflictingQsExpansionGesture && mQsExpanded;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 7fb5693..3ec2395 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -462,6 +462,16 @@
     protected void fling(float vel, boolean expand) {
         cancelPeek();
         float target = expand ? getMaxPanelHeight() : 0.0f;
+
+        // Hack to make the expand transition look nice when clear all button is visible - we make
+        // the animation only to the last notification, and then jump to the maximum panel height so
+        // clear all just fades in and the decelerating motion is towards the last notification.
+        final boolean clearAllExpandHack = expand && fullyExpandedClearAllVisible()
+                && mExpandedHeight < getMaxPanelHeight() - getClearAllHeight()
+                && !isClearAllVisible();
+        if (clearAllExpandHack) {
+            target = getMaxPanelHeight() - getClearAllHeight();
+        }
         if (target == mExpandedHeight || getOverExpansionAmount() > 0f && expand) {
             notifyExpandingFinished();
             return;
@@ -490,9 +500,32 @@
 
             @Override
             public void onAnimationEnd(Animator animation) {
-                mHeightAnimator = null;
-                if (!mCancelled) {
-                    notifyExpandingFinished();
+                if (clearAllExpandHack && !mCancelled) {
+                    mHeightAnimator = createHeightAnimator(getMaxPanelHeight());
+                    mHeightAnimator.setInterpolator(mLinearOutSlowInInterpolator);
+                    mHeightAnimator.setDuration(350);
+                    mHeightAnimator.addListener(new AnimatorListenerAdapter() {
+                        private boolean mCancelled;
+
+                        @Override
+                        public void onAnimationCancel(Animator animation) {
+                            mCancelled = true;
+                        }
+
+                        @Override
+                        public void onAnimationEnd(Animator animation) {
+                            mHeightAnimator = null;
+                            if (!mCancelled) {
+                                notifyExpandingFinished();
+                            }
+                        }
+                    });
+                    mHeightAnimator.start();
+                } else {
+                    mHeightAnimator = null;
+                    if (!mCancelled) {
+                        notifyExpandingFinished();
+                    }
                 }
             }
         });
@@ -878,4 +911,16 @@
     protected abstract float getPeekHeight();
 
     protected abstract float getCannedFlingDurationFactor();
+
+    /**
+     * @return whether "Clear all" button will be visible when the panel is fully expanded
+     */
+    protected abstract boolean fullyExpandedClearAllVisible();
+
+    protected abstract boolean isClearAllVisible();
+
+    /**
+     * @return the height of the clear all button, in pixels
+     */
+    protected abstract int getClearAllHeight();
 }
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 8b4e79f..9b819f2b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -175,7 +175,6 @@
     private boolean mInterceptDelegateEnabled;
     private boolean mDelegateToScrollView;
     private boolean mDisallowScrollingInThisMotion;
-    private boolean mDismissWillBeGone;
 
     private ViewTreeObserver.OnPreDrawListener mChildrenUpdater
             = new ViewTreeObserver.OnPreDrawListener() {
@@ -1978,24 +1977,26 @@
     }
 
     public void updateDismissView(boolean visible) {
-        int oldVisibility = mDismissWillBeGone ? GONE : mDismissView.getVisibility();
+        int oldVisibility = mDismissView.willBeGone() ? GONE : mDismissView.getVisibility();
         int newVisibility = visible ? VISIBLE : GONE;
         if (oldVisibility != newVisibility) {
             if (oldVisibility == GONE) {
-                if (mDismissWillBeGone) {
+                if (mDismissView.willBeGone()) {
                     mDismissView.cancelAnimation();
                 } else {
                     mDismissView.setInvisible();
                     mDismissView.setVisibility(newVisibility);
                 }
-                mDismissWillBeGone = false;
+                mDismissView.setWillBeGone(false);
+                updateContentHeight();
             } else {
-                mDismissWillBeGone = true;
+                mDismissView.setWillBeGone(true);
                 mDismissView.performVisibilityAnimation(false, new Runnable() {
                     @Override
                     public void run() {
                         mDismissView.setVisibility(GONE);
-                        mDismissWillBeGone = false;
+                        mDismissView.setWillBeGone(false);
+                        updateContentHeight();
                     }
                 });
             }
@@ -2006,6 +2007,18 @@
         mDismissAllInProgress = dismissAllInProgress;
     }
 
+    public boolean isDismissViewNotGone() {
+        return mDismissView.getVisibility() != View.GONE && !mDismissView.willBeGone();
+    }
+
+    public boolean isDismissViewVisible() {
+        return mDismissView.isVisible();
+    }
+
+    public int getDismissViewHeight() {
+        return mDismissView.getHeight() + mPaddingBetweenElementsNormal;
+    }
+
     /**
      * A listener that is notified when some child locations might have changed.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
index 76115a7..7b90a351 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
@@ -176,7 +176,7 @@
                 } else if (child instanceof DismissView) {
                     DismissView dismissView = (DismissView) child;
                     boolean visible = state.topOverLap < mClearAllTopPadding;
-                    dismissView.performVisibilityAnimation(visible);
+                    dismissView.performVisibilityAnimation(visible && !dismissView.willBeGone());
                 }
             }
         }