Android: Display a Notification
3 min read

Android: Display a Notification

Android: Display a Notification

One of the applications I'm thinking about I want it designed like the notification-based applications you see out there (Light Flow from reactle comes to mind). To be able to do this, I first need the app to display a notification on the device as a first step.

The code to trigger a notification is pretty simple and you can find a lot of resources online with various options. To cover for a lot of cases, I ended up over-engineering the process a bit, in the sense that I've encased the notification in a Runnable. This way, the display notification class is stand-alone and can be dropped anywhere in your project. The code I ended up with is still comprehensible though :)

In a Runnable , all you need to implement is the run() method which, unfortunately doesn't take any parameters. The code should look something like:

public class DisplayNotification implements Runnable {
    @Override
    public void run() {
        // TODO: Display the notification here
    }
};

Display a Notification

To build a notification, you need to call the notification builder (d'oh!). In a good Android way, it allow you to chain setting properties. Some interesting ones are:

  • setContentTitle(String) - sets the notification title (a larger, bold text in the notification)
  • setContentText(String) - sets the content of the notification. Usually, this is a single line, but is more malleable to user needs
  • setSmallIcon(int) - sets a small icon (on the right hand side of the notification). One can use this to e.g. identify the type of notification
  • setLargeIcon(Bitmap) - set a large icon on the left hand side. Normally, this is the application icon (or a variation of)

These (and others) can be found in the Notification.Builder 's documentation and are the most common to get implemented.

The builder code looks something like this:

Notification.Builder builder = new Notification.Builder(context)
    .setContentTitle("Flickr wallpaper")
    .setContentText("Last update @ 00:00")
    .setSmallIcon(R.drawable.ic_action_picture)
    .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher))
    .setContentIntent(pendingIntent)
    ;

As you can see, we need a Context in order to use the builder. Also, any respectable notification allows the user to perform an action if tapped on (e.g. open up the application). This is where the .setContentIntent(pendingIntent) comes into play; Android needs Intents to communicate between UI bits, so we'll need to build one.

The Pending Intent

Building a pending intent is simple. Build an Intent object and wrap it in a PendingIntent one:

Intent intent = new Intent(context, // the context
    Mainctivity.class // the activity to be triggered
);

PendingIntent pendingIntent = PendingIntent.getActivity(context,
    NOTIFICATION_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT);

The NOTIFICATION_ID is an unique id (integer) you give to the Notification. The FLAG_UPDATE_CURRENT flag tells the system to update an existing notification (with the NOTIFICATION_ID id) instead of creating new ones. This is also suggested by Android usability guidelines (to update the content of a notification, not fill up the notifications list...). I think this is fairly trivial, but if you have any doubts, please check the documentation.

The Notification

Once we have the PendingIntent and the Builder objects, we can actually build the notification object:

NotificationManager notificationManager;
Notification n;

notificationManager =
    (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
n = builder.build();

notificationManager.notify(NOTIFICATION_ID, n);

This part is also simple:

  • We need to get access to the notification manager (which is a system manager)

  • We build the notification object (builder.build()). Mind you, if you target a build earlier than Jelly Bean, the API si different. My code looks like this:

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
    n = builder.build();
    } else {
    n = builder.getNotification(); // deprecated
    }
    
  • Lastly, send the notification to the manager

Easy!

Fill Up the Gaps

As I've mentioned above, run() doesn't do parameters. so, to make things simple, I've added a constructor for them. The end code looks like this:

public class DisplayNotification implements Runnable {

    Context mContext;
    NotificationManager mNotificationManager;

    public DisplayCustomNotification(Context mContext) {
        this.mContext = mContext;
        mNotificationManager = (NotificationManager)
            mContext.getSystemService(Context.NOTIFICATION_SERVICE);
    }

    @Override
    public void run() {
        makeNotification(mContext);
    }

    private void makeNotification(Context context) {
        Intent intent = new Intent(context, MainActivity.class);

        PendingIntent pendingIntent = PendingIntent.getActivity(context,
            NOTIFICATION_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT);

        Notification.Builder builder = new Notification.Builder(context)
            .setContentTitle("Notification Title")
            .setContentText("Sample Notification Content")
            .setContentIntent(pendingIntent)
            .setSmallIcon(R.drawable.ic_action_picture)
            .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher))
            ;
        Notification n;

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            n = builder.build();
        } else {
            n = builder.getNotification();
        }

        notificationManager.notify(NOTIFICATION_ID, n);
    }
}

I've added the makeNotification() method to allow for different types of notifications later on.

Then, in your code, all you need to do is to build the runnable object and execute. I however ended up with the full monty:

Handler mHandler = new Handler();
Context appContext = context.getApplicationContext();
mHandler.post(new DisplayNotification(appContext));

HTH,