8.1: Notifications

Contents:

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. Notification Drawer

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.

Preview for the Notify Me! App Notification Drawer

Task 1. Create a basic notification

1.1 Create the project

  1. Create a new project called "Notify Me!", accept the default options, and use the empty template.
  2. In your activity_main.xml file, change the rootview element to a vertical LinearLayout with its gravity attribute set to "center".
  3. 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"

  4. Create a method stub for the sendNotification() method. The method should take no arguments and return void:
    public void sendNotification() {}
    
  5. Create a member variable for the Notify Button.
  6. 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) {
       }
    });
    
  7. 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

  1. Go to File > New > Image Asset.
  2. From the Icon Type dropdown, select Notification Icons.
  3. 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.
  4. 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.
  5. Create a member variable in MainActivity to store the NotificationManager:
    private NotificationManager mNotifyManager;
    
  6. 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;
    
  7. Instantiate the NotificationManager in onCreate using getSystemService():
    mNotifyManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
    
  8. 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.
  9. Set the Notification Title to "You've been notified!".
  10. Set the Notification Text to "This is your notification text."
  11. 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);
    
  12. Call notify() on the NotificationManager at the end of the sendNotification() method, passing in the notification ID and the notification:
    Notification myNotification = notifyBuilder.build();
    mNotifyManager.notify(NOTIFICATION_ID, myNotification);
    
  13. 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).

  1. Create an explicit intent in the sendNotification() method to launch the MainActivity class:
    Intent notificationIntent = new Intent(this, MainActivity.class);
    
  2. 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);
    
  3. Add the PendingIntent to the Notification using setContentIntent() in the NotificationCompat.Builder:

    .setContentIntent(notificationPendingIntent)
    
  4. 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.

  1. Add the following line to the Notification Builder to set the priority of the notification to HIGH:
    .setPriority(NotificationCompat.PRIORITY_HIGH)
    
  2. 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)
    
  3. 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

  1. In your layout file, create two copies of the "Notify Me!" button.
  2. Change the text attribute in the copies to "Update Me!" and "Cancel Me!".
  3. Change the id's to "update" and "cancel", respectively.
  4. Add a member variable for each of the new buttons and initialize them in onCreate().
  5. Create two methods in the MainActivity that take no parameters and return void:
    public void updateNotification() {}
    public void cancelNotification() {}
    
  6. Create onClick Listeners for the new buttons and call updateNotification() in "update" button onClick method and cancelNotification() 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.

  1. Download this image to use in your notification, and rename it to mascot_1.
  2. Put it in the res/drawable folder.
  3. In your updateNotification() method, convert your drawable into a bitmap:
    Bitmap androidImage = BitmapFactory
        .decodeResource(getResources(),R.drawable.mascot_1);
    
  4. Copy the Intent and PendingIntent you create in sendNotification() to updateNotification(), as you will use the same PendingIntent as a Content Intent.
  5. Copy the NotificationCompat.Builder code from sendNotification() to updateNotification(), to have the same basic notification options in your updated notification.
  6. 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.
  7. 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)
    
  8. Call notify() on the NotificationManager, passing in the same notification ID as before.
    mNotifyManager.notify(NOTIFICATION_ID, notifyBuilder.build());
    
  9. 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.

  1. Create a member String variable that contains the URL to the Material Design guide for notifications: https://developer.android.com/design/patterns/notifications.html.
  2. Create an implicit Intent that opens the saved URL in thesendNotification() method before you build the notification.
  3. 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);
    
  4. Add this icon using the Image Asset Studio, and call it ic_learn_more: Learn More Icon
  5. Add the following line of code to your builder in both sendNotification() and updateNotification() to add the action to both the original and updated notification:
    .addAction(R.drawable.ic_learn_more,"Learn More", learnMorePendingIntent);
    
  6. 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.

  1. 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) {
       }
    }
    
  2. In the onReceive() method, call updateNotification().
  3. 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";
    
  4. Create a member variable for your receiver and initialize it using the default constructor.
  5. In the onCreate() method, register your Broadcast Receiver to receive the ACTION_UPDATE_NOTIFICATION intent:
    registerReceiver(mReceiver,new IntentFilter(ACTION_UPDATE_NOTIFICATION));
    
  6. 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

  1. Create a broadcast Intent in the sendNotification() method using the custom update action.
  2. Get a PendingIntent using getBroadcast():
    Intent updateIntent = new Intent(ACTION_UPDATE_NOTIFICATION);
    PendingIntent updatePendingIntent = PendingIntent.getBroadcast
          (this, NOTIFICATION_ID, updateIntent, PendingIntent.FLAG_ONE_SHOT);
    
  3. Create this icon using the Image Asset Studio, call it ic_update.Update Icon
  4. Add the action to the builder in the sendNotification() method, giving it the title "Update":
    .addAction(R.drawable.ic_update, "Update", updatePendingIntent)
    
  5. Run your app. You can now update your notification without opening the app!

Solution code

Android Studio project: NotifyMe

Coding challenge

Note: All coding challenges are optional and are not prerequisites for later lessons.

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.

The related concept documentation is in Android Developer Fundamentals: Concepts.

Learn more

Guides

Reference

results matching ""

    No results matching ""