82

My application shows some notifications, and depending on user preferences it might use a custom layout in a notification. It works well, but there is a small problem -- text colors. Stock Android and almost all manufacturer skins use black text against a light background for notification text, but Samsung doesn't: their notification pulldown has a dark background and the text in the default notification layout is white.

So this causes a problem: the notifications that don't use any fancy layouts show up fine, but the one that uses a custom layout is hard to read because the text is black instead of the default white. Even the official documentation just sets a #000 color for a TextView, so I couldn't find any pointers there.

A user was kind enough to take a screenshot of the problem:

Screenshot

So how do I use the default notification text color from the device in my layouts? I'd rather not start dynamically altering the text color based on phone model, since that requires a lot of updating and people with custom ROM's might still get the problem, depending on the skin they're using.

erdemlal
  • 481
  • 5
  • 19
Veeti
  • 5,149
  • 3
  • 29
  • 37

12 Answers12

84

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 .

Community
  • 1
  • 1
Malcolm
  • 38,924
  • 10
  • 67
  • 89
  • I don't think I understand this answer. The final sentence states that there isn't a color reference that can be used for this. What color should be used in this case? – Steve Pomeroy Jun 07 '11 at 15:53
  • That said, I noticed in Android's changelog that only as of API level 9, the android:textAppearance="@androidstyle/TextAppearance.StatusBar.EventContent" was made public. Before then, it was hidden. – Steve Pomeroy Jun 07 '11 at 15:58
  • 1
    It wasn't hidden before Android 2.3, it simply didn't exist. Back then there was a hard-coded value in the layout, and if the device vendor decided to change it, you woudn't be able to do anything about it. There were no colors or styles through which you could access this value. Right now you still have no colors, but you have a style, which you can apply to get this specific text color. Make sense? :) – Malcolm Jun 07 '11 at 17:14
  • Yeah, I figured out the 2.3 issue. What I don't understand is what you're doing with the local style. Is the style system somehow figuring out what you mean by the local style and ignoring the arbitrary color you posted there? – Steve Pomeroy Jun 07 '11 at 20:45
  • 3
    What local style? The style in my answer is taken straight from the Android source. You apply it as any other built-in style (`style="@android:style/TextAppearance.StatusBar.EventContent"`) and get the necessary color for the text. It's gonna be `#ff6b6b6b` in the case of vanilla Android, or whatever color was set there instead by the device vendor. – Malcolm Jun 07 '11 at 22:44
  • @karaokyo Well noted, I updated the answer. I didn't test the solution for Android 5.0, though, so please tell me if something doesn't work. – Malcolm Feb 11 '15 at 01:45
  • 4
    Just to let others know, this does not work on Android 6+ – iGoDa Sep 23 '15 at 16:28
  • @Malcolm in your solution can I use a **custom font** ? is it worked ? because in this method ( RemoteViews ) if I am using custom text view which made with a custom font , It crashed the app . So can you have a solution for this "custom font in notification" ? – Harsh Dalwadi Mar 30 '16 at 05:49
  • @HarshDalwadi You didn't post many details about your widget and the crash, but I assume you have some custom attributes without which the widget crashes. In that case you can define your own style with the necessary attributes and specify the style I mentioned as its parent. – Malcolm Mar 30 '16 at 07:53
  • @Malcolm Mail me at dalwadi2@gmail.com,I can show you my code. it's too urgent for me to use a custom font in Notification. – Harsh Dalwadi Mar 30 '16 at 10:15
  • @HarshDalwadi You're welcome to open a different question here, someone may help you. – Malcolm Mar 30 '16 at 10:27
  • Here I can't . because code is confidential and here some people do downvote question and account going to Ban .so if you willing to help then mail me otherwise thank you for your time ;) @Malcolm – Harsh Dalwadi Mar 30 '16 at 10:34
  • @HarshDalwadi You simply need to learn to [ask good questions](http://stackoverflow.com/help/how-to-ask). People won't do the job for you though, sorry. – Malcolm Mar 30 '16 at 10:38
63

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);
grzaks
  • 1,374
  • 2
  • 16
  • 31
  • 1
    +1 great answer. Works well. I noticed there seem to be 2 different default colors - for the title and for the main content. I want to fetch both, but have only been able to get the title color using this method - the iteration seems to only find 1 textbox. Any ideas? – Hermit Oct 03 '11 at 16:59
  • @Raw Did you set another search tip text for the content text? – lapis Oct 07 '11 at 23:59
  • 1
    I see the same as @Raw, only one TextView. I replaced "Utest" with another search tip (and with a different string). It only finds the one TextView and it matches COLOR_SEARCH_RECURSE_TIP. Any pointers? Thx. – ciscogambo Oct 27 '11 at 19:08
  • 6
    The reason it only finds one is a bug in "return recurseGroup((ViewGroup) gp.getChildAt(i));" - the method should return only if the inner "recurseGroup" returned true. It should be: "if (recurseGroup((ViewGroup) gp.getChildAt(i))) return true;". Thanks for the solution - great out of the box thinking. – AlikElzin-kilaka Dec 06 '11 at 18:27
  • Additionally, there's no need for recurseGroup() to return a boolean. Nobody checks this return value, so you can simplify by making it void. – greg7gkb Dec 23 '11 at 00:31
  • 9
    Thanks for this answer, it was very very helpful. For anyone interested, I threw together a quick class than can be dropped in to fetch both the title and text color/size based on Gaks answer. It can be found here: http://pastebin.com/sk08QGxs – NuSkooler Feb 10 '12 at 21:13
  • NuSkooler's pastebin is great and I've used it recently. One thing to note is line 56 should be TEXT_SEARCH_TEXT.equals(text) instead of its current implementation. – Rockmaninoff Jun 25 '12 at 15:12
  • Recently I ran into the bug described by kilaka on Galaxy Note 2, it actually was critical there, so I fixed it in the answer. I wish kilaka had done it himself because now we have about 30 instances of this bug judging by the number of +1's :) – lapis Oct 19 '12 at 06:21
  • @NuSkooler Your code works pretty well but I recently noticed a bug. It actually fails for HTC Evo View tablet with Android 3.2.1. The problem is that mNotifyTextColor is never initialized on this device. This causes a NullPointerException when we try to read the value. The solution is to set the defaults for mNotifyTextColor and mNotifyTitleColor after the catch block if either value is still unset. – jebcor Nov 09 '12 at 18:17
  • I've just found out that this method fails on Samsung Galaxy GT-S7500, SDK 2.3.6 with black notification background. The extracted color is also black – Amir Uval Mar 05 '13 at 10:33
  • 3
    Beware of this sentence because it's **not true** (tested in a Galaxy Ace with API level 10): `Solution by Malcolm works fine with API>=9`. You know, Android and manufacturers are a tricky thing. Use this solution for every platform. – cprcrack Dec 29 '13 at 01:38
  • @NuSkooler, great work, but stime got NullPoint around getTitileColor() – thecr0w Feb 21 '14 at 09:19
  • 1
    the setLatestEventInfo() now is deprecated.So I use `NotificationCompat` to build a notification.Then I use `ntf.contentView.apply(this, group)` but get an error "android.widget.RemoteViews$ActionException: view: android.support.v7.widget.AppCompatImageView can't use method with RemoteViews: setBackgroundResource(int)" . I don't know how to deal with it. – Allen Vork Jul 11 '16 at 11:05
17

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.

A-IV
  • 2,405
  • 2
  • 19
  • 16
13

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.

Community
  • 1
  • 1
AlikElzin-kilaka
  • 30,165
  • 25
  • 168
  • 248
  • 5
    Your 2.3+ instructions didn't work for Android 5. It produced white text on a white background. – Sam Aug 14 '15 at 21:15
6

I use this on the TextView in question:

style="@style/TextAppearance.Compat.Notification.Title"

It gives me white text if the background is black and black text if the background is white. And it works at least as far back as API 19.

Gavin Wright
  • 2,115
  • 2
  • 10
  • 25
2

You should use the colors specified in android.R.color

For example: android.R.color.primary_text_light

Custom ROM developers and Android skin designers are supposed to update these so your app's colors can be in line with the rest of the system. This includes making sure your text shows up properly throughout the system.

Austyn Mahoney
  • 11,078
  • 7
  • 59
  • 85
  • What color am I supposed to use from `android.R.color`? All the text colors have both "dark" and "light" variations. – Veeti Feb 02 '11 at 14:14
  • 1
    I don't have a device with a custom theme on it, so I can't really test which one to use. I'd say try both and see which one works for you. – Austyn Mahoney Feb 03 '11 at 19:11
1

I know it is an old question but it could help someone else ; ) I do this in my app and that works perfect in some few lines :

    RemoteViews notificationView = new RemoteViews(context.getPackageName(), R.layout.notification_layout);

    if (SDK >= LOLLIPOP) {

            TextView textView = new TextView(context);
            textView.setTextAppearance(context, android.R.style.TextAppearance_Material_Notification_Title);

            notificationView.setTextColor(R.id.title, textView.getCurrentTextColor());
            notificationView.setFloat(R.id.title, "setTextSize", textView.getTextSize());

            textView.setTextAppearance(context,android.R.style.TextAppearance_Material_Notification_Line2);

            notificationView.setTextColor(R.id.contentText,textView.getCurrentTextColor());
            notificationView.setFloat(R.id.contentText,"setTextSize",textView.getTextSize());

            textView = null;

    }
Samet
  • 919
  • 11
  • 26
1

Lookin at this instructions: http://developer.android.com/guide/topics/ui/notifiers/notifications.html#CustomExpandedView If you set up your background color for the LinearLayout container then you can have your colors in notification for text and the background.

If the default colour for the notification text is defined by the launcher application then you cannot retrieve it from the android defaults settings unless the launcher is sharring this information.

However, have you try to remove this line android:textColor="#000" from your layout so that it can get automatically the default color?

Lumis
  • 20,819
  • 7
  • 57
  • 66
  • 1
    Removing the text color doesn't help - the default doesn't match the notification colors. – Veeti Feb 06 '11 at 15:08
  • Did you try changing background? I think this is a serious problem and it is good that you brought it up. Have you tried Google developer forum/groups? – Lumis Feb 06 '11 at 15:37
1

I've found a very simple solution directly changing the name of the attribute provided by Android.

As you can see in this tutorial: http://www.framentos.com/android-tutorial/2012/02/20/how-to-create-a-custom-notification-on-android/

You only need to use a different attribute:

<item name="android:textColor">?android:attr/textColorPrimaryInverse</item>

Hope this can help you!

Mauri
  • 19
  • 2
0

Solution from @Malckom didn't help me at Lolipop with dark notification background because of TextAppearance.Material.Notification.Title is a system hardcoded color. Solution from @grzaks did, but with some changes within notification creating process:

NotificationCompat.Builder mBuilder =
    new NotificationCompat.Builder(this)
                          .setContentTitle(NOTIFICATION_TITLE_TIP)
                          .setContentText(NOTIFICATION_TEXT_TIP);
Notification ntf = mBuilder.build();
// ...
if (NOTIFICATION_TEXT_TIP.equals(szText)) {
    notification_text_color = text.getTextColors().getDefaultColor();
} else {
    if (NOTIFICATION_TITLE_TIP.equals(szText)) {
        notification_title_color = text.getTextColors().getDefaultColor();
    }
}
// ...
Tunaki
  • 116,530
  • 39
  • 281
  • 370
0

Here is what worked for me to resolve this error. Add the following to styles.xml

    <style name="TextAppearance">
    </style>
    <style name="TextAppearance.StatusBar">
    </style>
    <style name="TextAppearance.StatusBar.EventContent">
        <item name="android:textColor">#ff6b6b6b</item>
    </style>
    <style name="TextAppearance.StatusBar.EventContent.Info">
        <item name="android:textColor">#ff6b6b6b</item>
    </style>
inder
  • 1,646
  • 1
  • 14
  • 15
0

In your expanable or collaspse layout set android:background as a "@android:color/transparent" because this is automatically take your device notification theme color and set as a background

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="180dp"
android:layout_margin="0dp"
android:background="@android:color/transparent"
android:orientation="vertical">
          <TextView

            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textColor="@color/colorSecondary"
            android:textStyle="bold" />

And your textview color define black and white as in Resource/color file in simple color file and as well as night mode color file