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

4 Comments »

  1. Comment by Borja Ruiz Papaseit

    Posted on November 13, 2014 at 10:41 am

    Hi! nice tutorial, it works perfecly but… I have a question:
    How would it be if you want to send different notification messages?
    In this example you get the text from resources, what about getting it from your activity/fragment?
    I wait for your reply, thanks!

  2. Comment by محمد لعسيني

    Posted on April 30, 2015 at 1:49 pm

    that's not complete, notification appears again when i click on it. can you check please?
    thank you

  3. Comment by Tiago Dávila

    Posted on October 1, 2016 at 8:09 pm

    You should have commented to register both service and broadcast receiver on xml.

    nice tutorial though

  4. Comment by Deepak Gedia

    Posted on October 15, 2016 at 4:18 am

    Hi
    Thanks it is a nice tutorial
    as i am a new to android development and i have implemented it in my Xamarin application.
    The only one thing i need help from you is i have added extras in the intent but while sending the notification i am not getting the intent extras, every time i am getting new intent is there any way where we can pass extras from main activity?

Leave a comment

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>