Applications often need to communicate with users, even when the applications aren’t actively running. Applications can alert users with text notifications, vibration, blinking lights, and even audio. In this chapter, you will learn how to build different kinds of notifications into your Android applications using modern APIs, while keeping them compatible with devices running Android versions all the way back to API Level 4.
Applications can use notifications to greatly improve the user’s experience. For example:
An email application might notify a user when new messages arrive. A newsreader application might notify a user when there are new articles to read.
A game might notify a user when a friend has signed in, or sent an invitation to play, or beaten a high score.
A weather application might notify a user of special weather alerts.
A stock market application might notify the user when certain stock price targets are met. (Sell now before it’s too late!)
Users appreciate these notifications because they help drive application workflow, reminding the users when they need to launch the application. However, there is a fine line between just enough and too many notifications. Application designers need to consider carefully how they use notifications so as not to annoy users or interrupt them without good reason. Each notification should be appropriate for the specific application and the event the user is being notified of. For example, an application should not put out an emergency-style notification (think flashing lights, ringing noises, and generally making a “to-do”) simply to notify the user that his or her picture has been uploaded to a website or that new content has been downloaded.
The Android platform provides a number of different ways of notifying the user. Notifications are often displayed on the status bar at the top of the screen. They may involve:
Textual information
Graphical indicators
Sound indicators
Vibration of the device
Control of the indicator light
Warning
Although the Android SDK provides APIs for creating a variety of notifications, not all notifications are supported by all devices. For example, the BigTextStyle
and BigPictureStyle
are not available on all Android devices. There is also a degree of variation in how different devices handle notifications. Always test any notification implementations on target devices.
Now let’s look at how to use these different kinds of notifications in your applications. But first, let’s talk a little about compatibility.
Notifications have been around since the beginning of the Android platform. They have undergone some changes, but the basics have pretty much stayed the same. However, over time, some areas have changed enough that they no longer work the way they originally did. While we’re not attempting to cover every single type of notification or option allowed for notifications in this book, even this overview covers at least one area that behaves differently on different versions of Android. We point out these areas as we come across them.
Additionally, a new method of creating notifications was introduced in API Level 11. This method involves using the Notification.Builder()
class, but in this book we describe how to maintain compatibility with devices that are running Android API versions older than API Level 11. To do so, we use the NotificationCompat
library and the NotificationCompat.Builder()
class, rather than the standard API Level 11 and Notification.Builder()
class, so if you are developing only for devices at API Level 11 and higher, feel free to use the later class, as these two different approaches are easily interchangeable.
The standard location for displaying notifications and indicators on an Android device is the status bar that runs along the top of the screen. Typically, the status bar shows information such as the current date and time. It also displays notifications (such as incoming SMS messages) as they arrive—in short form along the bar and in full if the user pulls down the status bar to see the notification list. The user can clear certain notifications by pulling down the status bar and hitting the Clear button. Other notifications are intended to be ongoing, not dismissible by the user, and cleared only by the calling application or Service
.
Developers can enhance their applications by using notifications from their applications to inform the user of important events. For example, an application might want to send a simple notification to the user whenever new content has been downloaded. A simple notification has a number of important components:
An icon (appears on the status bar and the full notification)
Ticker text (appears on the status bar)
Notification title text (appears in the full notification)
Notification body text (appears in the full notification)
An Intent
(launches if the user clicks on the full notification)
In this section, you will learn how to create this basic kind of notification.
Tip
Many of the code examples provided in this chapter are taken from the SimpleNotifications application. The source code for this application is provided for download on the book’s website.
All notifications are created with the help of the NotificationManager
. The NotificationManager
(in the android.app
package) is a system Service
that must be requested. The following code demonstrates how to obtain a valid NotificationManager
object using the getSystemService()
method:
NotificationManager notifier = (NotificationManager)
getSystemService(Context.NOTIFICATION_SERVICE);
The NotificationManager
is not useful without having a valid Notification
object to use with the notify()
method. The Notification
object defines what information displays to the user when the Notification
is triggered. This includes text that displays on the status bar, a couple of lines of text that display on the expanded status bar, an icon displayed in both places, a count of the number of times this Notification
has been triggered, and a time for when the last event that caused this Notification
took place.
To remain backward compatible, first you must construct a NotificationCompat.Builder
object, passing in the application Context
as a parameter, like so:
NotificationCompat.Builder notifyBuilder = new
NotificationCompat.Builder(getApplicationContext());
You can then set the icon, ticker text, and notification timestamp, each of which displays on the status bar, through the notifyBuilder
variable, using the available methods of the object, as follows:
notifyBuilder.setSmallIcon(R.drawable.ic_launcher);
notifyBuilder.setTicker("Hello!");
notifyBuilder.setWhen(System.currentTimeMillis());
You need to set a couple more pieces of information before the call to the notify()
method takes place. First, you need to make a call to the setContentTitle()
method and the setContentText()
method, which configure a View
that displays in the expanded status bar. Here is an example:
Intent toLaunch = new Intent(SimpleNotificationsActivity.this,
SimpleNotificationsActivity.class);
notifyBuilder.setContentIntent(PendingIntent.getActivity(
SimpleNotificationsActivity.this, 0, toLaunch, 0));
notifyBuilder.setContentTitle("Hi there!");
notifyBuilder.setContentText("This is even more text.");
Next, you must create the Notification
object using the build()
method, then call the notify()
method of the notification to supply the notification’s title and body text as well as the Intent
triggered when the user clicks on the notification. In this case, we’re using our own Activity
so that when the user clicks on the notification, our Activity
launches again.
Note
When the expanded status bar is pulled down, the current Activity
lifecycle is still treated as if it were the top (displayed) Activity
. Triggering system notifications while running in the foreground, though, isn’t particularly useful. For an application that is in the foreground, it is better to use a Dialog
or Toast
to notify the user, not a Notification
.
Now the application is ready to actually notify the user of the event. All that is needed is a call to the notify()
method of the NotificationManager
with an identifier and the Notification
we configured using the Notification.Builder
. This is demonstrated with the following code:
private static final int NOTIFY_1 = 0x1001;
// ...
notifier.notify(NOTIFY_1, notify);
The identifier matches up a Notification
with any previous Notification
instances of that type. When the identifiers match, the old Notification
is updated instead of a new one being created. You might have a Notification
that some file is being downloaded. You can update the Notification
when the download is complete, instead of filling the notification queue with a separate Notification
, which quickly becomes obsolete. This Notification
identifier needs to be unique only in your application.
The Notification
displays as an icon and ticker text on the status bar. This is shown at the top of Figure 6.1.
Shortly after the ticker text displays, the status bar returns to normal with each notification icon shown. If the users expand the status bar, they see something like what is shown in Figure 6.2.
You don’t want your application’s notifications to pile up in the status bar. Therefore, you might want to reuse or update notifications to keep the notification list manageable. For example, there is no reason to keep a notification informing the user that the application is downloading File X when you now want to send another notification saying File X has finished downloading. Instead, you can simply update the first notification with new information.
When the notification identifiers match, the old notification is updated. When a notification with a matching identifier is posted, the ticker text does not draw a second time. To show the user that something has changed, you can use a counter. The value of the number
member variable of the NotificationCompat.Builder()
object tracks and displays this. For instance, we can set it to the number 4, as shown here:
notifyBuilder.setNumber(4);
Figure 6.3 shows how this might look.
When a user clicks on the notification, the Intent
assigned is triggered. At some point after this, the application might want to clear the notification from the system notifications queue. This is done through a call to the cancel()
method of the NotificationManager
object. For instance, the notification we created earlier can be canceled with the following call:
notifier.cancel(NOTIFY_1);
This cancels the notification that has the same identifier. However, if the application doesn’t care what the user does after clicking on the notification, there is an easier way to cancel notifications. Simply set a flag to do so, as shown here:
notifyBuilder.setAutoCancel(true);
Setting the setAutoCancel()
method to true
causes notifications to be canceled when the user clicks on them. This is convenient and easy for the application when just launching the Intent
is good enough.
Vibration is a great way to enable notifications to catch the attention of a user in noisy environments or alert the user when visible and audible alerts are not appropriate (though a vibrating phone is often noisy on a hard surface). Android notifications give a fine level of control over how vibration is performed. However, before the application can use vibration with a notification, an explicit permission is needed. The following XML in your application’s AndroidManifest.xml
file is required to use vibration:
<uses-permission android:name="android.permission.VIBRATE" />
Warning
The vibrate feature must be tested on the device. The emulator does not indicate vibration in any way. Also, some Android devices do not support vibration.
Without this permission, the vibrate functionality does not work, nor are there any errors. With this permission enabled, the application is free to vibrate the phone however it wants. This is accomplished by describing the vibrate
member variable using the setVibrate()
method, which determines the vibration pattern. An array of long
values describes the different vibration durations for turning the vibrator on and off. Thus, the following line of code enables a simple vibration pattern that occurs whenever the notification is triggered:
notifyBuilder.setVibrate(new long[] {0, 200, 200, 600, 600});
This pattern causes the device to vibrate for 200 milliseconds and then stop for 200 milliseconds. After that, it vibrates for 600 milliseconds and then stops for that long.
An application can use different patterns of vibrations to alert the user to different types of events or even present counts. For instance, think about a grandfather clock with which you can deduce the time based on the tones that are played.
Tip
Using short, unique patterns of vibration can be useful, and users become accustomed to them.
Blinking lights are a great way to pass information silently to the user when other forms of alert are not appropriate. The Android SDK provides reasonable control over a multicolored indicator light, when such a light is available on the device. Users might recognize this light as a Service
indicator or battery level warning. An application can also take advantage of this light by changing its blinking rate or color.
Warning
Indicator lights are not available on all Android devices. Also, the emulator does not display the light’s state. Use of the indicator light mandates testing on actual hardware.
You must call methods on the NotificationCompat.Builder()
object to use the indicator light. Then, the color of the light must be set as well as information about how it should blink. The following code configures the indicator light to shine green and blink at a rate of 1 second on and 1 second off:
notifyBuilder.setLights(Color.GREEN, 1000, 1000);
Although you can set arbitrary color values, a typical physical implementation of the indicator light has three small LEDs in red, green, and blue. Although the colors blend reasonably well, they won’t be as accurate as the colors on the screen.
Warning
On some devices, certain notifications appear to take precedence when it comes to using the indicator light. For instance, the light on certain devices is always solid green when the device is plugged into a USB port, regardless of whether other applications are trying to use the indicator light. Additionally, on other devices, the color trackball is not lit unless the screen is off. You must unplug the phone from the USB port for the color to change.
An application can use different colors and different blinking rates to indicate different information to the user. For instance, the more times an event occurs, the more urgent the indicator light could be. The following block of code shows changing the light based on the number of notifications that have been triggered, and the blinking light continues until the notification is cleared by the user:
if (counterNotify3.getCount() < 2) {
argb = Color.GREEN;
onMs = 1000;
offMs = 1000;
} else if (counterNotify3.getCount() < 3) {
argb = Color.BLUE;
onMs = 750;
offMs = 750;
} else if (counterNotify3.getCount() < 4) {
argb = Color.WHITE;
onMs = 500;
offMs = 500;
} else {
argb = Color.RED;
onMs = 50;
offMs = 50;
}
counterNotify3.increment();
notifyBuilder.setLights(argb, onMs, offMs);
Color and blinking rates can also be used to indicate other information. For instance, temperature from a weather service can be indicated with red and blue plus a blink rate. Use of such colors for passive data indication can be useful even when other forms would work. It is far less intrusive than annoying, loud rings or harsh, vibrating phone noises. For instance, a simple glance at the device can tell the user some useful piece of information without the need to launch any applications or change what he or she is doing.
Sometimes, the device has to make noise to get the user’s attention. Luckily, the Android SDK provides a means for doing this using the NotificationCompat.Builder
object. Begin by calling the setSound()
method and setting the URI of the sound, and also set the audio stream type to use when playing a sound. Generally, the most useful stream type is STREAM_NOTIFICATION
. The following code demonstrates how to play a sound that is included as a project resource:
notifyBuilder.setSound(Uri.parse(
"android.resource://com.advancedandroidbook.simplenotifications/"
+ R.raw.fallbackring),
AudioManager.STREAM_NOTIFICATION);
By default, the audio file is played once. No specific permissions are needed for this form of notification.
Note
The sound file used in this example is included in the project as a raw resource. However, you can use any sound file on the device. Keep in mind that the sound files available on different Android devices may vary.
Although the default notification behavior in the expanded status bar tray is sufficient for most purposes, developers can customize how notifications are displayed if they so choose. To do so, developers can use the RemoteViews
object to customize the look and feel of a notification.
The following code demonstrates how to create a RemoteViews
object and assign custom text to it:
RemoteViews remote = new RemoteViews(getPackageName(), R.layout.remote);
remote.setTextViewText(R.id.text1, "Big text here!");
remote.setTextViewText(R.id.text2, "Red text down here!");
notifyBuilder.setContent(remote);
To better understand this, here is the layout file remote.xml
referenced by the preceding code:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="31sp"
android:textColor="#ddd" />
<TextView
android:id="@+id/text2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
android:textColor="#f00" />
</LinearLayout>
This particular example is similar to the default notification but does not contain an icon. The setContentTitle()
and setContentText()
methods are normally used to assign the text to the default layout. In this example, we use our custom layout instead. The Intent
still needs to be assigned, though, as follows:
Intent toLaunch = new Intent(SimpleNotificationsActivity.this,
SimpleNotificationsActivity.class);
notifyBuilder.setContentIntent(PendingIntent.getActivity(
SimpleNotificationsActivity.this, 0, toLaunch, 0));
Notification notify = notifyBuilder.build();
notifier.notify(NOTIFY_5, notify);
The end result looks something like Figure 6.4.
Using a custom notification layout can provide better control over the information on the expanded status bar. Additionally, it can help differentiate your application’s notifications from those of other applications by providing a themed or branded appearance.
Note
The size of the area that a layout can use on the expanded status bar is fixed for a given device. However, the exact details might change from device to device. Keep this in mind when designing a custom notification layout. Additionally, be sure to test the layout on all target devices in all modes of screen operation so that you can be sure the notification layout draws properly.
The default layout includes two fields of text: an icon and a time field for when the notification was triggered. Users are accustomed to this information. An application, where feasible and where it makes sense, should try to conform to at least this level of information when using custom notifications.
Android 4.1 (API Level 17) introduced the ability to create notifications that either expand or contract, depending on their order in the notification queue. Expanded notifications are the most recent notifications displayed in the status bar; are larger than contracted ones, as they provide more screen real estate for displaying notification content; and provide the option to include action buttons.
There are many different notification style types that allow you to include large image previews as an attachment using the Notification.BigPictureStyle
class, larger amounts of text such as the contents of a long article using the Notification.BigTextStyle
class, or multiple text snippets such as multiple unread messages from an email inbox using the Notification.InboxStyle
class.
To create one of these Notification
style types, use the setStyle()
method of the NotificationCompat.Builder
class. The setStyle()
method requires passing in a Style
, which is done as follows:
notifyBuilder.setStyle(new NotificationCompat.BigTextStyle()
.bigText("This is a really long message that is used "
+ "for expanded notifications in the status bar"));
In this code sample, we call the bigText()
method on our BigTextStyle
class and include the text that we want displayed when the notification is expanded.
It is also possible to include action buttons in our Notification
. To do so, we need to create a PendingIntent
for each action that we include. The screen size of the device should determine how many action buttons you include, but we have found that more than two actions is usually too many. Figure 6.5 shows what our notification looks like when expanded, including two action buttons.
You should also include the setContentTitle()
and setContentText()
methods as we have demonstrated in past examples, to ensure that your notification displays properly when it is contracted; otherwise the notification content area will be empty if in the contracted state. Figure 6.6 shows what the same notification looks like when contracted.
Starting with Android 4.1 (API Level 17), notifications include the ability to set a priority. The priority allows the system to determine the order of importance in which to display an application’s activity to the device user. Here is a list of priorities you are able to set for your notifications:
Min: Notifications do not show in the status bar when triggered, only when the user expands the notification tray.
Low: Notifications show in the status bar with a low priority.
Default: Notifications default to this when not specifically configured.
High: Notifications for including messages such as, SMS, emails, etc.
Max: Notifications for missed calls, voicemails, etc.
Android 4.3 (API Level 18) introduced the NotificationListenerService
, and Android 4.4 (API Level 19) quickly released enhancements to this new Service
class. The class is designed so that you can configure your application to listen for notifications as the system triggers responses to them. The Android 4.4 updates include special metadata with the notification messages sent by the system, allowing your application to learn more about the information included, such as the picture or title of a notification. This metadata is included as a Bundle
, and Android 4.4 also includes a Notification.Action
class that is useful for learning information about the actions posted by the notifications. You must include the BIND_NOTIFICATION_LISTENER_SERVICE
permission on the NotificationListenerService
class registered in your manifest, in addition to including an <intent-filter>
action with a constant value of android.service.notification.NotificationListenerService
.
As you can see, the notification capabilities on the Android platform are quite robust—so robust that it is easy to overdo it and make your application tiresome for the user. Here are some tips for designing useful notifications:
Use notifications only when your application is not in the foreground. When the application is in the foreground, use Toast
or Dialog
controls.
Allow users to determine what types (text, lights, sound, and vibration) and frequency of notifications they receive, as well as what events to trigger notifications for.
Whenever possible, update and reuse an existing notification instead of creating a new one.
Clear notifications regularly so as not to overwhelm the user with dated information.
When in doubt, generate “polite” notifications (read as quiet).
Make sure your notifications contain useful information in the ticker, title, and body text fields and launch sensible intents.
The notification framework is lightweight yet powerful. However, some applications such as alarm clocks or stock market monitors might also need to implement their own alert windows above and beyond the notification framework provided. In this case, they may use a background Service
and launch full Activity
windows when certain events occur. In Android 2.0 and later, developers can use the WindowManager.LayoutParams
class to enable Activity
windows to display, even when the screen is locked with a keyguard.
Applications can interact with their users outside the normal activity boundaries by using notifications. Notifications can be visual, auditory, or sensory, using the vibrate feature of the device. Various methods can customize these notifications to provide rich information to the user. Special care must be taken to provide the right amount of appropriate information to the user without the application becoming a nuisance or the application being installed and forgotten about.
1. What method do you call to retrieve a NotificationManager
object?
2. True or false: You should use the setTicker()
method to set the ticker text of a Notification
.
3. True or false: The notify()
method is used to broadcast the Notification
.
4. What method should you set on the Notification.Builder
object to display a count of notifications?
5. What does the setAutoCancel()
method cause?
1. Add an InboxStyle Notification
to the SimpleNotifications application included with this chapter.
2. Add a Notification
that displays a progress indicator to the SimpleNotifications application included with this chapter.
3. Create a new Android application demonstrating how to use the NotificationListenerService
class.
Android Design: “Notifications”:
Android Training: “Notifying the User”:
http://developer.android.com/training/notify-user/index.html
Android API Guides: “Notifications”:
http://d.android.com/guide/topics/ui/notifiers/notifications.html
Android Reference documentation for the Notification
class:
http://d.android.com/reference/android/app/Notification.html
Android SDK Reference documentation for the NotificationListenerService
class:
http://d.android.com/reference/android/service/notification/NotificationListenerService.html
Android SDK Reference documentation for the StatusBarNotification
class:
http://d.android.com/reference/android/service/notification/StatusBarNotification.html
Android SDK Reference documentation for the NotificationManager
class:
http://d.android.com/reference/android/app/NotificationManager.html
Android SDK Reference documentation for the Notification.Builder
class:
http://d.android.com/reference/android/app/Notification.Builder.html
Android SDK Reference documentation for the NotificationCompat
class:
http://d.android.com/reference/android/support/v4/app/NotificationCompat.html
YouTube Android Developers Channel: “Android Design in Action: Notifications and Design Process with Alex Faaborg”:
http://www.youtube.com/watch?v=FaW8PwhU_BY
YouTube Android Developers Channel: “2012-10-18 Android Developer Lab+ - Notifications”:
http://www.youtube.com/watch?v=s61Rbk3ynXQ
YouTube Android Developers Channel: “Android Design in Action: Local Video and Rich Notifications”:
18.116.21.109