Custom notification layouts and text colors

Here is solution for any SDK version using only resources.

res/values/styles.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="NotificationTitle">
      <item name="android:textColor">?android:attr/textColorPrimaryInverse</item>
      <item name="android:textStyle">bold</item>
    </style>
    <style name="NotificationText">
      <item name="android:textColor">?android:attr/textColorPrimaryInverse</item>
    </style>
</resources>

res/values-v9/styles.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="NotificationText" parent="android:TextAppearance.StatusBar.EventContent" />
    <style name="NotificationTitle" parent="android:TextAppearance.StatusBar.EventContent.Title" />
</resources>

res/layout/my_notification.xml

...
<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="title"
    style="@style/NotificationTitle"
    />
<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="text"
    style="@style/NotificationText"
    />
...

P.S: Hard coded values are used for 2.2-. So problems can occur with some rare old custom firmwares.


The solution is to use built-in styles. The style you need is called TextAppearance.StatusBar.EventContent in Android 2.3 and Android 4.x. In Android 5.x material notifications use several other styles: TextAppearance.Material.Notification, TextAppearance.Material.Notification.Title, and TextAppearance.Material.Notification.Line2. Just set the appropriate text appearance for the text view, and you will get the necessary colors.

If you are interested how I have arrived at this solution, here's my trail of breadcrumbs. The code excerpts are taken from Android 2.3.

  1. When you use Notification and set the text by using built-in means, the following line creates the layout:

    RemoteViews contentView = new RemoteViews(context.getPackageName(),
            com.android.internal.R.layout.status_bar_latest_event_content);
    
  2. The mentioned layout contains the following View which is responsible for viewing notification text:

    <TextView android:id="@+id/text"
        android:textAppearance="@style/TextAppearance.StatusBar.EventContent"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:singleLine="true"
        android:ellipsize="marquee"
        android:fadingEdge="horizontal"
        android:paddingLeft="4dp"
        />
    
  3. So the conclusion is that the needed style is TextAppearance.StatusBar.EventContent, which definition looks like this:

    <style name="TextAppearance.StatusBar.EventContent">
        <item name="android:textColor">#ff6b6b6b</item>
    </style>
    

    You should note here that this style doesn't actually reference any of the built-in colors, so the safest way is to apply this style instead of some built-in color.

One more thing: before Android 2.3 (API Level 9), there were neither styles, nor colors, there were only hard-coded values. If you happen to have to support such old versions for some reason, see the answer by Gaks .


For 2.3+ (From Android documentation):

Use the style android:TextAppearance.StatusBar.EventContent.Title for the primary text.

Use the style android:TextAppearance.StatusBar.EventContent for the secondary text.

For 2.2-, do what Gaks suggested in another answer to this thread.

If you want to compile against 2.2 and support 2.3+, and support all the variety of devices out there, Gaks' solution is the only one I know.

BTW, what Google suggested about using the value ?android:attr/textColorPrimary for 2.2-, isn't working. Just try it using the emulator. Gaks' way is the only way.

More resources: This and this do not work.


Solution by Malcolm works fine with API>=9. Here's the solution for older API:

The trick is to create the standard notification object and then traverse the default contentView created by Notification.setLatestEventInfo(...). When you find the right TextView, just get the tv.getTextColors().getDefaultColor().

Here's the code that extracts the default text color and text size (in scaled density pixels - sp).

private Integer notification_text_color = null;
private float notification_text_size = 11;
private final String COLOR_SEARCH_RECURSE_TIP = "SOME_SAMPLE_TEXT";

private boolean recurseGroup(ViewGroup gp)
{
    final int count = gp.getChildCount();
    for (int i = 0; i < count; ++i)
    {
        if (gp.getChildAt(i) instanceof TextView)
        {
            final TextView text = (TextView) gp.getChildAt(i);
            final String szText = text.getText().toString();
            if (COLOR_SEARCH_RECURSE_TIP.equals(szText))
            {
                notification_text_color = text.getTextColors().getDefaultColor();
                notification_text_size = text.getTextSize();
                DisplayMetrics metrics = new DisplayMetrics();
                WindowManager systemWM = (WindowManager)getSystemService(Context.WINDOW_SERVICE);
                systemWM.getDefaultDisplay().getMetrics(metrics);
                notification_text_size /= metrics.scaledDensity;
                return true;
            }
        }
        else if (gp.getChildAt(i) instanceof ViewGroup)
            return recurseGroup((ViewGroup) gp.getChildAt(i));
    }
    return false;
}

private void extractColors()
{
    if (notification_text_color != null)
        return;

    try
    {
        Notification ntf = new Notification();
        ntf.setLatestEventInfo(this, COLOR_SEARCH_RECURSE_TIP, "Utest", null);
        LinearLayout group = new LinearLayout(this);
        ViewGroup event = (ViewGroup) ntf.contentView.apply(this, group);
        recurseGroup(event);
        group.removeAllViews();
    }
    catch (Exception e)
    {
        notification_text_color = android.R.color.black;
    }
}

Call extractColors ie. in onCreate() of your service. Then when you're creating the custom notification, the color and text size you want are in notification_text_color and notification_text_size:

Notification notification = new Notification();
RemoteViews notification_view = new RemoteViews(getPackageName(), R.layout.notification);       
notification_view.setTextColor(R.id.label, notification_text_color);
notification_view.setFloat(R.id.label, "setTextSize", notification_text_size);