8.1: Notifications
Contents:
- What you should already KNOW
- What you will LEARN
- What you will DO
- App overview
- Task 1. Create a basic notification
- Task 2. Update and cancel your notification
- Task 3. Add notification actions
- Coding challenge
- Summary
- Related concept
- Learn more
Until now, the apps you have built used UI elements that are visible only when your app is running. The only exception to this is the BroadcastReceiver you implemented that showed a Toast message when the device was connected or disconnected from power. There are many times when you want to show your user information even when your application is not running. For example, you might let them know that new content is available, or update them on their favorite team score. The Android framework provides a mechanism for your app to notify users even when the app is not in the foreground: the Notification framework.
A Notification is a message you can display to the user outside of your application's normal UI. When Android issues a notification, it will first appear as an icon in the notification area of the device. To see the specific details of the notification, the user opens the notification drawer. Both the notification area and the notification drawer are system-controlled areas that the user can view at any time.
In this practical you'll create an app that triggers a notification when a button is pressed and provides the ability to update the notification or cancel it.
What you should already KNOW
For this practical, you should be able to:
- Implement the
onClick()
method for buttons. - Create Implicit Intents.
- Send Custom Broadcast Intents.
- Use Broadcast Receivers.
What you will LEARN
During this practical, you will learn to:
- Create a Notification using the Notification Builder.
- Use Pending Intents to respond to Notification actions.
- Update or cancel existing Notifications.
What you will DO
In this practical, you will:
- Send a notification when a button is pushed.
- Update the notification both from a button and an action located in the notification.
- Launch an implicit intent to a web page from the notification.
App overview
Notify Me! is an application that can trigger, update and cancel a notification. It also experiments with notification styles, actions and priorities.
Task 1. Create a basic notification
1.1 Create the project
- Create a new project called "Notify Me!", accept the default options, and use the empty template.
- In your activity_main.xml file, change the rootview element to a vertical LinearLayout with its gravity attribute set to "center".
- Add a button with the following attributes to replace the default TextView:
Attribute
Value
android:id
"@+id/notify"
android:layout_width
"wrap_content"
android:layout_height
"wrap_content"
android:text
"Notify Me!"
android:layout_margin
"4dp"
- Create a method stub for the
sendNotification()
method. The method should take no arguments and return void:public void sendNotification() {}
- Create a member variable for the Notify Button.
- Initialize the button in
onCreate()
and create an onClickListener for it:mNotifyButton = (Button) findViewById(R.id.notify); mNotifyButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { } });
- Call
sendNotification()
from the onClick method.
1.2 Build your first notification
Notifications are created using the NotificationCompat.Builder class, which allows you to set the content and behavior of the Notification. A notification must contain the following elements:
- A title, set by
setContentTitle()
. - Detail text, set by
setContentText()
. - An icon, set by
setSmallIcon()
.
An Android Notification is deployed by the NotificationManager. If you need to update or cancel the notification in the future, you should associate a notification ID with your Notification.
Create the Notification Icon
- Go to File > New > Image Asset.
- From the Icon Type dropdown, select Notification Icons.
- Click on the icon next to the Clip Art item to select a material icon that you will use as the icon for your notification. In this example, you can use the Android icon.
- Rename the resource ic_android and click Next and Finish. This will create a number of drawable files with different resolutions for different API levels.
- Create a member variable in MainActivity to store the NotificationManager:
private NotificationManager mNotifyManager;
- Create a constant variable for the notification ID. Since there will be only one active notification at a time, we can use the same ID for all notifications:
private static final int NOTIFICATION_ID = 0;
- Instantiate the NotificationManager in onCreate using
getSystemService()
:mNotifyManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
- Create and instantiate the Notification Builder in the
sendNotification()
method:NotificationCompat.Builder notifyBuilder = new NotificationCompat.Builder(this)
Note: Make sure the NotificationCompat class is imported from the v4 support library. - Set the Notification Title to "You've been notified!".
- Set the Notification Text to "This is your notification text."
- Set the Notification icon to the android icon you added.
NotificationCompat.Builder notifyBuilder = new NotificationCompat.Builder(this) .setContentTitle("You've been notified!") .setContentText("This is your notification text.") .setSmallIcon(R.drawable.ic_android);
- Call
notify()
on the NotificationManager at the end of thesendNotification()
method, passing in the notification ID and the notification:Notification myNotification = notifyBuilder.build(); mNotifyManager.notify(NOTIFICATION_ID, myNotification);
- Run your app. The "Notify Me!" button now issues a notification (look for the icon in the status bar), but it's missing some essential features: there is no notification sound or vibration, clicking on the notification doesn't do anything. Let's add some additional functionality to the notification.
1.3 Add a content intent
In order to improve your notification, you will add a few more features available through the NotificationCompat.Builder class:
- A content intent, which is launched when the notification is tapped, and is set by
setContentIntent()
. - A priority, which determines how the system displays the notification with respect to other notifications, and is set by
setPriority()
. - The default options, such as sounds, vibration and LED lights (if available), and is set by
setDefaults()
.
Tapping a notification launches an Intent. Content Intents for notifications are very similar to the Intents you've been using throughout this course. They can be explicit intents to launch an activity, implicit intents to perform an action, or broadcast intents to notify the system of a system or custom event. The major difference with an Intent in a notification is that it must be wrapped in a PendingIntent, which allows the notification to perform the action even if your application is not running. A PendingIntent is given to an external component (e.g. NotificationManager) which allows the external application to use your application's permissions to execute a predefined piece of code. In effect, it authorizes the notification to send the intent on the application's behalf.
For this example, the content intent of the notification (that is, the intent that is launched when the notification is pressed) will launch the MainActivity of the application (if you are already in the application this will have no effect).
- Create an explicit intent in the
sendNotification()
method to launch the MainActivity class:Intent notificationIntent = new Intent(this, MainActivity.class);
- Get a PendingIntent using
getActivity()
, passing in the notification ID constant for the requestCode and using the FLAG_UPDATE_CURRENT flag:PendingIntent notificationPendingIntent = PendingIntent.getActivity(this, NOTIFICATION_ID, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Add the PendingIntent to the Notification using
setContentIntent()
in the NotificationCompat.Builder:.setContentIntent(notificationPendingIntent)
Run the app. Click the Notify Me! button to send the notification. Quit the app. Now view the notification and click it. Notice the app will open back up at the MainActivity.
1.4 Add priority and defaults to your notification
When your user clicks the "Notify Me!" button, the notification is issued but the only visual that the user sees is the icon in the notification bar. In order to catch the user's attention, the notification defaults and priority must be properly set.
Priority is an integer value from PRIORITY_MIN (-2) to PRIORITY_MAX (2) that represents how important your notification is to the user. Notifications with a higher priority will be sorted above lower priority ones in the notification drawer. HIGH or MAX priority notifications will be delivered as "Heads - Up" Notifications, which drop down on top of the user's active screen.
- Add the following line to the Notification Builder to set the priority of the notification to HIGH:
.setPriority(NotificationCompat.PRIORITY_HIGH)
- The defaults option in the Builder is used to set the sounds, vibration, and LED color pattern for your notification (if the user's device has an LED indicator). In this example, you will use the default options by adding the following line to your Builder:
.setDefaults(NotificationCompat.DEFAULT_ALL)
- You need to quit the application and start it again to see the changes.Note: The high priority notification will not drop down in front of the active screen unless both the priority and the defaults are set. The priority alone is not enough.
Task 2. Update and cancel your notification
After issuing a notification, it is useful to be able to update or cancel the notification if the information changes or becomes no longer relevant.
In this task, you will learn how to update and cancel your notification.
2.1 Add update and cancel buttons
- In your layout file, create two copies of the "Notify Me!" button.
- Change the text attribute in the copies to "Update Me!" and "Cancel Me!".
- Change the id's to "update" and "cancel", respectively.
- Add a member variable for each of the new buttons and initialize them in
onCreate()
. - Create two methods in the MainActivity that take no parameters and return void:
public void updateNotification() {} public void cancelNotification() {}
- Create onClick Listeners for the new buttons and call
updateNotification()
in "update" button onClick method andcancelNotification()
in the "cancel" button onClick method.
2.2 Implement the cancel and update notification methods
Cancel the Notification
Canceling a notification is straightforward: call cancel()
on the NotificationManager, passing in the notification ID:
mNotifyManager.cancel(NOTIFICATION_ID);
Update the Notification
Updating a notification is more complex. Android notifications come with alternative styles that can help condense information or represent it more efficiently. For example, the Gmail app uses "InboxStyle" notifications if there is more than a single unread message, condensing the information into a single notification.
In this example, you will update your notification to use the BigPictureStyle notification, which allows you to include an image in your notification.
- Download this image to use in your notification, and rename it to mascot_1.
- Put it in the res/drawable folder.
- In your
updateNotification()
method, convert your drawable into a bitmap:Bitmap androidImage = BitmapFactory .decodeResource(getResources(),R.drawable.mascot_1);
- Copy the Intent and PendingIntent you create in
sendNotification()
toupdateNotification()
, as you will use the same PendingIntent as a Content Intent. - Copy the NotificationCompat.Builder code from
sendNotification()
toupdateNotification()
, to have the same basic notification options in your updated notification. Change the style of your notification in the same NotificationCompat.Builder, setting the image and the "Big Content Title":
.setStyle(new NotificationCompat.BigPictureStyle() .bigPicture(androidImage) .setBigContentTitle("Notification Updated!"));
Note: The BigPictureStyle is a subclass of NotificationCompat.Style which provides alternative layouts for notifications. See the documentation for other defined subclasses.Change the priority of the Builder to the default, so that you don't get another heads up notification when it is updated (heads up notifications can only be shown in the default style).
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
- Call
notify()
on the NotificationManager, passing in the same notification ID as before.mNotifyManager.notify(NOTIFICATION_ID, notifyBuilder.build());
- Run your app. After clicking update, check the notification again. It now has the image and updated title! You can shrink back to the regular notification style by pinching on the extended one.
2.3 Toggle the button state
In this application, the user can get confused because the state of the notification is not tracked inside the activity. For example, the user may tap "Cancel Me!" when no notification is showing. You can fix this by enabling and disabling the various buttons depending on the state of the notification. When the app is first run, the "Notify Me!" button should be the only one enabled as there is no notification yet to update or cancel. After a notification is sent, the cancel and update buttons should be enabled, and the notification button should disabled since the notification has already been delivered. After the notification is updated, the update and notify buttons should be disabled, leaving only the cancel button enabled. Finally, if the notification is cancelled, the buttons should return to the initial condition with the notify button being the only one enabled.
Here is the enabled state toggle code for each method:
onCreate():
mNotifyButton.setEnabled(true);
mUpdateButton.setEnabled(false);
mCancelButton.setEnabled(false);
sendNotification():
mNotifyButton.setEnabled(false);
mUpdateButton.setEnabled(true);
mCancelButton.setEnabled(true);
updateNotification():
mNotifyButton.setEnabled(false);
mUpdateButton.setEnabled(false);
mCancelButton.setEnabled(true);
cancelNotification():
mNotifyButton.setEnabled(true);
mUpdateButton.setEnabled(false);
mCancelButton.setEnabled(false);
Task 3. Add notification actions
Sometimes, a notification requires immediate interaction: snoozing an alarm, replying to a text message, and so on. When these types of notifications occur, the user might tap your notification to respond to the event. Android then loads the proper Activity in your application for the user to respond. To avoid opening your application, the notification framework lets you embed a notification action directly in the notification itself. This allows the user to act on the notification without opening your application.
The components needed for an action are:
- An icon, to be placed in the notification.
- A label string, placed next to the icon.
- A PendingIntent, to be sent when the notification action is clicked.
For this example, you will add two actions to your notification. First you'll add a "Learn More" action with an implicit intent that launches a web page, then an "Update" action with a broadcast intent that updates your notification without launching the application.
3.1 Implement the "Learn More" action
As a first example of notification actions, you will implement one that launches an implicit intent to open a website.
- Create a member String variable that contains the URL to the Material Design guide for notifications: https://developer.android.com/design/patterns/notifications.html.
- Create an implicit Intent that opens the saved URL in the
sendNotification()
method before you build the notification. - Create a PendingIntent from the implicit intent, using the flag FLAG_ONE_SHOT so that the PendingIntent cannot be reused:
Intent learnMoreIntent = new Intent(Intent.ACTION_VIEW, Uri .parse(NOTIFICATION_GUIDE_URL)); PendingIntent learnMorePendingIntent = PendingIntent.getActivity (this,NOTIFICATION_ID,learnMoreIntent,PendingIntent.FLAG_ONE_SHOT);
- Add this icon using the Image Asset Studio, and call it ic_learn_more:
- Add the following line of code to your builder in both
sendNotification()
andupdateNotification()
to add the action to both the original and updated notification:.addAction(R.drawable.ic_learn_more,"Learn More", learnMorePendingIntent);
- Run your app. You notification will now have a clickable icon that takes you to the web!
3.2 Implement the "Update" action
You've seen that a notification action uses a PendingIntent to respond to user interaction. In the last step, you added an action that uses a PendingIntent created using the getActivity()
method. You can also create a PendingIntent which delivers a broadcast intent by calling getBroadcast()
on the PendingIntent class. Broadcast Intents are very useful in notifications, since a broadcast receiver can register its interest in the intent and respond accordingly, entirely without launching a specific activity.
You will now implement a Broadcast Receiver that will call the updateNotification()
method when the "Update" action in the notification is pressed. It is a common pattern to add functionality to a notification that already exists in the app, so the user does not need to launch any app to perform the action.
Subclass a BroadcastReceiver as an inner class in MainActivity and override the
onReceive()
method. Don't forget to include an empty constructor:public class NotificationReceiver extends BroadcastReceiver { public NotificationReceiver() { } @Override public void onReceive(Context context, Intent intent) { } }
- In the
onReceive()
method, callupdateNotification()
. - Create a constant member variable in MainActivity to represent the update notification action for your BroadcastIntent. Make sure it begins with your package name to insure it's uniqueness:
private static final String ACTION_UPDATE_NOTIFICATION = "com.example.android.notifyme.ACTION_UPDATE_NOTIFICATION";
- Create a member variable for your receiver and initialize it using the default constructor.
- In the
onCreate()
method, register your Broadcast Receiver to receive the ACTION_UPDATE_NOTIFICATION intent:registerReceiver(mReceiver,new IntentFilter(ACTION_UPDATE_NOTIFICATION));
- Override the
onDestroy()
method of your Activity to unregister your receiver:@Override protected void onDestroy() { unregisterReceiver(mReceiver); super.onDestroy(); }
Note: In this example you are registering your Broadcast Receiver programmatically because your receiver is defined as an inner class. When receivers are defined this way, they cannot be registered in the Android Manifest since they are dynamic and have the possibility of changing during the life of the application.
It may seem the broadcast sent by the notification only concerns your app and should be delivered with a LocalBroadcastManager. However, the use of PendingIntents delegates the responsibility of delivering the notification to the Android Framework. Because the Android runtime is handling the broadcast, LocalBroadcastManager can not be used.
Create the Update Action
- Create a broadcast Intent in the
sendNotification()
method using the custom update action. - Get a PendingIntent using
getBroadcast()
:Intent updateIntent = new Intent(ACTION_UPDATE_NOTIFICATION); PendingIntent updatePendingIntent = PendingIntent.getBroadcast (this, NOTIFICATION_ID, updateIntent, PendingIntent.FLAG_ONE_SHOT);
- Create this icon using the Image Asset Studio, call it ic_update.
- Add the action to the builder in the
sendNotification()
method, giving it the title "Update":.addAction(R.drawable.ic_update, "Update", updatePendingIntent)
- Run your app. You can now update your notification without opening the app!
Solution code
Android Studio project: NotifyMe
Coding challenge
Challenge: Enabling and disabling the various buttons is a common way to ensure the user does not perform any actions that are not supported in the current state of the app (think of disabling a "Sync" button when there is no network"). In this application, however, there is one use case in which the state of your buttons does not match the state of the application: when a user dismisses a notification by swiping it away or clearing the whole notification drawer. In this case, your app has no way of knowing that the notification was cancelled, and that the button state must be changed.
Create another broadcast intent that will let the application know that the user has dismissed the notification, and toggle the button states accordingly.
Hint: Check out the NotificationCompat.Builder class for a method that delivers an Intent when the notification has been dismissed by the user.
Summary
- A Notification is a message you can display to the user outside of your application's normal UI.
- Notifications provide a way for your app to interact with the user even when the app is not running.
- When Android issues a notification, it will first appear as an icon in the notification area of the device.
- The UI and actions for a notification are specified using NotificationCompat.Builder.
- To create a notification use
NotificationCompat.Builder.build()
. - To issue the notification, pass the Notification object to the Android runtime system with
NotificationManager.notify().
- To update or cancel a notification, you need to associate a notification ID with your Notification.
- An Intent can be part of a notification (Explicit, Implicit or Broadcast).
- Intents in a notification must be "wrapped" in a PendingIntent, which really isn't an Intent. A PendingIntent is an implementation of the decorator pattern.
- The required components of a notification are: small icon (setSmallIcon()), title (
setContentTitle()
) and some detailed text (setContentText()
). - Some optional components of a notification are: intent, expanded styles, priority, et al. See NotificationCompat.Builder for more info.
Related concept
The related concept documentation is in Android Developer Fundamentals: Concepts.
Learn more
Guides
Reference