Fixed a bug where notification header could become empty

Also fixed a few more issues with the header util that could
lead to wrong states.

Change-Id: I95c3479f5d9e5221ee9e91120271e7957b887607
Fixes: 28295743
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index fa6995a..39f8e19 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -185,6 +185,11 @@
     public long when;
 
     /**
+     * The creation time of the notification
+     */
+    private long creationTime;
+
+    /**
      * The resource id of a drawable to use as the icon in the status bar.
      *
      * @deprecated Use {@link Builder#setSmallIcon(Icon)} instead.
@@ -1479,6 +1484,7 @@
     public Notification()
     {
         this.when = System.currentTimeMillis();
+        this.creationTime = System.currentTimeMillis();
         this.priority = PRIORITY_DEFAULT;
     }
 
@@ -1516,6 +1522,7 @@
         this.icon = icon;
         this.tickerText = tickerText;
         this.when = when;
+        this.creationTime = System.currentTimeMillis();
     }
 
     /**
@@ -1526,6 +1533,7 @@
         int version = parcel.readInt();
 
         when = parcel.readLong();
+        creationTime = parcel.readLong();
         if (parcel.readInt() != 0) {
             mSmallIcon = Icon.CREATOR.createFromParcel(parcel);
             if (mSmallIcon.getType() == Icon.TYPE_RESOURCE) {
@@ -1614,6 +1622,7 @@
      */
     public void cloneInto(Notification that, boolean heavy) {
         that.when = this.when;
+        that.creationTime = this.creationTime;
         that.mSmallIcon = this.mSmallIcon;
         that.number = this.number;
 
@@ -1794,6 +1803,7 @@
         parcel.writeInt(1);
 
         parcel.writeLong(when);
+        parcel.writeLong(creationTime);
         if (mSmallIcon == null && icon != 0) {
             // you snuck an icon in here without using the builder; let's try to keep it
             mSmallIcon = Icon.createWithResource("", icon);
@@ -3129,6 +3139,7 @@
             contentView.setViewVisibility(R.id.header_text, View.GONE);
             contentView.setViewVisibility(R.id.header_text_divider, View.GONE);
             contentView.setViewVisibility(R.id.time_divider, View.GONE);
+            contentView.setViewVisibility(R.id.time, View.GONE);
             contentView.setImageViewIcon(R.id.profile_badge, null);
             contentView.setViewVisibility(R.id.profile_badge, View.GONE);
         }
@@ -3264,6 +3275,10 @@
                     contentView.setViewVisibility(R.id.time, View.VISIBLE);
                     contentView.setLong(R.id.time, "setTime", mN.when);
                 }
+            } else {
+                // We still want a time to be set but gone, such that we can show and hide it
+                // on demand in case it's a child notification without anything in the header
+                contentView.setLong(R.id.time, "setTime", mN.when != 0 ? mN.when : mN.creationTime);
             }
         }
 
@@ -3331,7 +3346,7 @@
          *         otherwise
          */
         private boolean showsTimeOrChronometer() {
-            return mN.when != 0 && mN.extras.getBoolean(EXTRA_SHOW_WHEN);
+            return mN.showsTimeOrChronometer();
         }
 
         private void resetStandardTemplateWithActions(RemoteViews big) {
@@ -3693,6 +3708,8 @@
                 mN.extras = getAllExtras();
             }
 
+            mN.creationTime = System.currentTimeMillis();
+
             // lazy stuff from mContext; see comment in Builder(Context, Notification)
             Notification.addFieldsFromContext(mContext, mN);
 
@@ -3826,6 +3843,15 @@
     }
 
     /**
+     * @return true if the notification will show the time or the chronometer; false
+     *         otherwise
+     * @hide
+     */
+    public boolean showsTimeOrChronometer() {
+        return when != 0 && extras.getBoolean(EXTRA_SHOW_WHEN);
+    }
+
+    /**
      * An object that can apply a rich notification style to a {@link Notification.Builder}
      * object.
      */
diff --git a/core/java/android/widget/DateTimeView.java b/core/java/android/widget/DateTimeView.java
index 39eba7d..d2ee866 100644
--- a/core/java/android/widget/DateTimeView.java
+++ b/core/java/android/widget/DateTimeView.java
@@ -131,8 +131,18 @@
         update();
     }
 
+    @Override
+    @android.view.RemotableViewMethod
+    public void setVisibility(@Visibility int visibility) {
+        boolean gotVisible = visibility != GONE && getVisibility() == GONE;
+        super.setVisibility(visibility);
+        if (gotVisible) {
+            update();
+        }
+    }
+
     void update() {
-        if (mTime == null) {
+        if (mTime == null || getVisibility() == GONE) {
             return;
         }
         if (mShowRelativeTime) {
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index 992e88e..d16dcc6 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -69,15 +69,17 @@
         android:text="@string/notification_header_divider_symbol"
         android:singleLine="true"
         android:visibility="gone"/>
-    <ViewStub
+    <DateTimeView
         android:id="@+id/time"
+        android:textAppearance="@style/TextAppearance.Material.Notification.Time"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
+        android:layout_gravity="center"
         android:layout_marginStart="2dp"
         android:layout_marginEnd="2dp"
-        android:layout="@layout/notification_template_part_time"
-        android:visibility="gone"
-        />
+        android:showRelative="true"
+        android:singleLine="true"
+        android:visibility="gone" />
     <ViewStub
         android:id="@+id/chronometer"
         android:layout_width="wrap_content"
diff --git a/core/res/res/layout/notification_template_part_time.xml b/core/res/res/layout/notification_template_part_time.xml
deleted file mode 100644
index e53f378..0000000
--- a/core/res/res/layout/notification_template_part_time.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 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.
--->
-
-<DateTimeView android:id="@+id/time" xmlns:android="http://schemas.android.com/apk/res/android"
-    android:textAppearance="@style/TextAppearance.Material.Notification.Time"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:layout_gravity="center"
-    android:layout_marginEnd="4dp"
-    android:layout_weight="0"
-    android:showRelative="true"
-    android:singleLine="true"
-    />
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
index 06d79a7..3363993 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
@@ -65,12 +65,7 @@
             ImageView expand = (ImageView) view.findViewById(
                     com.android.internal.R.id.expand_button);
             applyToChild(icon, apply, header.getOriginalIconColor());
-            int color = header.getOriginalNotificationColor();
-            if (color == Notification.COLOR_DEFAULT) {
-                color = view.getContext().getColor(
-                        com.android.internal.R.color.notification_icon_default_color);
-            }
-            applyToChild(expand, apply, color);
+            applyToChild(expand, apply, header.getOriginalNotificationColor());
         }
 
         private void applyToChild(View view, boolean shouldApply, int originalColor) {
@@ -116,7 +111,7 @@
                     @Override
                     public boolean compare(View parent, View child, Object parentData,
                             Object childData) {
-                        return parent.getVisibility() == View.VISIBLE;
+                        return parent.getVisibility() != View.GONE;
                     }
 
                     @Override
@@ -161,11 +156,11 @@
                 mComparators.get(compI).apply(row);
             }
             // We need to sanitize the dividers since they might be off-balance now
-            sanitizeDividers(row);
+            sanitizeHeaderViews(row);
         }
     }
 
-    private void sanitizeDividers(ExpandableNotificationRow row) {
+    private void sanitizeHeaderViews(ExpandableNotificationRow row) {
         if (row.isSummaryWithChildren()) {
             sanitizeHeader(row.getNotificationHeader());
             return;
@@ -188,9 +183,26 @@
         if (rowHeader == null) {
             return;
         }
+        final int childCount = rowHeader.getChildCount();
+        View time = rowHeader.findViewById(com.android.internal.R.id.time);
+        boolean hasVisibleText = false;
+        for (int i = 1; i < childCount - 1 ; i++) {
+            View child = rowHeader.getChildAt(i);
+            if (child instanceof TextView
+                    && child.getVisibility() != View.GONE
+                    && !mDividers.contains(Integer.valueOf(child.getId()))
+                    && child != time) {
+                hasVisibleText = true;
+                break;
+            }
+        }
+        // in case no view is visible we make sure the time is visible
+        int timeVisibility = !hasVisibleText
+                || mRow.getStatusBarNotification().getNotification().showsTimeOrChronometer()
+                ? View.VISIBLE : View.GONE;
+        time.setVisibility(timeVisibility);
         View left = null;
         View right;
-        final int childCount = rowHeader.getChildCount();
         for (int i = 1; i < childCount - 1 ; i++) {
             View child = rowHeader.getChildAt(i);
             if (mDividers.contains(Integer.valueOf(child.getId()))) {
@@ -202,14 +214,14 @@
                         // A divider was found, this needs to be hidden
                         i--;
                         break;
-                    } else if (right.getVisibility() == View.VISIBLE) {
+                    } else if (right.getVisibility() != View.GONE && right instanceof TextView) {
                         visible = left != null;
                         left = right;
                         break;
                     }
                 }
                 child.setVisibility(visible ? View.VISIBLE : View.GONE);
-            } else if (child.getVisibility() == View.VISIBLE) {
+            } else if (child.getVisibility() != View.GONE && child instanceof TextView) {
                 left = child;
             }
         }
@@ -219,7 +231,7 @@
         for (int compI = 0; compI < mComparators.size(); compI++) {
             mComparators.get(compI).apply(row, true /* reset */);
         }
-        sanitizeDividers(row);
+        sanitizeHeaderViews(row);
     }
 
     private static class HeaderProcessor {