Notification background in Android Lollipop is white. How can I change it?

12,952

Solution 1

This answer describes a hacky method of changing of notification background color.
Pay attention: this is an undocumented workaround; it is based on reflection and may be broken on custom firmwares; it is applicable to Android Lollipop and Marshmallow versions only; it will not work on Android N and higher versions. I would recommend to stick with the default color unless you have a serious reason to avoid it.

WHY
There is no legal way to set a background color for the custom notification. Google decided that notifications must be white or light gray depending on its priority according to Material Design. However, Google also made two exceptions to this rule:

  1. Notifications for older applications are displayed with black background;
  2. Notifications created with MediaStyle can be of ANY color.

As a result of the second exception, such limitation looks illogical and unreasoned, and that is the only possible excuse why you still want to use a custom color instead of the one that is recommended (or forced?) by Google.

WHAT'S INSIDE
Lets look into BaseStatusBar to see how this limitation is imposed. The only place where the background color for the notification is calculated is applyColorsAndBackgrounds method.
First branch of the if statement is for legacy applications. The only way to get here is to set the target SDK of your application below Build.VERSION_CODES.LOLLIPOP. The background will be turned black in this case.
We are interested in the entry.row.setTintColor statement. To reach it, several checks should be passed, including the one contained within isMediaNotification method. Here they are:

  1. The notification must contain both regular and big views.
  2. The top level layout in both views must have com.android.internal.R.id.status_bar_latest_event_content as its ID.
  3. The big layout must contain a widget with com.android.internal.R.id.media_actions as its ID.

HOW
The most problematic are IDs as long as they are declared in the internal resources and cannot be accessed from the application's layout XML.
The second problem is that the RemoteViews used in the notification is just an ID of the layout resource within the application and cannot be constructed in the code. As a result, we cannot add layouts with IDs required to pass all the checks mentioned above.

However, Google added the addView and removeAllViews methods into RemoteViews for their needs (they are used in the MediaStyle notification) and forgot to make them private.

So, the final idea is simple:

  • build the notification based on the internal layout defined by Google (to pass the 2nd check)
  • remove everything with removeAllViews
  • add our custom layout with addView
  • special case for big view: add the layout defined by Google which contains media_actions to invisible view inside of our custom layout (to pass the 3rd check)

Drawbacks:

  • inflating unused views (they are removed right after inflating)
  • complicated and deeper layout hierarchy

SOLUTION
Our custom big view must contain FrameLayout with android.R.id.empty as its ID. Actually, any ID may be used here, just make sure that you reference the same ID in code (see below).

// We need theese ids to use internal Android resources
int topId = Resources.getSystem().getIdentifier("status_bar_latest_event_content", "id", "android");
int topBigLayout = Resources.getSystem().getIdentifier("notification_template_material_big_media_narrow", "layout", "android");
int topSmallLayout = Resources.getSystem().getIdentifier("notification_template_material_media", "layout", "android");

RemoteViews viewSmall = ...; // Create our custom view here
RemoteViews viewBig = ...; // Create our custom big view here

// This is invisible inner view - to have media_actions in hierarchy
RemoteViews innerTopView = new RemoteViews("android", topBigLayout);
viewBig.addView(android.R.id.empty, innerTopView);

// This should be on top - we need status_bar_latest_event_content as top layout
RemoteViews topBigView = new RemoteViews("android", topBigLayout);
topBigView.removeAllViews(topId);
topBigView.addView(topId, viewBig);

// This should be on top - we need status_bar_latest_event_content as top layout
RemoteViews topSmallView = new RemoteViews("android", topSmallLayout);
topSmallView.removeAllViews(topId);
topSmallView.addView(topId, viewSmall);

Notification.Builder builder = new Notification.Builder(this);

builder.setSmallIcon(R.drawable.ic_notification)
        .setTicker("Some text")
        .setColor(0xff000000) // The desired color!
        .setContent(topSmallView);

Notification n = builder.build();
n.bigContentView = topBigView;

// Use our notification "n" here as usual

It is possible to use another layout instead of notification_template_material_big_media_narrow on the top level to manipulate the height of the big view. Search for appropriate one here among notification_template_xxx.xml files. But do not forget about putting media_actions into hierarchy.

Solution 2

Eventually, if you like to follow official material design specification

Always use style resources for the text of a custom notification

and use custom layout of your notifications, you maybe consider to not overriding default background, but change text style due to API version. You can achieve it by creating two resource style files and used them according to current API version:

values-v21/notification_styles.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <!-- according to official recommendation custom notifications should has the same text style as default one-->
  <style name="NotificationHeader" parent="@android:style/TextAppearance.Material.Notification.Title" />
  <style name="NotificationContent" parent="@android:style/TextAppearance.Material.Notification.Line2" />
</resources>

and

values/notification_styles.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <!-- according to official recommendation custom notifications should has the same text style as default one-->
  <style name="NotificationHeader" parent="@android:style/TextAppearance.StatusBar.EventContent.Title" />
  <style name="NotificationContent" parent="@android:style/TextAppearance.StatusBar.EventContent" />
</resources>

Solution 3

This is the correct behavior for notifications.

If you want to have a direct control on this aspect I suggest you to do the following:

  • Create an alternative version of layout_message_notification.xml inside the folder res/layout-v21

  • Make a slightly change on this new version by changing the outer layout's background color:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/layout_messageNotification"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/gray">
//...
Share:
12,952
Mansour
Author by

Mansour

Updated on June 04, 2022

Comments

  • Mansour
    Mansour almost 2 years

    I want to show a notification for a message in my app. In previous versions of Android everything is ok, but in Lollipop the notification background is white. I used this XML code for my notification layout in layout_message_notification.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/layout_messageNotification"
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/transparent">
    
        <LinearLayout
            android:layout_width="0dp"
            android:layout_weight=".2"
            android:layout_height="wrap_content"
            android:background="@android:color/transparent">
    
            <ImageView
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:scaleType="fitCenter"
                android:src="@drawable/message_icon"/>
    
        </LinearLayout>
    
        <LinearLayout
            android:layout_width="0dp"
            android:layout_weight=".8"
            android:layout_height="wrap_content"
            android:background="@android:color/transparent">
    
            <TextView
                android:id="@+id/textView_notification_title"
                android:layout_width="fill_parent"
                android:layout_height="fill_parent"
                android:gravity="right|center_vertical"
                android:layout_margin="15dp"/>
    
        </LinearLayout>
    
    </LinearLayout>
    

    My notification in lollipop is shown like this: notification style in lollipop

    How can I make the notification background dark or transparent, like in previous versions of Android?

  • Mansour
    Mansour about 9 years
    Yes, finally I did this! but I hoped to find a better solution! Thanks.
  • Stanislav
    Stanislav almost 9 years
    Assigning a background color here is not a good idea because: 1. it will cover rounded corners of the notification row (not applicable to HTC - they changed this style) 2. on resizing of notification the underlying notification row layout with white color is still could be seen. It is especially noticable on lock-screen. Even in case there is no big notification view.
  • Alex Bitek
    Alex Bitek over 8 years
    Yes, but how do you set the background of the notification to be the same as the theme? What you provided only sets the text in the title and body of the notifcation. The background of the custom notification is still white in Lollipop. Do you need to set it with android:background?