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


5 responses to “Simple example of scheduled, self-clearing Android notifications”

  1. 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. that's not complete, notification appears again when i click on it. can you check please?
    thank you

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

    nice tutorial though

  4. 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?

  5. Thanks for the tutorial. Just a note though. Unless you specify a new thread or use an AsyncTask you will be on the main thread.

    Caution: A service runs in the main thread of its hosting process; the service does not create its own thread and does not run in a separate process unless you specify otherwise. If your service is going to perform any CPU-intensive work or blocking operations, such as MP3 playback or networking, you should create a new thread within the service to complete that work. By using a separate thread, you can reduce the risk of Application Not Responding (ANR) errors, and the application’s main thread can remain dedicated to user interaction with your activities.