Merge changes from topic 'more_groups'

* changes:
  Fixed a bug where the heads up had the wrong size
  Adapted the appear animation for notification children
  Added darkmode for notification groups
  And suddenly notification children appeared
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 4e812aa..8737d43 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -45,8 +45,11 @@
     <!-- Height of a large notification in the status bar -->
     <dimen name="notification_max_height">276dp</dimen>
 
-    <!-- Height of a medium notification in the status bar -->
-    <dimen name="notification_mid_height">128dp</dimen>
+    <!-- Height of a heads up notification in the status bar for legacy custom views -->
+    <dimen name="notification_max_heads_up_height_legacy">128dp</dimen>
+
+    <!-- Height of a heads up notification in the status bar -->
+    <dimen name="notification_max_heads_up_height">140dp</dimen>
 
     <!-- Height of a the summary ("more card") notification on keyguard. -->
     <dimen name="notification_summary_height">44dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java b/packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java
index 5b8d3d6..c9ba885 100644
--- a/packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java
@@ -19,6 +19,7 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
+import android.content.Context;
 import android.graphics.ColorMatrix;
 import android.graphics.ColorMatrixColorFilter;
 import android.graphics.Paint;
@@ -35,13 +36,19 @@
 
     private final Paint mDarkPaint = new Paint();
     private final Interpolator mLinearOutSlowInInterpolator;
-    private final ArrayList<View> mTargets;
     private final ColorMatrix mMatrix = new ColorMatrix();
     private final ColorMatrix mGrayscaleMatrix = new ColorMatrix();
     private final long mFadeDuration;
+    private final ArrayList<View> mTargets = new ArrayList<>();
 
-    public ViewInvertHelper(View target, long fadeDuration) {
-        this(constructArray(target), fadeDuration);
+    public ViewInvertHelper(View v, long fadeDuration) {
+        this(v.getContext(), fadeDuration);
+        addTarget(v);
+    }
+    public ViewInvertHelper(Context context, long fadeDuration) {
+        mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
+                android.R.interpolator.linear_out_slow_in);
+        mFadeDuration = fadeDuration;
     }
 
     private static ArrayList<View> constructArray(View target) {
@@ -50,11 +57,12 @@
         return views;
     }
 
-    public ViewInvertHelper(ArrayList<View> targets, long fadeDuration) {
-        mTargets = targets;
-        mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(mTargets.get(0).getContext(),
-                android.R.interpolator.linear_out_slow_in);
-        mFadeDuration = fadeDuration;
+    public void clearTargets() {
+        mTargets.clear();
+    }
+
+    public void addTarget(View target) {
+        mTargets.add(target);
     }
 
     public void fade(final boolean invert, long delay) {
@@ -112,4 +120,12 @@
         mMatrix.preConcat(mGrayscaleMatrix);
         mDarkPaint.setColorFilter(new ColorMatrixColorFilter(mMatrix));
     }
+
+    public void setInverted(boolean invert, boolean fade, long delay) {
+        if (fade) {
+            fade(invert, delay);
+        } else {
+            update(invert);
+        }
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 879624e..b1847e1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -127,8 +127,8 @@
 
     public static final boolean ENABLE_REMOTE_INPUT =
             SystemProperties.getBoolean("debug.enable_remote_input", true);
-    public static final boolean ENABLE_CHILD_NOTIFICATIONS = Build.IS_DEBUGGABLE
-                    && SystemProperties.getBoolean("debug.child_notifs", false);
+    public static final boolean ENABLE_CHILD_NOTIFICATIONS
+            = SystemProperties.getBoolean("debug.child_notifs", true);
 
     protected static final int MSG_SHOW_RECENT_APPS = 1019;
     protected static final int MSG_HIDE_RECENT_APPS = 1020;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 5c79c7d..2b93554 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -51,6 +51,8 @@
     private static final int COLORED_DIVIDER_ALPHA = 0x7B;
     private final LinearInterpolator mLinearInterpolator = new LinearInterpolator();
     private final int mNotificationMinHeightLegacy;
+    private final int mMaxHeadsUpHeightLegacy;
+    private final int mMaxHeadsUpHeight;
     private final int mNotificationMinHeight;
     private final int mNotificationMaxHeight;
     private int mRowMinHeight;
@@ -95,6 +97,7 @@
     private boolean mIsHeadsUp;
     private boolean mLastChronometerRunning = true;
     private NotificationHeaderView mNotificationHeader;
+    private NotificationViewWrapper mNotificationHeaderWrapper;
     private ViewStub mChildrenContainerStub;
     private NotificationGroupManager mGroupManager;
     private boolean mChildrenExpanded;
@@ -218,10 +221,15 @@
         boolean beforeN = mEntry.targetSdk < Build.VERSION_CODES.N;
         int minHeight = customView && beforeN && !mIsSummaryWithChildren ?
                 mNotificationMinHeightLegacy : mNotificationMinHeight;
+        boolean headsUpCustom = getPrivateLayout().getHeadsUpChild() != null &&
+                getPrivateLayout().getHeadsUpChild().getId()
+                != com.android.internal.R.id.status_bar_latest_event_content;
+        int headsUpheight = headsUpCustom && beforeN ? mMaxHeadsUpHeightLegacy
+                : mMaxHeadsUpHeight;
         mRowMinHeight = minHeight;
         mMaxViewHeight = mNotificationMaxHeight;
-        mPrivateLayout.setSmallHeight(mRowMinHeight);
-        mPublicLayout.setSmallHeight(mRowMinHeight);
+        mPrivateLayout.setHeights(mRowMinHeight, headsUpheight);
+        mPublicLayout.setHeights(mRowMinHeight, headsUpheight);
     }
 
     public StatusBarNotification getStatusBarNotification() {
@@ -385,6 +393,9 @@
     }
 
     public int getHeadsUpHeight() {
+        if (mIsSummaryWithChildren) {
+            return mChildrenContainer.getIntrinsicHeight();
+        }
         return mHeadsUpHeight;
     }
 
@@ -462,6 +473,10 @@
                 R.dimen.notification_min_height);
         mNotificationMaxHeight =  getResources().getDimensionPixelSize(
                 R.dimen.notification_max_height);
+        mMaxHeadsUpHeightLegacy =  getResources().getDimensionPixelSize(
+                R.dimen.notification_max_heads_up_height_legacy);
+        mMaxHeadsUpHeight =  getResources().getDimensionPixelSize(
+                R.dimen.notification_max_heads_up_height);
     }
 
     /**
@@ -570,6 +585,10 @@
         if (showing != null) {
             showing.setDark(dark, fade, delay);
         }
+        if (mIsSummaryWithChildren) {
+            mChildrenContainer.setDark(dark, fade, delay);
+            mNotificationHeaderWrapper.setDark(dark, fade, delay);
+        }
     }
 
     public boolean isExpandable() {
@@ -967,9 +986,12 @@
                     com.android.internal.R.id.expand_button);
             expandButton.setVisibility(VISIBLE);
             mNotificationHeader.setOnClickListener(mExpandClickListener);
+            mNotificationHeaderWrapper = NotificationViewWrapper.wrap(getContext(),
+                    mNotificationHeader);
             addView(mNotificationHeader);
         } else {
             header.reapply(getContext(), mNotificationHeader);
+            mNotificationHeaderWrapper.notifyContentUpdated();
         }
         updateHeaderExpandButton();
         updateChildrenHeaderAppearance();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index da01d54..2944c4f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -52,7 +52,6 @@
     private static final int VISIBLE_TYPE_SINGLELINE = 3;
 
     private final Rect mClipBounds = new Rect();
-    private final int mHeadsUpHeight;
     private final int mRoundRectRadius;
     private final Interpolator mLinearInterpolator = new LinearInterpolator();
     private final boolean mRoundRectClippingEnabled;
@@ -77,6 +76,7 @@
     private boolean mShowingLegacyBackground;
     private boolean mIsChildInGroup;
     private int mSmallHeight;
+    private int mHeadsUpHeight;
     private StatusBarNotification mStatusBarNotification;
     private NotificationGroupManager mGroupManager;
 
@@ -103,7 +103,6 @@
         super(context, attrs);
         mHybridViewManager = new HybridNotificationViewManager(getContext(), this);
         mFadePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD));
-        mHeadsUpHeight = getResources().getDimensionPixelSize(R.dimen.notification_mid_height);
         mRoundRectRadius = getResources().getDimensionPixelSize(
                 R.dimen.notification_material_rounded_rect_radius);
         mRoundRectClippingEnabled = getResources().getBoolean(
@@ -112,8 +111,9 @@
         setOutlineProvider(mOutlineProvider);
     }
 
-    public void setSmallHeight(int smallHeight) {
+    public void setHeights(int smallHeight, int headsUpMaxHeight) {
         mSmallHeight = smallHeight;
+        mHeadsUpHeight = headsUpMaxHeight;
     }
 
     @Override
@@ -150,7 +150,7 @@
             ViewGroup.LayoutParams layoutParams = mHeadsUpChild.getLayoutParams();
             if (layoutParams.height >= 0) {
                 // An actual height is set
-                size = Math.min(maxSize, layoutParams.height);
+                size = Math.min(size, layoutParams.height);
             }
             mHeadsUpChild.measure(widthMeasureSpec,
                     MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST));
@@ -283,10 +283,10 @@
     }
 
     public int getMaxHeight() {
-        if (mIsHeadsUp && mHeadsUpChild != null) {
-            return mHeadsUpChild.getHeight();
-        } else if (mExpandedChild != null) {
+        if (mExpandedChild != null) {
             return mExpandedChild.getHeight();
+        } else if (mIsHeadsUp && mHeadsUpChild != null) {
+            return mHeadsUpChild.getHeight();
         }
         return mSmallHeight;
     }
@@ -457,6 +457,9 @@
         if (mDark == dark || mContractedChild == null) return;
         mDark = dark;
         mContractedWrapper.setDark(dark && !mShowingLegacyBackground, fade, delay);
+        if (mSingleLineView != null) {
+            mSingleLineView.setDark(dark, fade, delay);
+        }
     }
 
     public void setHeadsUp(boolean headsUp) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderViewWrapper.java
new file mode 100644
index 0000000..ddad2e0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderViewWrapper.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.graphics.drawable.Drawable;
+import android.view.NotificationHeaderView;
+import android.view.View;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+import android.widget.ImageView;
+
+import com.android.systemui.R;
+import com.android.systemui.ViewInvertHelper;
+import com.android.systemui.statusbar.phone.NotificationPanelView;
+
+import java.util.ArrayList;
+
+/**
+ * Wraps a notification header view.
+ */
+public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
+
+    private final ColorMatrix mGrayscaleColorMatrix = new ColorMatrix();
+    private final PorterDuffColorFilter mIconColorFilter = new PorterDuffColorFilter(
+            0, PorterDuff.Mode.SRC_ATOP);
+    private final int mIconDarkAlpha;
+    private final int mIconDarkColor = 0xffffffff;
+    protected final Interpolator mLinearOutSlowInInterpolator;
+    protected final ViewInvertHelper mInvertHelper;
+
+    protected int mColor;
+    private ImageView mIcon;
+
+    private ImageView mExpandButton;
+    private NotificationHeaderView mNotificationHeader;
+
+    protected NotificationHeaderViewWrapper(Context ctx, View view) {
+        super(view);
+        mIconDarkAlpha = ctx.getResources().getInteger(R.integer.doze_small_icon_alpha);
+        mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(ctx,
+                android.R.interpolator.linear_out_slow_in);
+        mInvertHelper = new ViewInvertHelper(ctx, NotificationPanelView.DOZE_ANIMATION_DURATION);
+        resolveHeaderViews();
+    }
+
+    protected void resolveHeaderViews() {
+        mIcon = (ImageView) mView.findViewById(com.android.internal.R.id.icon);
+        mExpandButton = (ImageView) mView.findViewById(com.android.internal.R.id.expand_button);
+        mColor = resolveColor(mExpandButton);
+        mNotificationHeader = (NotificationHeaderView) mView.findViewById(
+                com.android.internal.R.id.notification_header);
+        for (int i = 0; i < mNotificationHeader.getChildCount(); i++) {
+            View child = mNotificationHeader.getChildAt(i);
+            if (child != mIcon) {
+                mInvertHelper.addTarget(child);
+            }
+        }
+    }
+
+    private int resolveColor(ImageView icon) {
+        if (icon != null && icon.getDrawable() != null) {
+            ColorFilter filter = icon.getDrawable().getColorFilter();
+            if (filter instanceof PorterDuffColorFilter) {
+                return ((PorterDuffColorFilter) filter).getColor();
+            }
+        }
+        return 0;
+    }
+
+    @Override
+    public void notifyContentUpdated() {
+        mInvertHelper.clearTargets();
+        // Reinspect the notification.
+        resolveHeaderViews();
+    }
+
+    @Override
+    public void setDark(boolean dark, boolean fade, long delay) {
+        if (fade) {
+            mInvertHelper.fade(dark, delay);
+        } else {
+            mInvertHelper.update(dark);
+        }
+        if (mIcon != null) {
+            boolean hadColorFilter = mNotificationHeader.getOriginalIconColor()
+                    != NotificationHeaderView.NO_COLOR;
+            if (fade) {
+                if (hadColorFilter) {
+                    fadeIconColorFilter(mIcon, dark, delay);
+                    fadeIconAlpha(mIcon, dark, delay);
+                } else {
+                    fadeGrayscale(mIcon, dark, delay);
+                }
+            } else {
+                if (hadColorFilter) {
+                    updateIconColorFilter(mIcon, dark);
+                    updateIconAlpha(mIcon, dark);
+                } else {
+                    updateGrayscale(mIcon, dark);
+                }
+            }
+        }
+    }
+
+    protected void startIntensityAnimation(ValueAnimator.AnimatorUpdateListener updateListener,
+            boolean dark, long delay, Animator.AnimatorListener listener) {
+        float startIntensity = dark ? 0f : 1f;
+        float endIntensity = dark ? 1f : 0f;
+        ValueAnimator animator = ValueAnimator.ofFloat(startIntensity, endIntensity);
+        animator.addUpdateListener(updateListener);
+        animator.setDuration(NotificationPanelView.DOZE_ANIMATION_DURATION);
+        animator.setInterpolator(mLinearOutSlowInInterpolator);
+        animator.setStartDelay(delay);
+        if (listener != null) {
+            animator.addListener(listener);
+        }
+        animator.start();
+    }
+
+    private void fadeIconColorFilter(final ImageView target, boolean dark, long delay) {
+        startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                updateIconColorFilter(target, (Float) animation.getAnimatedValue());
+            }
+        }, dark, delay, null /* listener */);
+    }
+
+    private void fadeIconAlpha(final ImageView target, boolean dark, long delay) {
+        startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                float t = (float) animation.getAnimatedValue();
+                target.setImageAlpha((int) (255 * (1f - t) + mIconDarkAlpha * t));
+            }
+        }, dark, delay, null /* listener */);
+    }
+
+    protected void fadeGrayscale(final ImageView target, final boolean dark, long delay) {
+        startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                updateGrayscaleMatrix((float) animation.getAnimatedValue());
+                target.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix));
+            }
+        }, dark, delay, new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                if (!dark) {
+                    target.setColorFilter(null);
+                }
+            }
+        });
+    }
+
+    private void updateIconColorFilter(ImageView target, boolean dark) {
+        updateIconColorFilter(target, dark ? 1f : 0f);
+    }
+
+    private void updateIconColorFilter(ImageView target, float intensity) {
+        int color = interpolateColor(mColor, mIconDarkColor, intensity);
+        mIconColorFilter.setColor(color);
+        Drawable iconDrawable = target.getDrawable();
+
+        // Also, the notification might have been modified during the animation, so background
+        // might be null here.
+        if (iconDrawable != null) {
+            iconDrawable.mutate().setColorFilter(mIconColorFilter);
+        }
+    }
+
+    private void updateIconAlpha(ImageView target, boolean dark) {
+        target.setImageAlpha(dark ? mIconDarkAlpha : 255);
+    }
+
+    protected void updateGrayscale(ImageView target, boolean dark) {
+        if (dark) {
+            updateGrayscaleMatrix(1f);
+            target.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix));
+        } else {
+            target.setColorFilter(null);
+        }
+    }
+
+    @Override
+    public void updateExpandability(boolean expandable, View.OnClickListener onClickListener) {
+        mExpandButton.setVisibility(expandable ? View.VISIBLE : View.GONE);
+        mNotificationHeader.setOnClickListener(expandable ? onClickListener : null);
+    }
+
+    private void updateGrayscaleMatrix(float intensity) {
+        mGrayscaleColorMatrix.setSaturation(1 - intensity);
+    }
+
+    private static int interpolateColor(int source, int target, float t) {
+        int aSource = Color.alpha(source);
+        int rSource = Color.red(source);
+        int gSource = Color.green(source);
+        int bSource = Color.blue(source);
+        int aTarget = Color.alpha(target);
+        int rTarget = Color.red(target);
+        int gTarget = Color.green(target);
+        int bTarget = Color.blue(target);
+        return Color.argb(
+                (int) (aSource * (1f - t) + aTarget * t),
+                (int) (rSource * (1f - t) + rTarget * t),
+                (int) (gSource * (1f - t) + gTarget * t),
+                (int) (bSource * (1f - t) + bTarget * t));
+    }
+
+    @Override
+    public NotificationHeaderView getNotificationHeader() {
+        return mNotificationHeader;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationTemplateViewWrapper.java
index fb0a419..77e8c55 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationTemplateViewWrapper.java
@@ -16,74 +16,31 @@
 
 package com.android.systemui.statusbar;
 
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.content.Context;
-import android.content.res.ColorStateList;
 import android.graphics.Color;
-import android.graphics.ColorFilter;
-import android.graphics.ColorMatrix;
-import android.graphics.ColorMatrixColorFilter;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffColorFilter;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.text.TextUtils;
-import android.view.MotionEvent;
-import android.view.NotificationHeaderView;
 import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.ViewGroup;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
 import android.widget.ImageView;
 import android.widget.ProgressBar;
-import android.widget.TextView;
-
-import com.android.systemui.R;
-import com.android.systemui.ViewInvertHelper;
-import com.android.systemui.statusbar.phone.NotificationPanelView;
-
-import java.util.ArrayList;
 
 /**
  * Wraps a notification view inflated from a template.
  */
-public class NotificationTemplateViewWrapper extends NotificationViewWrapper {
+public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapper {
 
-    private final ColorMatrix mGrayscaleColorMatrix = new ColorMatrix();
-    private final PorterDuffColorFilter mIconColorFilter = new PorterDuffColorFilter(
-            0, PorterDuff.Mode.SRC_ATOP);
-    private final int mIconDarkAlpha;
-    private final int mIconDarkColor = 0xffffffff;
-    private final int mDarkProgressTint = 0xffffffff;
-    private final Interpolator mLinearOutSlowInInterpolator;
+    private static final int mDarkProgressTint = 0xffffffff;
 
-    private int mColor;
-    private ViewInvertHelper mInvertHelper;
-    private ImageView mIcon;
     protected ImageView mPicture;
-
-    private ImageView mExpandButton;
-    private NotificationHeaderView mNotificationHeader;
     private ProgressBar mProgressBar;
 
     protected NotificationTemplateViewWrapper(Context ctx, View view) {
-        super(view);
-        mIconDarkAlpha = ctx.getResources().getInteger(R.integer.doze_small_icon_alpha);
-        mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(ctx,
-                android.R.interpolator.linear_out_slow_in);
-
-        resolveViews();
+        super(ctx, view);
+        resolveTemplateViews();
     }
 
-    private void resolveViews() {
+    private void resolveTemplateViews() {
         View mainColumn = mView.findViewById(com.android.internal.R.id.notification_main_column);
-        mIcon = (ImageView) mView.findViewById(com.android.internal.R.id.icon);
         mPicture = (ImageView) mView.findViewById(com.android.internal.R.id.right_icon);
-        mExpandButton = (ImageView) mView.findViewById(com.android.internal.R.id.expand_button);
-        mColor = resolveColor(mExpandButton);
         final View progress = mView.findViewById(com.android.internal.R.id.progress);
         if (progress instanceof ProgressBar) {
             mProgressBar = (ProgressBar) progress;
@@ -91,30 +48,9 @@
             // It's still a viewstub
             mProgressBar = null;
         }
-        mNotificationHeader = (NotificationHeaderView) mView.findViewById(
-                com.android.internal.R.id.notification_header);
-        ArrayList<View> viewsToInvert = new ArrayList<>();
         if (mainColumn != null) {
-            viewsToInvert.add(mainColumn);
+            mInvertHelper.addTarget(mainColumn);
         }
-        for (int i = 0; i < mNotificationHeader.getChildCount(); i++) {
-            View child = mNotificationHeader.getChildAt(i);
-            if (child != mIcon) {
-                viewsToInvert.add(child);
-            }
-        }
-        mInvertHelper = new ViewInvertHelper(viewsToInvert,
-                NotificationPanelView.DOZE_ANIMATION_DURATION);
-    }
-
-    private int resolveColor(ImageView icon) {
-        if (icon != null && icon.getDrawable() != null) {
-            ColorFilter filter = icon.getDrawable().getColorFilter();
-            if (filter instanceof PorterDuffColorFilter) {
-                return ((PorterDuffColorFilter) filter).getColor();
-            }
-        }
-        return 0;
     }
 
     @Override
@@ -122,37 +58,12 @@
         super.notifyContentUpdated();
 
         // Reinspect the notification.
-        resolveViews();
+        resolveTemplateViews();
     }
 
     @Override
     public void setDark(boolean dark, boolean fade, long delay) {
-        if (mInvertHelper != null) {
-            if (fade) {
-                mInvertHelper.fade(dark, delay);
-            } else {
-                mInvertHelper.update(dark);
-            }
-        }
-        if (mIcon != null) {
-            boolean hadColorFilter = mNotificationHeader.getOriginalIconColor()
-                    != NotificationHeaderView.NO_COLOR;
-            if (fade) {
-                if (hadColorFilter) {
-                    fadeIconColorFilter(mIcon, dark, delay);
-                    fadeIconAlpha(mIcon, dark, delay);
-                } else {
-                    fadeGrayscale(mIcon, dark, delay);
-                }
-            } else {
-                if (hadColorFilter) {
-                    updateIconColorFilter(mIcon, dark);
-                    updateIconAlpha(mIcon, dark);
-                } else {
-                    updateGrayscale(mIcon, dark);
-                }
-            }
-        }
+        super.setDark(dark, fade, delay);
         setPictureGrayscale(dark, fade, delay);
         setProgressBarDark(dark, fade, delay);
     }
@@ -197,96 +108,6 @@
         }
     }
 
-    private void startIntensityAnimation(ValueAnimator.AnimatorUpdateListener updateListener,
-            boolean dark, long delay, Animator.AnimatorListener listener) {
-        float startIntensity = dark ? 0f : 1f;
-        float endIntensity = dark ? 1f : 0f;
-        ValueAnimator animator = ValueAnimator.ofFloat(startIntensity, endIntensity);
-        animator.addUpdateListener(updateListener);
-        animator.setDuration(NotificationPanelView.DOZE_ANIMATION_DURATION);
-        animator.setInterpolator(mLinearOutSlowInInterpolator);
-        animator.setStartDelay(delay);
-        if (listener != null) {
-            animator.addListener(listener);
-        }
-        animator.start();
-    }
-
-    private void fadeIconColorFilter(final ImageView target, boolean dark, long delay) {
-        startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
-            @Override
-            public void onAnimationUpdate(ValueAnimator animation) {
-                updateIconColorFilter(target, (Float) animation.getAnimatedValue());
-            }
-        }, dark, delay, null /* listener */);
-    }
-
-    private void fadeIconAlpha(final ImageView target, boolean dark, long delay) {
-        startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
-            @Override
-            public void onAnimationUpdate(ValueAnimator animation) {
-                float t = (float) animation.getAnimatedValue();
-                target.setImageAlpha((int) (255 * (1f - t) + mIconDarkAlpha * t));
-            }
-        }, dark, delay, null /* listener */);
-    }
-
-    protected void fadeGrayscale(final ImageView target, final boolean dark, long delay) {
-        startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
-            @Override
-            public void onAnimationUpdate(ValueAnimator animation) {
-                updateGrayscaleMatrix((float) animation.getAnimatedValue());
-                target.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix));
-            }
-        }, dark, delay, new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                if (!dark) {
-                    target.setColorFilter(null);
-                }
-            }
-        });
-    }
-
-    private void updateIconColorFilter(ImageView target, boolean dark) {
-        updateIconColorFilter(target, dark ? 1f : 0f);
-    }
-
-    private void updateIconColorFilter(ImageView target, float intensity) {
-        int color = interpolateColor(mColor, mIconDarkColor, intensity);
-        mIconColorFilter.setColor(color);
-        Drawable iconDrawable = target.getDrawable();
-
-        // Also, the notification might have been modified during the animation, so background
-        // might be null here.
-        if (iconDrawable != null) {
-            iconDrawable.mutate().setColorFilter(mIconColorFilter);
-        }
-    }
-
-    private void updateIconAlpha(ImageView target, boolean dark) {
-        target.setImageAlpha(dark ? mIconDarkAlpha : 255);
-    }
-
-    protected void updateGrayscale(ImageView target, boolean dark) {
-        if (dark) {
-            updateGrayscaleMatrix(1f);
-            target.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix));
-        } else {
-            target.setColorFilter(null);
-        }
-    }
-
-    @Override
-    public void updateExpandability(boolean expandable, View.OnClickListener onClickListener) {
-        mExpandButton.setVisibility(expandable ? View.VISIBLE : View.GONE);
-        mNotificationHeader.setOnClickListener(expandable ? onClickListener : null);
-    }
-
-    private void updateGrayscaleMatrix(float intensity) {
-        mGrayscaleColorMatrix.setSaturation(1 - intensity);
-    }
-
     private static int interpolateColor(int source, int target, float t) {
         int aSource = Color.alpha(source);
         int rSource = Color.red(source);
@@ -302,9 +123,4 @@
                 (int) (gSource * (1f - t) + gTarget * t),
                 (int) (bSource * (1f - t) + bTarget * t));
     }
-
-    @Override
-    public NotificationHeaderView getNotificationHeader() {
-        return mNotificationHeader;
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewWrapper.java
index 119d57b..61499de 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewWrapper.java
@@ -26,16 +26,13 @@
  */
 public abstract class NotificationViewWrapper {
 
-    private static final String TAG_BIG_MEDIA_NARROW = "bigMediaNarrow";
-    private static final String TAG_MEDIA = "media";
-    private static final String TAG_BIG_PICTURE = "bigPicture";
-
     protected final View mView;
-    private boolean mSubTextVisible = true;
 
     public static NotificationViewWrapper wrap(Context ctx, View v) {
         if (v.getId() == com.android.internal.R.id.status_bar_latest_event_content) {
             return new NotificationTemplateViewWrapper(ctx, v);
+        } else if (v instanceof NotificationHeaderView) {
+            return new NotificationHeaderViewWrapper(ctx, v);
         } else {
             return new NotificationCustomViewWrapper(v);
         }
@@ -57,9 +54,7 @@
     /**
      * Notifies this wrapper that the content of the view might have changed.
      */
-    public void notifyContentUpdated() {
-        setSubTextVisible(mSubTextVisible);
-    }
+    public void notifyContentUpdated() {};
 
     /**
      * @return true if this template might need to be clipped with a round rect to make it look
@@ -70,14 +65,6 @@
     }
 
     /**
-     * Change the subTextVisibility
-     * @param visible Should the subtext be visible
-     */
-    public void setSubTextVisible(boolean visible) {
-        mSubTextVisible = visible;
-    }
-
-    /**
      * Update the appearance of the expand button.
      *
      * @param expandable should this view be expandable
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationView.java
index fafea98..5fb6fec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationView.java
@@ -23,6 +23,8 @@
 
 import com.android.keyguard.AlphaOptimizedLinearLayout;
 import com.android.systemui.R;
+import com.android.systemui.ViewInvertHelper;
+import com.android.systemui.statusbar.phone.NotificationPanelView;
 
 /**
  * A hybrid view which may contain information about one ore more notifications.
@@ -31,6 +33,7 @@
 
     protected TextView mTitleView;
     protected TextView mTextView;
+    private ViewInvertHelper mInvertHelper;
 
     public HybridNotificationView(Context context) {
         this(context, null);
@@ -54,6 +57,7 @@
         super.onFinishInflate();
         mTitleView = (TextView) findViewById(R.id.notification_title);
         mTextView = (TextView) findViewById(R.id.notification_text);
+        mInvertHelper = new ViewInvertHelper(this, NotificationPanelView.DOZE_ANIMATION_DURATION);
     }
 
     public void bind(CharSequence title) {
@@ -65,4 +69,8 @@
         mTextView.setText(text);
         requestLayout();
     }
+
+    public void setDark(boolean dark, boolean fade, long delay) {
+        mInvertHelper.setInverted(dark, fade, delay);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
index 9015a0e..2b71ce9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
@@ -23,9 +23,11 @@
 import android.view.ViewGroup;
 
 import com.android.systemui.R;
+import com.android.systemui.ViewInvertHelper;
 import com.android.systemui.statusbar.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.HybridNotificationView;
 import com.android.systemui.statusbar.notification.HybridNotificationViewManager;
+import com.android.systemui.statusbar.phone.NotificationPanelView;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -49,6 +51,7 @@
     private final int mNotificatonTopPadding;
     private final HybridNotificationViewManager mHybridViewManager;
     private final float mCollapsedBottompadding;
+    private ViewInvertHelper mOverflowInvertHelper;
     private boolean mChildrenExpanded;
     private ExpandableNotificationRow mNotificationParent;
     private HybridNotificationView mGroupOverflowContainer;
@@ -172,12 +175,17 @@
         if (hasOverflow) {
             mGroupOverflowContainer = mHybridViewManager.bindFromNotificationGroup(
                     mGroupOverflowContainer, mChildren, lastVisibleIndex + 1);
+            if (mOverflowInvertHelper == null) {
+                mOverflowInvertHelper= new ViewInvertHelper(mGroupOverflowContainer,
+                        NotificationPanelView.DOZE_ANIMATION_DURATION);
+            }
             if (mGroupOverFlowState == null) {
                 mGroupOverFlowState = new ViewState();
             }
         } else if (mGroupOverflowContainer != null) {
             removeView(mGroupOverflowContainer);
             mGroupOverflowContainer = null;
+            mOverflowInvertHelper = null;
             mGroupOverFlowState = null;
         }
     }
@@ -324,7 +332,7 @@
         if (!likeCollapsed && (mChildrenExpanded || mNotificationParent.isUserLocked())) {
             return NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED;
         }
-        if (mNotificationParent.isExpanded()) {
+        if (mNotificationParent.isExpanded() || mNotificationParent.isHeadsUp()) {
             return NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED;
         }
         return NUMBER_OF_CHILDREN_WHEN_COLLAPSED;
@@ -387,16 +395,17 @@
             boolean withDelays, long baseDelay, long duration) {
         int childCount = mChildren.size();
         ViewState tmpState = new ViewState();
-        int notGoneIndex = 0;
-        for (int i = 0; i < childCount; i++) {
+        int delayIndex = 0;
+        int maxAllowChildCount = getMaxAllowedVisibleChildren(true /* likeCollapsed */);
+        for (int i = childCount - 1; i >= 0; i--) {
             ExpandableNotificationRow child = mChildren.get(i);
             StackViewState viewState = state.getViewStateForView(child);
             int difference = Math.min(StackStateAnimator.DELAY_EFFECT_MAX_INDEX_DIFFERENCE_CHILDREN,
-                    notGoneIndex + 1);
+                    delayIndex);
             long delay = withDelays
                     ? difference * StackStateAnimator.ANIMATION_DELAY_PER_ELEMENT_EXPAND_CHILDREN
                     : 0;
-            delay += baseDelay;
+            delay = (long) (delay * (mChildrenExpanded ? 1.0f : 0.5f) + baseDelay);
             stateAnimator.startStackAnimations(child, viewState, state, -1, delay);
 
             // layout the divider
@@ -405,11 +414,13 @@
             tmpState.yTranslation = viewState.yTranslation - mDividerHeight;
             tmpState.alpha = mChildrenExpanded && viewState.alpha != 0 ? 0.5f : 0;
             stateAnimator.startViewAnimations(divider, tmpState, delay, duration);
-
-            notGoneIndex++;
+            if (i < maxAllowChildCount) {
+                delayIndex++;
+            }
         }
         if (mGroupOverflowContainer != null) {
-            stateAnimator.startViewAnimations(mGroupOverflowContainer, mGroupOverFlowState, -1, 0);
+            stateAnimator.startViewAnimations(mGroupOverflowContainer, mGroupOverFlowState,
+                    baseDelay, duration);
         }
     }
 
@@ -443,4 +454,10 @@
     public int getMinHeight() {
         return getIntrinsicHeight(getMaxAllowedVisibleChildren(true /* forceCollapsed */));
     }
+
+    public void setDark(boolean dark, boolean fade, long delay) {
+        if (mGroupOverflowContainer != null) {
+            mOverflowInvertHelper.setInverted(dark, fade, delay);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 33f39bc..c83012c 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -159,8 +159,8 @@
 public class NotificationManagerService extends SystemService {
     static final String TAG = "NotificationService";
     static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
-    public static final boolean ENABLE_CHILD_NOTIFICATIONS = Build.IS_DEBUGGABLE
-            && SystemProperties.getBoolean("debug.child_notifs", false);
+    public static final boolean ENABLE_CHILD_NOTIFICATIONS
+            = SystemProperties.getBoolean("debug.child_notifs", true);
 
     static final int MAX_PACKAGE_NOTIFICATIONS = 50;