Tag Archives: Android notifications

Android notifications and PendingIntents

Notifications

On an Android phone, the bar at the top of the screen is the notification area or status bar. On a tablet, this area is at the bottom right of the screen. In either case, it is an area that is always visible and is used by apps to post notifications about various events that have occurred. The notification area can be opened to get a list containing more detailed information about these events.

To illustrate a few of the features of notifications, we’ll modify our earlier example using Fragments so that each time the user inputs some new text, a notification is received. In addition, if the user then closes the app and then opens the notification area and clicks on a given notification item, the app is restarted displaying the text that was entered to give that notification.

Implementing these additions requires only adding a bit to the MainActivity. Since we want to generate a notification when the user presses the enterText button we can modify the event handler for this button as follows:

	@Override
	public void enterTextButtonPressed() {
		EnterTextFragment enterTextFragment =
				(EnterTextFragment) getFragmentManager().
				findFragmentById(R.id.enter_text_fragment);
		GetTextFragment getTextFragment =
				(GetTextFragment) getFragmentManager().
				findFragmentById(R.id.get_text_fragment);
		String text = enterTextFragment.getEnteredText();
		getTextFragment.setText(text);
		restartNotifyText(text);
	}

The only change we’ve made is the addition of a call to restartNotifyText() at the end, so let’s look at this method:

	private void restartNotifyText(String text) {
		Notification.Builder notifBuilder = new Notification.Builder(this).
				setSmallIcon(android.R.drawable.btn_star_big_on).
				setContentTitle("Text sent").setContentText(text).
				setTicker("New text arrived");
		Intent mainIntent = new Intent(getApplicationContext(), MainActivity.class);
		mainIntent.putExtra(LOADED_TEXT, text);
		PendingIntent mainPendingIntent = PendingIntent.getActivity(
				getApplicationContext(), simpleNotification, mainIntent,
				PendingIntent.FLAG_UPDATE_CURRENT);
		notifBuilder.setContentIntent(mainPendingIntent);
		NotificationManager notifManager =
				(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
		notifManager.notify(simpleNotification++, notifBuilder.build());
	}

To create a notification, we can use the Notification.Builder class. Its constructor takes a context (the current Activity will do). Following this, we can add as much information as we want to the notification. However, there are three things we must add to any notification: a small icon, a title and the content text. The methods for adding features each return the Builder object that calls them, so they can be chained as seen here. We’ve used one of the built-in icons (it displays a yellow star) for the small icon and provided values for the title and text. In addition we’ve added a ticker which is a message that is displayed for a few seconds when the notification first appears. The ticker is optional.

For a bare bones notification, that’s all the information we need and if we didn’t want the notification to respond to clicks, we could just skip from here to line 12 to get the NotificationManager, then build the notification and send it to the device using notify().

However, to allow the notification to restart a closed app when clicked, we need to provide some more code. We can use an explicit Intent to restart the app in the way we did earlier, so we define mainIntent on line 6 to do this. Since we want the restarted app to display the text just entered, we need to save this text as an extra within the Intent (line 7). The parameter LOADED_TEXT is just a pre-defined string which serves as a key for the extra; it can be any string you like so long as it’s unique.

PendingIntents

Next, we define a PendingIntent. This is essentially a token object which contains a reference to a genuine Intent, and which can be sent to another process giving that process permission to run the enclosed Intent. PendingIntents can specify that various types of processes can be started, but here we’re interested in starting up an Activity, so we call the static PendingIntent.getActivity() method. Its arguments are

  • the usual context in which it is to run
  • an ID int which can be referred to later if we want to modify the PendingIntent. Here, we’ve defined an int called simpleNotification earlier and set its initial value to 1.
  • the Intent itself
  • a flag indicating what action should be taken with the PendingIntent. FLAG_UPDATE_CURRENT means that if this PendingIntent already exists, we should update it with the new data rather than create a new one.

Then we attach this PendingIntent to notifBuilder using setContentIntent(). Setting the content Intent in the builder provides an event handler for clicks on the notification item: clicking on the item will tell the PendingIntent to start up its Intent.

With that done, we can now use the NotificationManager to build and post the notification as before.

One final point should be mentioned. The notify() method takes two arguments. The first is an int ID that can be used to update the notification later. If you call notify() again with the same ID, it overwrites the existing notification. If you call it with a new ID that hasn’t been used before, it creates a new notification. In this example, we want a separate notification for each new bit of text that is entered, so we increment the ID each time a notification is posted.

We use the same ID for the PendingIntent and the notification. If the ID and Intent used to construct a PendingIntent are the same as those used earlier, then the old PendingIntent is overwritten rather than a new one being generated. Note that changing the text stored in the Intent’s extras bundle does not change the Intent as seen by the PendingIntent, so unless we change the ID of the PendingIntent on each call, the same PendingIntent would be attached to all the notifications. That is, each notification would display the correct entered text, but when the Activity is restarted, only the last entered text would be displayed in the app itself.

Finally, we can look at the onCreate() method:

	private static final String LOADED_TEXT = "LoadedText";
	int simpleNotification = 1;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		Intent notifyIntent = getIntent();
		Bundle extras = notifyIntent.getExtras();
		if (extras != null) {
			String loadedText = extras.getString(LOADED_TEXT);
			GetTextFragment getTextFragment =
					(GetTextFragment) getFragmentManager().
					findFragmentById(R.id.get_text_fragment);
			getTextFragment.setText(loadedText);
		}
	}

We need to retrieve the Intent that was used to start (or restart) the Activity. When the app is started by clicking its icon on device’s screen, it is the main system Intent that initializes the Activity. This Intent won’t have any extras. We can use the Activity’s getIntent() method to retrieve the Intent that started the Activity, and getExtras() to get its extras Bundle. If this isn’t null, we can then retrieve the LOADED_TEXT string and set the TextView in getTextFragment to this text.

Advertisements