Bind children dynamically in new pipeline (2/2)

Support setting a logical child count on the notification group view.
Since we are filtering out the notifications and their views entirely in
the new pipeline, we can't just use the number of children views on the
notification group view to determine the "+X" value in the group view UX
for how many children are in the unexanded group. So instead we set this
value directly.

Bug: 145748993
Test: add notification group and see overflow values are accurate in
both old and new pipeline

Change-Id: I60bc58994a77bc3801f062dedc41faaee1d48494
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 669a86b..7bdeb1c 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -1110,7 +1110,7 @@
 
     private void handleSummaryDismissalInterception(NotificationEntry summary) {
         // current children in the row:
-        final List<NotificationEntry> children = summary.getChildren();
+        final List<NotificationEntry> children = summary.getAttachedNotifChildren();
         if (children != null) {
             for (int i = 0; i < children.size(); i++) {
                 NotificationEntry child = children.get(i);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
index ba3db09..670a65f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
@@ -148,7 +148,7 @@
     }
 
     public void updateChildrenHeaderAppearance() {
-        List<ExpandableNotificationRow> notificationChildren = mRow.getNotificationChildren();
+        List<ExpandableNotificationRow> notificationChildren = mRow.getAttachedChildren();
         if (notificationChildren == null) {
             return;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 1297f99..37fc13e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -307,17 +307,20 @@
             }
 
             ExpandableNotificationRow parent = (ExpandableNotificationRow) view;
-            List<ExpandableNotificationRow> children = parent.getNotificationChildren();
+            List<ExpandableNotificationRow> children = parent.getAttachedChildren();
             List<NotificationEntry> orderedChildren = mTmpChildOrderMap.get(parent.getEntry());
-
-            for (int childIndex = 0; orderedChildren != null && childIndex < orderedChildren.size();
-                    childIndex++) {
+            if (orderedChildren == null) {
+                // Not a group
+                continue;
+            }
+            parent.setUntruncatedChildCount(orderedChildren.size());
+            for (int childIndex = 0; childIndex < orderedChildren.size(); childIndex++) {
                 ExpandableNotificationRow childView = orderedChildren.get(childIndex).getRow();
                 if (children == null || !children.contains(childView)) {
                     if (childView.getParent() != null) {
-                        Log.wtf(TAG, "trying to add a notification child that already has " +
-                                "a parent. class:" + childView.getParent().getClass() +
-                                "\n child: " + childView);
+                        Log.wtf(TAG, "trying to add a notification child that already has "
+                                + "a parent. class:" + childView.getParent().getClass()
+                                + "\n child: " + childView);
                         // This shouldn't happen. We can recover by removing it though.
                         ((ViewGroup) childView.getParent()).removeView(childView);
                     }
@@ -349,7 +352,7 @@
             }
 
             ExpandableNotificationRow parent = (ExpandableNotificationRow) view;
-            List<ExpandableNotificationRow> children = parent.getNotificationChildren();
+            List<ExpandableNotificationRow> children = parent.getAttachedChildren();
             List<NotificationEntry> orderedChildren = mTmpChildOrderMap.get(parent.getEntry());
 
             if (children != null) {
@@ -454,7 +457,7 @@
             }
             if (row.isSummaryWithChildren()) {
                 List<ExpandableNotificationRow> notificationChildren =
-                        row.getNotificationChildren();
+                        row.getAttachedChildren();
                 int size = notificationChildren.size();
                 for (int i = size - 1; i >= 0; i--) {
                     stack.push(notificationChildren.get(i));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index d37e16b..3cf0765 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -515,7 +515,7 @@
                 // always cancelled. We only remove them if they were dismissed by the user.
                 return;
             }
-            List<NotificationEntry> childEntries = entry.getChildren();
+            List<NotificationEntry> childEntries = entry.getAttachedNotifChildren();
             if (childEntries == null) {
                 return;
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java
index 2c747bd..81494ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.statusbar.notification.collection.coordinator.PreparationCoordinator;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -36,6 +37,7 @@
 
     private final List<NotificationEntry> mUnmodifiableChildren =
             Collections.unmodifiableList(mChildren);
+    private int mUntruncatedChildCount;
 
     @VisibleForTesting
     public GroupEntry(String key) {
@@ -62,6 +64,24 @@
         mSummary = summary;
     }
 
+    /**
+     * @see #getUntruncatedChildCount()
+     */
+    public void setUntruncatedChildCount(int childCount) {
+        mUntruncatedChildCount = childCount;
+    }
+
+    /**
+     * Get the untruncated number of children from the data model, including those that will not
+     * have views bound. This includes children that {@link PreparationCoordinator} will filter out
+     * entirely when they are beyond the last visible child.
+     *
+     * TODO: This should move to some shared class between the model and view hierarchy
+     */
+    public int getUntruncatedChildCount() {
+        return mUntruncatedChildCount;
+    }
+
     void clearChildren() {
         mChildren.clear();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifViewManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifViewManager.kt
index cf670bd..339809e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifViewManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifViewManager.kt
@@ -113,7 +113,7 @@
                     } else if (entries[idx] is GroupEntry) {
                         // A top-level entry exists. If it's a group, diff the children
                         val groupChildren = (entries[idx] as GroupEntry).children
-                        listItem.notificationChildren?.forEach { listChild ->
+                        listItem.attachedChildren?.forEach { listChild ->
                             if (!groupChildren.contains(listChild.entry)) {
                                 listItem.removeChildNotification(listChild)
 
@@ -155,8 +155,8 @@
                 for ((idx, childEntry) in entry.children.withIndex()) {
                     val childListItem = rowRegistry.requireView(childEntry)
                     // Child hasn't been added yet. add it!
-                    if (listItem.notificationChildren == null ||
-                            !listItem.notificationChildren.contains(childListItem)) {
+                    if (listItem.attachedChildren == null ||
+                            !listItem.attachedChildren.contains(childListItem)) {
                         // TODO: old code here just Log.wtf()'d here. This might wreak havoc
                         if (childListItem.view.parent != null) {
                             throw IllegalStateException("trying to add a notification child that " +
@@ -179,6 +179,7 @@
                                 stabilityManager,
                                 null /*TODO: stability callback */
                         )
+                listItem.setUntruncatedChildCount(entry.untruncatedChildCount)
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 749c3e4..5236385 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -434,13 +434,18 @@
         mRowController = controller;
     }
 
-    @Nullable
-    public List<NotificationEntry> getChildren() {
+    /**
+     * Get the children that are actually attached to this notification's row.
+     *
+     * TODO: Seems like most callers here should probably be using
+     * {@link com.android.systemui.statusbar.phone.NotificationGroupManager#getChildren}
+     */
+    public @Nullable List<NotificationEntry> getAttachedNotifChildren() {
         if (row == null) {
             return null;
         }
 
-        List<ExpandableNotificationRow> rowChildren = row.getNotificationChildren();
+        List<ExpandableNotificationRow> rowChildren = row.getAttachedChildren();
         if (rowChildren == null) {
             return null;
         }
@@ -748,7 +753,7 @@
             return false;
         }
 
-        List<NotificationEntry> children = getChildren();
+        List<NotificationEntry> children = getAttachedNotifChildren();
         if (children != null && children.size() > 0) {
             for (int i = 0; i < children.size(); i++) {
                 NotificationEntry child =  children.get(i);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
index 5a34f29..4159d43 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
@@ -208,6 +208,7 @@
             ListEntry entry = entries.get(i);
             if (entry instanceof GroupEntry) {
                 GroupEntry groupEntry = (GroupEntry) entry;
+                groupEntry.setUntruncatedChildCount(groupEntry.getChildren().size());
                 inflateRequiredGroupViews(groupEntry);
             } else {
                 NotificationEntry notifEntry = (NotificationEntry) entry;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 998230f..fd5cd58 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -410,7 +410,7 @@
             setIconAnimationRunningForChild(running, mChildrenContainer.getHeaderView());
             setIconAnimationRunningForChild(running, mChildrenContainer.getLowPriorityHeaderView());
             List<ExpandableNotificationRow> notificationChildren =
-                    mChildrenContainer.getNotificationChildren();
+                    mChildrenContainer.getAttachedChildren();
             for (int i = 0; i < notificationChildren.size(); i++) {
                 ExpandableNotificationRow child = notificationChildren.get(i);
                 child.setIconAnimationRunning(running);
@@ -559,7 +559,7 @@
         if (mNotificationParent != null) {
             mNotificationParent.updateChildrenHeaderAppearance();
         }
-        onChildrenCountChanged();
+        onAttachedChildrenCountChanged();
         // The public layouts expand button is always visible
         mPublicLayout.updateExpandButtons(true);
         updateLimits();
@@ -763,6 +763,16 @@
     }
 
     /**
+     * @see NotificationChildrenContainer#setUntruncatedChildCount(int)
+     */
+    public void setUntruncatedChildCount(int childCount) {
+        if (mChildrenContainer == null) {
+            mChildrenContainerStub.inflate();
+        }
+        mChildrenContainer.setUntruncatedChildCount(childCount);
+    }
+
+    /**
      * Add a child notification to this view.
      *
      * @param row the row to add
@@ -773,7 +783,7 @@
             mChildrenContainerStub.inflate();
         }
         mChildrenContainer.addNotification(row, childIndex);
-        onChildrenCountChanged();
+        onAttachedChildrenCountChanged();
         row.setIsChildInGroup(true, this);
     }
 
@@ -792,7 +802,7 @@
         if (mChildrenContainer != null) {
             mChildrenContainer.removeNotification(row);
         }
-        onChildrenCountChanged();
+        onAttachedChildrenCountChanged();
         row.setIsChildInGroup(false, null);
         row.setBottomRoundness(0.0f, false /* animate */);
     }
@@ -886,15 +896,8 @@
         return mChildrenExpanded;
     }
 
-    public List<ExpandableNotificationRow> getNotificationChildren() {
-        return mChildrenContainer == null ? null : mChildrenContainer.getNotificationChildren();
-    }
-
-    public int getNumberOfNotificationChildren() {
-        if (mChildrenContainer == null) {
-            return 0;
-        }
-        return mChildrenContainer.getNotificationChildren().size();
+    public List<ExpandableNotificationRow> getAttachedChildren() {
+        return mChildrenContainer == null ? null : mChildrenContainer.getAttachedChildren();
     }
 
     /**
@@ -1028,7 +1031,7 @@
         setChronometerRunning(running, mPublicLayout);
         if (mChildrenContainer != null) {
             List<ExpandableNotificationRow> notificationChildren =
-                    mChildrenContainer.getNotificationChildren();
+                    mChildrenContainer.getAttachedChildren();
             for (int i = 0; i < notificationChildren.size(); i++) {
                 ExpandableNotificationRow child = notificationChildren.get(i);
                 child.setChronometerRunning(running);
@@ -1228,7 +1231,7 @@
         mUpdateBackgroundOnUpdate = true;
         reInflateViews();
         if (mChildrenContainer != null) {
-            for (ExpandableNotificationRow child : mChildrenContainer.getNotificationChildren()) {
+            for (ExpandableNotificationRow child : mChildrenContainer.getAttachedChildren()) {
                 child.onUiModeChanged();
             }
         }
@@ -1286,8 +1289,8 @@
     }
 
     public void removeAllChildren() {
-        List<ExpandableNotificationRow> notificationChildren
-                = mChildrenContainer.getNotificationChildren();
+        List<ExpandableNotificationRow> notificationChildren =
+                mChildrenContainer.getAttachedChildren();
         ArrayList<ExpandableNotificationRow> clonedList = new ArrayList<>(notificationChildren);
         for (int i = 0; i < clonedList.size(); i++) {
             ExpandableNotificationRow row = clonedList.get(i);
@@ -1297,7 +1300,7 @@
             mChildrenContainer.removeNotification(row);
             row.setIsChildInGroup(false, null);
         }
-        onChildrenCountChanged();
+        onAttachedChildrenCountChanged();
     }
 
     @Override
@@ -1308,7 +1311,7 @@
     public void setForceUnlocked(boolean forceUnlocked) {
         mForceUnlocked = forceUnlocked;
         if (mIsSummaryWithChildren) {
-            List<ExpandableNotificationRow> notificationChildren = getNotificationChildren();
+            List<ExpandableNotificationRow> notificationChildren = getAttachedChildren();
             for (ExpandableNotificationRow child : notificationChildren) {
                 child.setForceUnlocked(forceUnlocked);
             }
@@ -1324,7 +1327,7 @@
         mEntry.getIcons().getStatusBarIcon().setDismissed();
         if (isChildInGroup()) {
             List<ExpandableNotificationRow> notificationChildren =
-                    mNotificationParent.getNotificationChildren();
+                    mNotificationParent.getAttachedChildren();
             int i = notificationChildren.indexOf(this);
             if (i != -1 && i < notificationChildren.size() - 1) {
                 mChildAfterViewWhenDismissed = notificationChildren.get(i + 1);
@@ -2328,7 +2331,7 @@
         return mGroupManager.isGroupExpanded(mEntry.getSbn());
     }
 
-    private void onChildrenCountChanged() {
+    private void onAttachedChildrenCountChanged() {
         mIsSummaryWithChildren = mChildrenContainer != null
                 && mChildrenContainer.getNotificationChildCount() > 0;
         if (mIsSummaryWithChildren && mChildrenContainer.getHeaderView() == null) {
@@ -2361,7 +2364,7 @@
         // If this is a summary, then add in the children notification channels for the
         // same user and pkg.
         if (mIsSummaryWithChildren) {
-            final List<ExpandableNotificationRow> childrenRows = getNotificationChildren();
+            final List<ExpandableNotificationRow> childrenRows = getAttachedChildren();
             final int numChildren = childrenRows.size();
             for (int i = 0; i < numChildren; i++) {
                 final ExpandableNotificationRow childRow = childrenRows.get(i);
@@ -2468,7 +2471,7 @@
         mHideSensitiveForIntrinsicHeight = hideSensitive;
         if (mIsSummaryWithChildren) {
             List<ExpandableNotificationRow> notificationChildren =
-                    mChildrenContainer.getNotificationChildren();
+                    mChildrenContainer.getAttachedChildren();
             for (int i = 0; i < notificationChildren.size(); i++) {
                 ExpandableNotificationRow child = notificationChildren.get(i);
                 child.setHideSensitiveForIntrinsicHeight(hideSensitive);
@@ -2806,7 +2809,7 @@
         updateBackgroundForGroupState();
         if (mIsSummaryWithChildren) {
             List<ExpandableNotificationRow> notificationChildren =
-                    mChildrenContainer.getNotificationChildren();
+                    mChildrenContainer.getAttachedChildren();
             for (int i = 0; i < notificationChildren.size(); i++) {
                 ExpandableNotificationRow child = notificationChildren.get(i);
                 child.updateBackgroundForGroupState();
@@ -2831,7 +2834,7 @@
             mShowNoBackground = !mShowGroupBackgroundWhenExpanded && isGroupExpanded()
                     && !isGroupExpansionChanging() && !isUserLocked();
             mChildrenContainer.updateHeaderForExpansion(mShowNoBackground);
-            List<ExpandableNotificationRow> children = mChildrenContainer.getNotificationChildren();
+            List<ExpandableNotificationRow> children = mChildrenContainer.getAttachedChildren();
             for (int i = 0; i < children.size(); i++) {
                 children.get(i).updateBackgroundForGroupState();
             }
@@ -3241,7 +3244,7 @@
             pw.print(", alpha: " + mChildrenContainer.getAlpha());
             pw.print(", translationY: " + mChildrenContainer.getTranslationY());
             pw.println();
-            List<ExpandableNotificationRow> notificationChildren = getNotificationChildren();
+            List<ExpandableNotificationRow> notificationChildren = getAttachedChildren();
             pw.println("  Children: " + notificationChildren.size());
             pw.println("  {");
             for(ExpandableNotificationRow child : notificationChildren) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index ee3b753..5797944 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -590,7 +590,7 @@
         // handling reset for child notifications
         if (this instanceof ExpandableNotificationRow) {
             ExpandableNotificationRow row = (ExpandableNotificationRow) this;
-            List<ExpandableNotificationRow> children = row.getNotificationChildren();
+            List<ExpandableNotificationRow> children = row.getAttachedChildren();
             if (row.isSummaryWithChildren() && children != null) {
                 for (ExpandableNotificationRow childRow : children) {
                     childRow.resetViewState();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index 400e794..351a3ef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -65,7 +65,7 @@
     }.setDuration(200);
 
     private final List<View> mDividers = new ArrayList<>();
-    private final List<ExpandableNotificationRow> mChildren = new ArrayList<>();
+    private final List<ExpandableNotificationRow> mAttachedChildren = new ArrayList<>();
     private final HybridGroupManager mHybridGroupManager;
     private int mChildPadding;
     private int mDividerHeight;
@@ -105,6 +105,7 @@
     private int mTranslationForHeader;
     private int mCurrentHeaderTranslation = 0;
     private float mHeaderVisibleAmount = 1.0f;
+    private int mUntruncatedChildCount;
 
     public NotificationChildrenContainer(Context context) {
         this(context, null);
@@ -153,9 +154,10 @@
 
     @Override
     protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        int childCount = Math.min(mChildren.size(), NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED);
+        int childCount =
+                Math.min(mAttachedChildren.size(), NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED);
         for (int i = 0; i < childCount; i++) {
-            View child = mChildren.get(i);
+            View child = mAttachedChildren.get(i);
             // We need to layout all children even the GONE ones, such that the heights are
             // calculated correctly as they are used to calculate how many we can fit on the screen
             child.layout(0, 0, child.getMeasuredWidth(), child.getMeasuredHeight());
@@ -195,11 +197,12 @@
         }
         int dividerHeightSpec = MeasureSpec.makeMeasureSpec(mDividerHeight, MeasureSpec.EXACTLY);
         int height = mNotificationHeaderMargin + mNotificatonTopPadding;
-        int childCount = Math.min(mChildren.size(), NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED);
+        int childCount =
+                Math.min(mAttachedChildren.size(), NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED);
         int collapsedChildren = getMaxAllowedVisibleChildren(true /* likeCollapsed */);
         int overflowIndex = childCount > collapsedChildren ? collapsedChildren - 1 : -1;
         for (int i = 0; i < childCount; i++) {
-            ExpandableNotificationRow child = mChildren.get(i);
+            ExpandableNotificationRow child = mAttachedChildren.get(i);
             // We need to measure all children even the GONE ones, such that the heights are
             // calculated correctly as they are used to calculate how many we can fit on the screen.
             boolean isOverflow = i == overflowIndex;
@@ -242,14 +245,24 @@
     }
 
     /**
+     * Set the untruncated number of children in the group so that the view can update the UI
+     * appropriately. Note that this may differ from the number of views attached as truncated
+     * children will not have views.
+     */
+    public void setUntruncatedChildCount(int childCount) {
+        mUntruncatedChildCount = childCount;
+        updateGroupOverflow();
+    }
+
+    /**
      * Add a child notification to this view.
      *
      * @param row the row to add
      * @param childIndex the index to add it at, if -1 it will be added at the end
      */
     public void addNotification(ExpandableNotificationRow row, int childIndex) {
-        int newIndex = childIndex < 0 ? mChildren.size() : childIndex;
-        mChildren.add(newIndex, row);
+        int newIndex = childIndex < 0 ? mAttachedChildren.size() : childIndex;
+        mAttachedChildren.add(newIndex, row);
         addView(row);
         row.setUserLocked(mUserLocked);
 
@@ -257,7 +270,6 @@
         addView(divider);
         mDividers.add(newIndex, divider);
 
-        updateGroupOverflow();
         row.setContentTransformationAmount(0, false /* isLastChild */);
         // It doesn't make sense to keep old animations around, lets cancel them!
         ExpandableViewState viewState = row.getViewState();
@@ -268,8 +280,8 @@
     }
 
     public void removeNotification(ExpandableNotificationRow row) {
-        int childIndex = mChildren.indexOf(row);
-        mChildren.remove(row);
+        int childIndex = mAttachedChildren.indexOf(row);
+        mAttachedChildren.remove(row);
         removeView(row);
 
         final View divider = mDividers.remove(childIndex);
@@ -284,7 +296,6 @@
 
         row.setSystemChildExpanded(false);
         row.setUserLocked(false);
-        updateGroupOverflow();
         if (!row.isRemoved()) {
             mHeaderUtil.restoreNotificationHeader(row);
         }
@@ -294,7 +305,7 @@
      * @return The number of notification children in the container.
      */
     public int getNotificationChildCount() {
-        return mChildren.size();
+        return mAttachedChildren.size();
     }
 
     public void recreateNotificationHeader(OnClickListener listener) {
@@ -364,10 +375,9 @@
     }
 
     public void updateGroupOverflow() {
-        int childCount = mChildren.size();
         int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren(true /* likeCollapsed */);
-        if (childCount > maxAllowedVisibleChildren) {
-            int number = childCount - maxAllowedVisibleChildren;
+        if (mUntruncatedChildCount > maxAllowedVisibleChildren) {
+            int number = mUntruncatedChildCount - maxAllowedVisibleChildren;
             mOverflowNumber = mHybridGroupManager.bindOverflowNumber(mOverflowNumber, number);
             if (mGroupOverFlowState == null) {
                 mGroupOverFlowState = new ViewState();
@@ -401,8 +411,11 @@
                 R.layout.notification_children_divider, this, false);
     }
 
-    public List<ExpandableNotificationRow> getNotificationChildren() {
-        return mChildren;
+    /**
+     * Get notification children that are attached currently.
+     */
+    public List<ExpandableNotificationRow> getAttachedChildren() {
+        return mAttachedChildren;
     }
 
     /**
@@ -420,13 +433,13 @@
             return false;
         }
         boolean result = false;
-        for (int i = 0; i < mChildren.size() && i < childOrder.size(); i++) {
-            ExpandableNotificationRow child = mChildren.get(i);
+        for (int i = 0; i < mAttachedChildren.size() && i < childOrder.size(); i++) {
+            ExpandableNotificationRow child = mAttachedChildren.get(i);
             ExpandableNotificationRow desiredChild = (ExpandableNotificationRow) childOrder.get(i);
             if (child != desiredChild) {
                 if (visualStabilityManager.canReorderNotification(desiredChild)) {
-                    mChildren.remove(desiredChild);
-                    mChildren.add(i, desiredChild);
+                    mAttachedChildren.remove(desiredChild);
+                    mAttachedChildren.add(i, desiredChild);
                     result = true;
                 } else {
                     visualStabilityManager.addReorderingAllowedCallback(callback);
@@ -442,9 +455,9 @@
             // we don't modify it the group is expanded or if we are expanding it
             return;
         }
-        int size = mChildren.size();
+        int size = mAttachedChildren.size();
         for (int i = 0; i < size; i++) {
-            ExpandableNotificationRow child = mChildren.get(i);
+            ExpandableNotificationRow child = mAttachedChildren.get(i);
             child.setSystemChildExpanded(i == 0 && size == 1);
         }
     }
@@ -468,7 +481,7 @@
         }
         int intrinsicHeight = mNotificationHeaderMargin + mCurrentHeaderTranslation;
         int visibleChildren = 0;
-        int childCount = mChildren.size();
+        int childCount = mAttachedChildren.size();
         boolean firstChild = true;
         float expandFactor = 0;
         if (mUserLocked) {
@@ -499,7 +512,7 @@
                 }
                 firstChild = false;
             }
-            ExpandableNotificationRow child = mChildren.get(i);
+            ExpandableNotificationRow child = mAttachedChildren.get(i);
             intrinsicHeight += child.getIntrinsicHeight();
             visibleChildren++;
         }
@@ -518,7 +531,7 @@
      * @param ambientState the ambient state containing ambient information
      */
     public void updateState(ExpandableViewState parentState, AmbientState ambientState) {
-        int childCount = mChildren.size();
+        int childCount = mAttachedChildren.size();
         int yPosition = mNotificationHeaderMargin + mCurrentHeaderTranslation;
         boolean firstChild = true;
         int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren();
@@ -535,7 +548,7 @@
                 && !mContainingNotification.isGroupExpansionChanging();
         int launchTransitionCompensation = 0;
         for (int i = 0; i < childCount; i++) {
-            ExpandableNotificationRow child = mChildren.get(i);
+            ExpandableNotificationRow child = mAttachedChildren.get(i);
             if (!firstChild) {
                 if (expandingToExpandedGroup) {
                     yPosition += NotificationUtils.interpolate(mChildPadding, mDividerHeight,
@@ -586,7 +599,7 @@
 
         }
         if (mOverflowNumber != null) {
-            ExpandableNotificationRow overflowView = mChildren.get(Math.min(
+            ExpandableNotificationRow overflowView = mAttachedChildren.get(Math.min(
                     getMaxAllowedVisibleChildren(true /* likeCollapsed */), childCount) - 1);
             mGroupOverFlowState.copyFrom(overflowView.getViewState());
 
@@ -672,7 +685,7 @@
 
     /** Applies state to children. */
     public void applyState() {
-        int childCount = mChildren.size();
+        int childCount = mAttachedChildren.size();
         ViewState tmpState = new ViewState();
         float expandFraction = 0.0f;
         if (mUserLocked) {
@@ -683,7 +696,7 @@
                 || (mContainingNotification.isGroupExpansionChanging()
                 && !mHideDividersDuringExpand);
         for (int i = 0; i < childCount; i++) {
-            ExpandableNotificationRow child = mChildren.get(i);
+            ExpandableNotificationRow child = mAttachedChildren.get(i);
             ExpandableViewState viewState = child.getViewState();
             viewState.applyToView(child);
 
@@ -716,10 +729,10 @@
         if (mContainingNotification.hasExpandingChild()) {
             return;
         }
-        int childCount = mChildren.size();
+        int childCount = mAttachedChildren.size();
         int layoutEnd = mContainingNotification.getActualHeight() - mClipBottomAmount;
         for (int i = 0; i < childCount; i++) {
-            ExpandableNotificationRow child = mChildren.get(i);
+            ExpandableNotificationRow child = mAttachedChildren.get(i);
             if (child.getVisibility() == GONE) {
                 continue;
             }
@@ -754,7 +767,7 @@
 
     /** Animate to a given state. */
     public void startAnimationToState(AnimationProperties properties) {
-        int childCount = mChildren.size();
+        int childCount = mAttachedChildren.size();
         ViewState tmpState = new ViewState();
         float expandFraction = getGroupExpandFraction();
         final boolean dividersVisible = mUserLocked && !showingAsLowPriority()
@@ -762,7 +775,7 @@
                 || (mContainingNotification.isGroupExpansionChanging()
                 && !mHideDividersDuringExpand);
         for (int i = childCount - 1; i >= 0; i--) {
-            ExpandableNotificationRow child = mChildren.get(i);
+            ExpandableNotificationRow child = mAttachedChildren.get(i);
             ExpandableViewState viewState = child.getViewState();
             viewState.animateTo(child, properties);
 
@@ -799,9 +812,9 @@
 
     public ExpandableNotificationRow getViewAtPosition(float y) {
         // find the view under the pointer, accounting for GONE views
-        final int count = mChildren.size();
+        final int count = mAttachedChildren.size();
         for (int childIdx = 0; childIdx < count; childIdx++) {
-            ExpandableNotificationRow slidingChild = mChildren.get(childIdx);
+            ExpandableNotificationRow slidingChild = mAttachedChildren.get(childIdx);
             float childTop = slidingChild.getTranslationY();
             float top = childTop + slidingChild.getClipTopAmount();
             float bottom = childTop + slidingChild.getActualHeight();
@@ -818,9 +831,9 @@
         if (mNotificationHeader != null) {
             mNotificationHeader.setExpanded(childrenExpanded);
         }
-        final int count = mChildren.size();
+        final int count = mAttachedChildren.size();
         for (int childIdx = 0; childIdx < count; childIdx++) {
-            ExpandableNotificationRow child = mChildren.get(childIdx);
+            ExpandableNotificationRow child = mAttachedChildren.get(childIdx);
             child.setChildrenExpanded(childrenExpanded, false);
         }
         updateHeaderTouchability();
@@ -919,12 +932,12 @@
     private void startChildAlphaAnimations(boolean toVisible) {
         float target = toVisible ? 1.0f : 0.0f;
         float start = 1.0f - target;
-        int childCount = mChildren.size();
+        int childCount = mAttachedChildren.size();
         for (int i = 0; i < childCount; i++) {
             if (i >= NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED) {
                 break;
             }
-            ExpandableNotificationRow child = mChildren.get(i);
+            ExpandableNotificationRow child = mAttachedChildren.get(i);
             child.setAlpha(start);
             ViewState viewState = new ViewState();
             viewState.initFrom(child);
@@ -979,12 +992,12 @@
         int maxContentHeight = mNotificationHeaderMargin + mCurrentHeaderTranslation
                 + mNotificatonTopPadding;
         int visibleChildren = 0;
-        int childCount = mChildren.size();
+        int childCount = mAttachedChildren.size();
         for (int i = 0; i < childCount; i++) {
             if (visibleChildren >= NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED) {
                 break;
             }
-            ExpandableNotificationRow child = mChildren.get(i);
+            ExpandableNotificationRow child = mAttachedChildren.get(i);
             float childHeight = child.isExpanded(true /* allowOnKeyguard */)
                     ? child.getMaxExpandHeight()
                     : child.getShowingLayout().getMinHeight(true /* likeGroupExpanded */);
@@ -1006,9 +1019,9 @@
         boolean showingLowPriority = showingAsLowPriority();
         updateHeaderTransformation();
         int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren(true /* forceCollapsed */);
-        int childCount = mChildren.size();
+        int childCount = mAttachedChildren.size();
         for (int i = 0; i < childCount; i++) {
-            ExpandableNotificationRow child = mChildren.get(i);
+            ExpandableNotificationRow child = mAttachedChildren.get(i);
             float childHeight;
             if (showingLowPriority) {
                 childHeight = child.getShowingLayout().getMinHeight(false /* likeGroupExpanded */);
@@ -1042,13 +1055,13 @@
         int intrinsicHeight = mNotificationHeaderMargin + mCurrentHeaderTranslation
                 + mNotificatonTopPadding + mDividerHeight;
         int visibleChildren = 0;
-        int childCount = mChildren.size();
+        int childCount = mAttachedChildren.size();
         int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren(true /* forceCollapsed */);
         for (int i = 0; i < childCount; i++) {
             if (visibleChildren >= maxAllowedVisibleChildren) {
                 break;
             }
-            ExpandableNotificationRow child = mChildren.get(i);
+            ExpandableNotificationRow child = mAttachedChildren.get(i);
             float childHeight = child.isExpanded(true /* allowOnKeyguard */)
                     ? child.getMaxExpandHeight()
                     : child.getShowingLayout().getMinHeight(true /* likeGroupExpanded */);
@@ -1097,7 +1110,7 @@
         int minExpandHeight = mNotificationHeaderMargin + headerTranslation;
         int visibleChildren = 0;
         boolean firstChild = true;
-        int childCount = mChildren.size();
+        int childCount = mAttachedChildren.size();
         for (int i = 0; i < childCount; i++) {
             if (visibleChildren >= maxAllowedVisibleChildren) {
                 break;
@@ -1107,7 +1120,7 @@
             } else {
                 firstChild = false;
             }
-            ExpandableNotificationRow child = mChildren.get(i);
+            ExpandableNotificationRow child = mAttachedChildren.get(i);
             minExpandHeight += child.getSingleLineView().getHeight();
             visibleChildren++;
         }
@@ -1149,9 +1162,9 @@
         if (!mUserLocked) {
             updateHeaderVisibility(false /* animate */);
         }
-        int childCount = mChildren.size();
+        int childCount = mAttachedChildren.size();
         for (int i = 0; i < childCount; i++) {
-            ExpandableNotificationRow child = mChildren.get(i);
+            ExpandableNotificationRow child = mAttachedChildren.get(i);
             child.setUserLocked(userLocked && !showingAsLowPriority());
         }
         updateHeaderTouchability();
@@ -1172,8 +1185,8 @@
         int position = mNotificationHeaderMargin + mCurrentHeaderTranslation
                 + mNotificatonTopPadding;
 
-        for (int i = 0; i < mChildren.size(); i++) {
-            ExpandableNotificationRow child = mChildren.get(i);
+        for (int i = 0; i < mAttachedChildren.size(); i++) {
+            ExpandableNotificationRow child = mAttachedChildren.get(i);
             boolean notGone = child.getVisibility() != View.GONE;
             if (notGone) {
                 position += mDividerHeight;
@@ -1251,8 +1264,8 @@
 
     public void setCurrentBottomRoundness(float currentBottomRoundness) {
         boolean last = true;
-        for (int i = mChildren.size() - 1; i >= 0; i--) {
-            ExpandableNotificationRow child = mChildren.get(i);
+        for (int i = mAttachedChildren.size() - 1; i >= 0; i--) {
+            ExpandableNotificationRow child = mAttachedChildren.get(i);
             if (child.getVisibility() == View.GONE) {
                 continue;
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListItem.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListItem.java
index 8991abe..c2dd229 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListItem.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListItem.java
@@ -43,7 +43,7 @@
 
     // This generic is kind of ugly - we should change this once the old VHM is gone
     /** @return list of the children of this item */
-    List<? extends NotificationListItem> getNotificationChildren();
+    List<? extends NotificationListItem> getAttachedChildren();
 
     /** remove all children from this list item */
     void removeAllChildren();
@@ -54,6 +54,9 @@
     /** add an item as a child */
     void addChildNotification(NotificationListItem child, int childIndex);
 
+    /** set the child count view should display */
+    void setUntruncatedChildCount(int count);
+
     /** Update the order of the children with the new list */
     boolean applyChildOrder(
             List<? extends NotificationListItem> childOrderList,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 6054b50..5655d8f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -2377,7 +2377,7 @@
                 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
                 if (row.isSummaryWithChildren() && row.areChildrenExpanded()) {
                     List<ExpandableNotificationRow> notificationChildren =
-                            row.getNotificationChildren();
+                            row.getAttachedChildren();
                     for (int childIndex = 0; childIndex < notificationChildren.size();
                             childIndex++) {
                         ExpandableNotificationRow rowChild = notificationChildren.get(childIndex);
@@ -4638,7 +4638,7 @@
                 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
                 row.setHeadsUpAnimatingAway(false);
                 if (row.isSummaryWithChildren()) {
-                    for (ExpandableNotificationRow child : row.getNotificationChildren()) {
+                    for (ExpandableNotificationRow child : row.getAttachedChildren()) {
                         child.setHeadsUpAnimatingAway(false);
                     }
                 }
@@ -5598,7 +5598,7 @@
                         && (!hasClipBounds || mTmpRect.height() > 0)) {
                     parentVisible = true;
                 }
-                List<ExpandableNotificationRow> children = row.getNotificationChildren();
+                List<ExpandableNotificationRow> children = row.getAttachedChildren();
                 if (children != null) {
                     for (ExpandableNotificationRow childRow : children) {
                         if (includeChildInDismissAll(row, selection)) {
@@ -6388,7 +6388,7 @@
                 if (parent != null && parent.areChildrenExpanded()
                         && (parent.areGutsExposed()
                         || mSwipeHelper.getExposedMenuView() == parent
-                        || (parent.getNotificationChildren().size() == 1
+                        || (parent.getAttachedChildren().size() == 1
                         && parent.getEntry().isClearable()))) {
                     // In this case the group is expanded and showing the menu for the
                     // group, further interaction should apply to the group, not any
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 9646c01..1a15377 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -297,7 +297,7 @@
                     ExpandableNotificationRow row = (ExpandableNotificationRow) v;
 
                     // handle the notgoneIndex for the children as well
-                    List<ExpandableNotificationRow> children = row.getNotificationChildren();
+                    List<ExpandableNotificationRow> children = row.getAttachedChildren();
                     if (row.isSummaryWithChildren() && children != null) {
                         for (ExpandableNotificationRow childRow : children) {
                             if (childRow.getVisibility() != View.GONE) {