Thursday, 2 October 2008

An Icon in the Status Bar

A little excursion here, but now I am putting an icon into the status bar of Android to indicate whether tracklogging is enabled or not.

This is done with the NotificationManager system service.

The interface seems to be easy enough, but there is once again a pitfall that is somehow not really documented. At first sight, this is the only thing one needs to do to get it running.

protected void notify(Context context, Boolean on){
NotificationManager nm = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
if (on){
Notification n = new Notification(R.drawable.locationloggerservice, "GPS Logging started", System.currentTimeMillis());
nm.notify(22, n);
} else {
nm.cancel(22);
}
}
However, doing this always raises an exception and looking at the message it becomes clear that the notification cannot live without an associated intent that is raised whenever someone clicks on the notification. So in this sense a 'read-only' notification that simply says the LocationLoggerService is up and running is not an option.  

We therefore need to wire an Intent into it. What should the intent be? Well, in this case, lacking any better I want to go to the preferences screen where the location logger can be turned off. This Intent has to be wrapped into a PendingIntent, which is a little parcel around all the information that can be handed back to the Notification application to launch an activity within my GPS application with the same rights as anything run within my own application natively.

Once we have created this PendingIntent and passed it into the notification, all is happy.
 protected void notify(Context context, Boolean on){
NotificationManager nm = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
if (on){
Notification n = new Notification(R.drawable.locationloggerservice, "GPS Logging started", System.currentTimeMillis());

ComponentName comp = new ComponentName(context.getPackageName(), GPSPreferenceActivity.class.getName());
Intent intent = new Intent().setComponent(comp);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, Intent.FLAG_ACTIVITY_NEW_TASK);
n.setLatestEventInfo(context, "Location Logger Service", "GPS Tracklogging Enabled", pendingIntent);
nm.notify(22, n);
} else {
nm.cancel(22);
}
}


I call this notify() message from within my service manager that turns tracklogging on and off.

However, there remains one thing not so pretty. Now if I toggle in the preferences the Logging service on, I get the notification, and when I click on it, it takes me to the Preferences Screen again (I now have two of them stacked up). To make sure, only one is shown I also set another flag in the notification:


PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, Intent.FLAG_ACTIVITY_NEW_TASK + Intent.FLAG_ACTIVITY_SINGLE_TOP);


Now if my preferences screen is already top, it will not be launched again.

What is 22? Well, the notification needs an application specific unique int associated with it. 22 was as good as any. It is of course better to code this somewhere so uniqueness is guaranteed and a symbolic value can be associated with it.

No comments: