Custom notification layouts and text colors
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:
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.
Solution 1:
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.
-
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);
-
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" />
-
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 .
Solution 2:
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);
Solution 3:
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.