Enable surfacing of notification children

Yo Dawg, I herd you like notifications, so I put a
notification in your notification so you can be
interrupted while you are being interrupted.

Bug: 15869874
Bug: 15188947
Change-Id: I6c733d6f8e8a04f85036182f82d3e945c6feb5bc
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
index 5214ab4..7072dcb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -100,6 +100,8 @@
                             }
                         }
                     });
+                } else {
+                    group.summary.row.updateExpandButton();
                 }
             }
         }
@@ -116,11 +118,15 @@
         }
         if (notif.isGroupSummary()) {
             group.summary = added;
+            group.expanded = added.row.areChildrenExpanded();
             if (!group.children.isEmpty()) {
                 mListener.onGroupCreatedFromChildren(group);
             }
         } else {
             group.children.add(added);
+            if (group.summary != null && group.children.size() == 1 && !group.expanded) {
+                group.summary.row.updateExpandButton();
+            }
         }
     }
 
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 7513fc6..195da46 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -1680,7 +1680,7 @@
     }
 
     @Override
-    public void onHeightChanged(ExpandableView view) {
+    public void onHeightChanged(ExpandableView view, boolean needsAnimation) {
 
         // Block update if we are in quick settings and just the top padding changed
         // (i.e. view == null).
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 e5eb747..f3ec34a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -170,6 +170,7 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -524,6 +525,8 @@
             goToLockedShade(null);
         }
     };
+    private HashMap<ExpandableNotificationRow, List<ExpandableNotificationRow>> mTmpChildOrderMap
+            = new HashMap<>();
 
     @Override
     public void start() {
@@ -664,6 +667,8 @@
                 R.id.notification_stack_scroller);
         mStackScroller.setLongPressListener(getNotificationLongClicker());
         mStackScroller.setPhoneStatusBar(this);
+        mStackScroller.setGroupManager(mGroupManager);
+        mGroupManager.setOnGroupChangeListener(mStackScroller);
 
         mKeyguardIconOverflowContainer =
                 (NotificationOverflowContainer) LayoutInflater.from(mContext).inflate(
@@ -855,9 +860,20 @@
         final ArrayList<View> viewsToHide = new ArrayList<View>(numChildren);
         for (int i = 0; i < numChildren; i++) {
             final View child = mStackScroller.getChildAt(i);
-            if (mStackScroller.canChildBeDismissed(child)) {
-                if (child.getVisibility() == View.VISIBLE) {
-                    viewsToHide.add(child);
+            if (child instanceof ExpandableNotificationRow) {
+                if (mStackScroller.canChildBeDismissed(child)) {
+                    if (child.getVisibility() == View.VISIBLE) {
+                        viewsToHide.add(child);
+                    }
+                }
+                ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+                List<ExpandableNotificationRow> children = row.getNotificationChildren();
+                if (row.areChildrenExpanded() && children != null) {
+                    for (ExpandableNotificationRow childRow : children) {
+                        if (childRow.getVisibility() == View.VISIBLE) {
+                            viewsToHide.add(childRow);
+                        }
+                    }
                 }
             }
         }
@@ -1296,10 +1312,23 @@
                     ent.row.setShowingLegacyBackground(true);
                 }
             }
-            toShow.add(ent.row);
+            if (mGroupManager.isChildInGroupWithSummary(ent.row.getStatusBarNotification())) {
+                ExpandableNotificationRow summary = mGroupManager.getGroupSummary(
+                        ent.row.getStatusBarNotification());
+                List<ExpandableNotificationRow> orderedChildren =
+                        mTmpChildOrderMap.get(summary);
+                if (orderedChildren == null) {
+                    orderedChildren = new ArrayList<>();
+                    mTmpChildOrderMap.put(summary, orderedChildren);
+                }
+                orderedChildren.add(ent.row);
+            } else {
+                toShow.add(ent.row);
+            }
+
         }
 
-        ArrayList<View> toRemove = new ArrayList<View>();
+        ArrayList<View> toRemove = new ArrayList<>();
         for (int i=0; i< mStackScroller.getChildCount(); i++) {
             View child = mStackScroller.getChildAt(i);
             if (!toShow.contains(child) && child instanceof ExpandableNotificationRow) {
@@ -1328,17 +1357,22 @@
                 continue;
             }
 
-            if (child == toShow.get(j)) {
-                // Everything is well, advance both lists.
-                j++;
-                continue;
+            ExpandableNotificationRow targetChild = toShow.get(j);
+            if (child != targetChild) {
+                // Oops, wrong notification at this position. Put the right one
+                // here and advance both lists.
+                mStackScroller.changeViewPosition(targetChild, i);
             }
-
-            // Oops, wrong notification at this position. Put the right one
-            // here and advance both lists.
-            mStackScroller.changeViewPosition(toShow.get(j), i);
             j++;
+
         }
+
+        // lets handle the child notifications now
+        updateNotificationShadeForChildren();
+
+        // clear the map again for the next usage
+        mTmpChildOrderMap.clear();
+
         updateRowStates();
         updateSpeedbump();
         updateClearAll();
@@ -1353,6 +1387,52 @@
         mShadeUpdates.check();
     }
 
+    private void updateNotificationShadeForChildren() {
+        ArrayList<ExpandableNotificationRow> toRemove = new ArrayList<>();
+        boolean orderChanged = false;
+        for (int i = 0; i < mStackScroller.getChildCount(); i++) {
+            View view = mStackScroller.getChildAt(i);
+            if (!(view instanceof ExpandableNotificationRow)) {
+                // We don't care about non-notification views.
+                continue;
+            }
+
+            ExpandableNotificationRow parent = (ExpandableNotificationRow) view;
+            List<ExpandableNotificationRow> children = parent.getNotificationChildren();
+            List<ExpandableNotificationRow> orderedChildren = mTmpChildOrderMap.get(parent);
+
+            // lets first remove all undesired children
+            if (children != null) {
+                toRemove.clear();
+                for (ExpandableNotificationRow childRow : children) {
+                    if (orderedChildren == null || !orderedChildren.contains(childRow)) {
+                        toRemove.add(childRow);
+                    }
+                }
+                for (ExpandableNotificationRow remove : toRemove) {
+                    parent.removeChildNotification(remove);
+                    mStackScroller.notifyGroupChildRemoved(remove);
+                }
+            }
+
+            // We now add all the children which are not in there already
+            for (int childIndex = 0; orderedChildren != null && childIndex < orderedChildren.size();
+                    childIndex++) {
+                ExpandableNotificationRow childView = orderedChildren.get(childIndex);
+                if (children == null || !children.contains(childView)) {
+                    parent.addChildNotification(childView, childIndex);
+                    mStackScroller.notifyGroupChildAdded(childView);
+                }
+            }
+
+            // Finally after removing and adding has been beformed we can apply the order.
+            orderChanged |= parent.applyChildOrder(orderedChildren);
+        }
+        if (orderChanged) {
+            mStackScroller.generateChildOrderChangedEvent();
+        }
+    }
+
     private boolean packageHasVisibilityOverride(String key) {
         return mNotificationData.getVisibilityOverride(key)
                 != NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE;
@@ -1379,6 +1459,10 @@
         final int N = activeNotifications.size();
         for (int i = 0; i < N; i++) {
             Entry entry = activeNotifications.get(i);
+            boolean isChild = !isTopLevelChild(entry);
+            if (isChild) {
+                continue;
+            }
             if (entry.row.getVisibility() != View.GONE &&
                     mNotificationData.isAmbient(entry.key)) {
                 speedbumpIndex = currentIndex;
@@ -1389,6 +1473,10 @@
         mStackScroller.updateSpeedBumpIndex(speedbumpIndex);
     }
 
+    public static boolean isTopLevelChild(Entry entry) {
+        return entry.row.getParent() instanceof NotificationStackScrollLayout;
+    }
+
     @Override
     protected void updateNotifications() {
         mNotificationData.filterAndSort();
@@ -3074,7 +3162,7 @@
         mLeaveOpenOnKeyguardHide = false;
         if (mDraggedDownRow != null) {
             mDraggedDownRow.setUserLocked(false);
-            mDraggedDownRow.notifyHeightChanged();
+            mDraggedDownRow.notifyHeightChanged(false  /* needsAnimation */);
             mDraggedDownRow = null;
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index 798467f..c49f620 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -158,13 +158,16 @@
         final int N = activeNotifications.size();
         ArrayList<StatusBarIconView> toShow = new ArrayList<>(N);
 
-        // Filter out ambient notifications.
+        // Filter out ambient notifications and notification children.
         for (int i = 0; i < N; i++) {
             NotificationData.Entry ent = activeNotifications.get(i);
             if (notificationData.isAmbient(ent.key)
                     && !NotificationData.showNotificationEvenIfUnprovisioned(ent.notification)) {
                 continue;
             }
+            if (!PhoneStatusBar.isTopLevelChild(ent)) {
+                continue;
+            }
             toShow.add(ent.icon);
         }