Android Notifications

By Paulus, 4 December, 2014

Introduction

Notifications are those little badges that appear at the top of your Android device's screen. Creating the notifications are pretty easy and straight forward and can be extended to provide actions opposed to a simple message notification. The application in this post is a simple application which has a single button that fires off a notification.

Application

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.paulusworld.notificationexample"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="19" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".NotifiedActivity"
            android:label="@string/title_activity_notified"
            android:parentActivityName=".MainActivity" >
            <meta-data
                android:name="android.support.PARENT_ACTIVITY"
                android:value=".MainActivity" />
        </activity>
    </application>

</manifest>

According to Google, when you start an Activity form a notification, you must preserve the user's expected navigation experience. This is only true if the Activity you are starting from the notification is a part of the application's normal workflow. If this is a "special" Activity -- an Activity that is extending the notification by providing information that would be impossible to have in an Notification, then you do not need to preserve the navigation.

The first step we must take to preserve the navigation stack is to define the application hierarchy. For Android 4.0.3 and earlier you do this by adding the meta-data tag (lines 28 - 30) and for Android 4.1 and later, use the android:parentActivityName attribute, found on line 27. The second step is to create a back stack based on the Intent that starts the Activity, which is done in code and found in the MainActivity.java file.  

package com.paulusworld.notificationexample;

import android.app.Activity;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.graphics.BitmapFactory;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.TaskStackBuilder;
import android.view.View;

public class MainActivity extends Activity {

    private final String NOTIFICATION_TAG = "NotificationExample";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void sendNotification(View view) {
        Intent resultIntent = new Intent(this, NotifiedActivity.class);
        TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);

        // Adds the back stack -- defined in the manifest file.
        stackBuilder.addParentStack(NotifiedActivity.class);
        // Adds the intent to the top of the stack.
        stackBuilder.addNextIntent(resultIntent);
        // Create a PendingIntent that contains the back stack.
        PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);

        NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
            // Set appropriate defaults for the notification light, sound,
            // and vibration.
            .setDefaults(Notification.DEFAULT_ALL)
            // Set the required fields.
            .setSmallIcon(R.drawable.ic_launcher)
            .setContentTitle("Notification Title")
            .setContentText("Notification Message")
            // The following are all optional.
            // Use a default priority; recognized by Android 4.1+
            .setPriority(Notification.PRIORITY_DEFAULT)
            // Provide a large icon, shown with the notification
            // in the notification drawer on devices running Android
            // 3.0+
            .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher))
            // Show some preview text.
            .setTicker("Ticker!")
            // Show a number -- this is typically used to show the
            // number of notifications.
            .setNumber(1)
            // If the notification relates to a past or upcoming event
            // You can specify the time in which the notification
            // will appear to have been fired. The following is an
            // example of how to set the notification time. If you
            // don't want to specify a time, then the current time
            // will be used.
            .setWhen(System.currentTimeMillis())
            // Set the Intent for when a user touches the notification.
            .setContentIntent(resultPendingIntent)
            // With Android 4.1+ you can add more actions to a notifications.
            // For example, the gmail app. When an email is received, you have
            // the options of replying or deleting the email right from the
            // Notification drawer.
            .addAction(
                    R.drawable.ic_launcher,
                    "Action",
                    null)
            // Automatically dismiss the notification when the user taps on it.
            .setAutoCancel(true);

        NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.ECLAIR) {
            notificationManager.notify(NOTIFICATION_TAG, 0, builder.build());
        } else {
            notificationManager.notify(NOTIFICATION_TAG.hashCode(), builder.build());
        }
    }
}

The NotificationCompat.Builder has several methods that set various aspects of the notification before actually being built when the build() function is called. The first four calls are required in order for the notification to be successfully built; setDefaults(), setSmallIcon(), setContentTitle(), and setContentText().

The setDefaults() tells the notification manager what to use when the notification is sent. DEFAULT_SOUND, DEFAULT_VIBRATE, DEFAULT_LIGHTS, or DEFAULT_ALL.

setSmallIcon() assigns the icon that will be displayed in the notification bar.

the setContentTitle() and setContentText() assigns the title and message, respectively.

setPriority() is recognized by Android 4.1+. This indicates how important the notification is. Low-priority notifications may be hidden from the user in certain situations, while higher-priority notifications may interrupt the user.

setLargeIcon() is the icon that is used when the ticker is displayed. This is also the icon that is used in the notification itself.

The setTicker() function takes a string and uses that in the notification bar at the top of the screen to give the user a quick preview of the notification.

setNumber() simply shows a number. This number usually indicates the number of notifications.

setWhen() allows you to specify a time in which you want to report to the user. Setting this is useful when the notification relates to a past or future event.

Although the setContentIntent() function is optional, it's a good idea set this. This function takes a PendingIntent object as it's only argument.

With Android 4.1+ you can add more actions to a notification. Using the GMail application as an example. When you receive an email, a notification is displayed. Pulling down the Notification Drawer, you'll see the expanded notification along with additional actions; Reply and Delete. In the example above, the third parameter is null, which should be a PendingIntent object. For simplicity, we just left it as null.

Setting the setAutoCancel() to true will clear the notification when the user taps on it. 

There are other attribtues you can set on the notification such as the color of the light and how fast it will flash, the sound file to use, etc. You can view the list of functions the NotificationCompat.Builder provides here.

 

In the MainActivity class, we didn't implement an OnClickListener, but we defined the button action within the layout. android:onClick="@string/sendNotification"

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="${relativePackage}.${activityClass}" >

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:onClick="@string/sendNotification"
        android:text="@string/button_notify" />

</RelativeLayout>
package com.paulusworld.notificationexample;

import android.app.Activity;
import android.os.Bundle;

public class NotifiedActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_notified);
	}
}

The NotifiedActivity is nothing special and only displayed when the user taps on the notification.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/notified"
        android:textAppearance="?android:attr/textAppearanceLarge" />

</LinearLayout>

Finally, the strings.xml file.

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <string name="app_name">NotificationExample</string>
    <string name="button_notify">Notify</string>
    <string name="message_notified">You have acknowledged the notification.</string>
    <string name="title_activity_notified">Notified</string>
    <string name="sendNotification">sendNotification</string>
    <string name="notified">You have just been notified!</string>

</resources>