Cascading clear-all in the phone notifications panel.

Bug: 5156350
Change-Id: I22c5a19b162e4aa35bf66d51cd17c1e0cd079053
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 79abfed..d6e4d1b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -87,6 +87,7 @@
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.LocationController;
 import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.NotificationRowLayout;
 
 public class PhoneStatusBar extends StatusBar {
     static final String TAG = "PhoneStatusBar";
@@ -103,8 +104,10 @@
     static final int EXPANDED_LEAVE_ALONE = -10000;
     static final int EXPANDED_FULL_OPEN = -10001;
 
-    private static final int MSG_ANIMATE = 1000;
-    private static final int MSG_ANIMATE_REVEAL = 1001;
+    private static final int MSG_ANIMATE = 100;
+    private static final int MSG_ANIMATE_REVEAL = 101;
+    private static final int MSG_OPEN_NOTIFICATION_PANEL = 1000;
+    private static final int MSG_CLOSE_NOTIFICATION_PANEL = 1001;
     private static final int MSG_SHOW_INTRUDER = 1002;
     private static final int MSG_HIDE_INTRUDER = 1003;
     private static final int MSG_OPEN_RECENTS_PANEL = 1020;
@@ -165,7 +168,7 @@
     
     // all notifications
     NotificationData mNotificationData = new NotificationData();
-    ViewGroup mPile;
+    NotificationRowLayout mPile;
 
     // position
     int[] mPositionTmp = new int[2];
@@ -324,7 +327,7 @@
 
         mExpandedDialog = new ExpandedDialog(context);
         mExpandedView = expanded;
-        mPile = (ViewGroup)expanded.findViewById(R.id.latestItems);
+        mPile = (NotificationRowLayout)expanded.findViewById(R.id.latestItems);
         mExpandedContents = mPile; // was: expanded.findViewById(R.id.notificationLinearLayout);
         mNoNotificationsTitle = (TextView)expanded.findViewById(R.id.noNotificationsTitle);
         mNoNotificationsTitle.setVisibility(View.GONE); // disabling for now
@@ -332,6 +335,7 @@
         mClearButton = expanded.findViewById(R.id.clear_all_button);
         mClearButton.setOnClickListener(mClearButtonListener);
         mClearButton.setAlpha(0f);
+        mClearButton.setEnabled(false);
         mDateView = (DateView)expanded.findViewById(R.id.date);
         mSettingsButton = expanded.findViewById(R.id.settings_button);
         mSettingsButton.setOnClickListener(mSettingsButtonListener);
@@ -1005,6 +1009,7 @@
         } else {
             mClearButton.setAlpha(clearable ? 1.0f : 0.0f);
         }
+        mClearButton.setEnabled(clearable);
 
         /*
         if (mNoNotificationsTitle.isShown()) {
@@ -1114,6 +1119,12 @@
                 case MSG_ANIMATE_REVEAL:
                     doRevealAnimation();
                     break;
+                case MSG_OPEN_NOTIFICATION_PANEL:
+                    animateExpand();
+                    break;
+                case MSG_CLOSE_NOTIFICATION_PANEL:
+                    animateCollapse();
+                    break;
                 case MSG_SHOW_INTRUDER:
                     setIntruderAlertVisibility(true);
                     break;
@@ -1181,6 +1192,10 @@
     }
 
     public void animateCollapse(boolean excludeRecents) {
+        animateCollapse(excludeRecents, 1.0f);
+    }
+    
+    public void animateCollapse(boolean excludeRecents, float velocityMultiplier) {
         if (SPEW) {
             Slog.d(TAG, "animateCollapse(): mExpanded=" + mExpanded
                     + " mExpandedVisible=" + mExpandedVisible
@@ -1209,7 +1224,7 @@
         // and doesn't try to re-open the windowshade.
         mExpanded = true;
         prepareTracking(y, false);
-        performFling(y, -mSelfCollapseVelocityPx, true);
+        performFling(y, -mSelfCollapseVelocityPx*velocityMultiplier, true);
     }
 
     void performExpand() {
@@ -2086,13 +2101,57 @@
     }
 
     private View.OnClickListener mClearButtonListener = new View.OnClickListener() {
+        final int mini(int a, int b) {
+            return (b>a?a:b);
+        }
         public void onClick(View v) {
-            try {
-                mBarService.onClearAllNotifications();
-            } catch (RemoteException ex) {
-                // system process is dead if we're here.
+            synchronized (mNotificationData) {
+                // let's also queue up 400ms worth of animated dismissals
+                final int N = mini(5, mPile.getChildCount());
+
+                final ArrayList<View> snapshot = new ArrayList<View>(N);
+                for (int i=0; i<N; i++) {
+                    final View child = mPile.getChildAt(i);
+                    if (mPile.canChildBeDismissed(child)) snapshot.add(child);
+                }
+                new Thread(new Runnable() {
+                    @Override
+                    public void run() {
+                        final int ROW_DELAY = 100;
+
+                        mHandler.postDelayed(new Runnable() {
+                            public void run() {
+                                animateCollapse(false, 0f);
+                            }
+                        }, (N-1) * ROW_DELAY);
+
+                        mHandler.postDelayed(new Runnable() {
+                            public void run() {
+                                try {
+                                    mBarService.onClearAllNotifications();
+                                } catch (RemoteException ex) { }
+                            }
+                        }, N * ROW_DELAY + 500);
+
+                        mPile.setAnimateBounds(false); // temporarily disable some re-layouts
+
+                        for (View v : snapshot) {
+                            final View _v = v;
+                            mHandler.post(new Runnable() {
+                                @Override
+                                public void run() {
+                                    mPile.dismissRowAnimated(_v, (int)(ROW_DELAY*0.25f));
+                                }
+                            });
+                            try {
+                                Thread.sleep(ROW_DELAY);
+                            } catch (InterruptedException ex) { }
+                        }
+                        
+                        mPile.setAnimateBounds(true); // reenable layout animation
+                    }
+                }).start();
             }
-            animateCollapse();
         }
     };
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
index 06798c6..a7342dc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
@@ -44,11 +44,11 @@
     private static final boolean DEBUG = false;
     private static final boolean SLOW_ANIMATIONS = DEBUG;
 
-    private static final boolean ANIMATE_LAYOUT = true;
-
     private static final int APPEAR_ANIM_LEN = SLOW_ANIMATIONS ? 5000 : 250;
     private static final int DISAPPEAR_ANIM_LEN = APPEAR_ANIM_LEN;
 
+    boolean mAnimateBounds = true;
+
     Rect mTmpRect = new Rect();
     int mNumRows = 0;
     int mRowHeight = 0;
@@ -93,6 +93,10 @@
         mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, densityScale, pagingTouchSlop);
     }
 
+    public void setAnimateBounds(boolean anim) {
+        mAnimateBounds = anim;
+    }
+
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         if (DEBUG) Log.v(TAG, "onInterceptTouchEvent()");
@@ -165,7 +169,7 @@
 
         final View childF = child;
 
-        if (ANIMATE_LAYOUT) {
+        if (mAnimateBounds) {
             child.setPivotY(0);
             final ObjectAnimator alphaFade = ObjectAnimator.ofFloat(child, "alpha", 0f, 1f);
             alphaFade.setDuration(APPEAR_ANIM_LEN);
@@ -185,10 +189,18 @@
         }
     }
 
+    public void dismissRowAnimated(View child) {
+        dismissRowAnimated(child, 0);
+    }
+
+    public void dismissRowAnimated(View child, int vel) {
+        mSwipeHelper.dismissChild(child, vel);
+    }
+
     @Override
     public void removeView(View child) {
         final View childF = child;
-        if (ANIMATE_LAYOUT) {
+        if (mAnimateBounds) {
             if (mAppearingViews.containsKey(child)) {
                 mAppearingViews.remove(child);
             }
@@ -264,7 +276,7 @@
 
             mNumRows = numRows;
 
-            if (ANIMATE_LAYOUT && isShown()) {
+            if (mAnimateBounds && isShown()) {
                 ObjectAnimator.ofInt(this, "forcedHeight", computedHeight)
                     .setDuration(APPEAR_ANIM_LEN)
                     .start();