Simple example of scheduled, self-clearing Android notifications


Banana Alarm
I couldn’t find a relevant example of doing a schedule notification that launches the app, then clears it. Various bugs and design flaws in Google’s Android O/S make this needlessly complicated, in my opinion. In this, iPhone made it easy.

The basic functionality: schedule daily ordinal time (say, 10am daily) notifications. Upon click, display main activity and clear notification from the notification manager. Sounds easy right? Nope.

I found a good example here, but my notifications weren’t clearing. This solution of using the AlarmManager and Notifications is quite common- but doing both userCancel and loading the MainActivity are a little nuanced.

The general architecture is such: You’re going to create a scheduled alarm event, and upon that event’s firing, kick off a notification. Notification manager lists the notification. When the user clicks the notification, it will launch the app. The app will then globally erase all notifications, then reschedule them.

Questions you may have:
Why not just field the notification on app launch? Delete if necessary, instead of all of them? There may be a stack of notifications. It’s just cleaner to delete them all.

Why not just use the “auto cancel” feature of the notification builder? There are known issues in Android with removing a notification and also launching an activity upon click.

How to build this:

1. Create Alarms
In your MainActivity.java:

public void setAlarm(){ alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE); alarmIntent = new Intent(MainActivity.this, AlarmReceiver.class); pendingIntent = PendingIntent.getBroadcast( MainActivity.this, 0, alarmIntent, 0); alarmStartTime.set(Calendar.HOUR_OF_DAY, 10); alarmStartTime.set(Calendar.MINUTE, 00); alarmStartTime.set(Calendar.SECOND, 0); alarmManager.setRepeating(AlarmManager.RTC, alarmStartTime.getTimeInMillis(), getInterval(), pendingIntent); } private int getInterval(){ int days = 1; int hours = 24; int minutes = 60; int seconds = 60; int milliseconds = 1000; int repeatMS = days * hours * minutes * seconds * milliseconds; return repeatMS; }

2. You’ll notice we have a Receiver class. This is to handle the broadcast of the alarm. Create a new class AlarmReceiver.java:

public class AlarmReceiver extends BroadcastReceiver { NotificationManager notificationManager; @Override public void onReceive(Context context, Intent intent) { Intent service1 = new Intent(context, AlarmService.class); context.startService(service1); } }

3. This simply takes the intent and passes it onto another class we’ll make, the service class. Creating a service class (instead of just doing it on the onReceive()) enables it to work in the background.

public class AlarmService extends Service { private static final int NOTIFICATION_ID = 1; private NotificationManager notificationManager; private PendingIntent pendingIntent; @Override public IBinder onBind(Intent arg0) { return null; } @SuppressWarnings("static-access") @Override public void onStart(Intent intent, int startId) { super.onStart(intent, startId); Context context = this.getApplicationContext(); notificationManager = (NotificationManager)context.getSystemService(context.NOTIFICATION_SERVICE); Intent mIntent = new Intent(this, MainActivity.class); pendingIntent = PendingIntent.getActivity(context, 0, mIntent, PendingIntent.FLAG_CANCEL_CURRENT); NotificationCompat.Builder builder = new NotificationCompat.Builder(this); builder.setContentTitle("Bananas"); builder.setContentText("get your bananas"); builder.setSmallIcon(R.drawable.ic_launcher); builder.setContentIntent(pendingIntent); notificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE); notificationManager.notify(NOTIFICATION_ID, builder.build()); } }

Some important notes:
Above, we “setContentIntent()” and that tells Android O/S what to load once the user clicks. Pending Intent has to have a new Intent(), and that will launch the correct app. There’s a conflict between AutoCancel and that working right, so that is why we do a global clear all notifications onStart() of the MainActivity.

Download Code

Sample code available on Github: BananeAlarm.

Gotchas:
– If your ordinal time is before the current time, it will notify immediately. This annoyed me, so I put some logic in to check if now() (another instance of Calendar getInstance()) is after setAlarmTime. Then, I bump it forward one day.

if(now.after(startingTime)){ startingTime.add(Calendar.DATE, 1); }

– Don’t put the clear and create notification cycle in onCreate() because of Android’s application lifecycle, you have a lot more control if you put it under onStart();

More Reading:
Google on Notifications
Google on AlarmManager