Developers of Android are no doubt familiar with the Toast class, which displays a little pop-up containing whatever message you want to display to the user. However, the Toast pop-up will automatically dismiss itself after a while, making it useful as a tool to provide timely feedback to the user (such as informing the user that a file has been saved successfully), but not a really good tool to use for some asynchronous action (such as telling the user that an incoming SMS message has arrived). For the latter, you can make use of the Status Bar Notification feature of Android, which displays a persistent notification on the status bar of your Android device. This notification can be dismissed when the user has read it, and thus this makes it a good tool for delivering notifications to the users to respond at their convenience. In this article, I shall walk you through the steps to creating status bar notifications for your Android application.
Creating the Project
Using Eclipse, create a new Android project and name it UsingNotifications (see Figure 1).
For this project, you will be building a simple alarm system that allows the users to set a date and time for the alarm to trigger. Once the specified date and time is reached, you will display another activity containing some additional information.
First, modify the main.xml file as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/hello" /> <DatePicker android:id="@+id/datePicker" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TimePicker android:id="@+id/timePicker" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <Button android:id="@+id/btnSetAlarm" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Set Alarm" /> </LinearLayout> |
This creates the UI as shown in Figure 2.
Add a new XML file to the res/layout folder and name it alarmdetails.xml. Populate it as follows:
1 2 3 4 5 6 7 8 9 10 |
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Details of the alarm..." /> </LinearLayout> |
This creates a very simple UI with a message as shown in Figure 3.
Add a new Class file to the net.learn2develop.UsingNotifications package and name it AlarmDetails.java. Populate it as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
package net.learn2develop.UsingNotifications; import android.app.Activity; import android.os.Bundle; public class AlarmDetails extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.alarmdetails); } } |
Add the following block of code in bold to the AndroidManifest.xml file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="net.learn2develop.UsingNotifications" android:versionCode="1" android:versionName="1.0"> <uses-sdk android:minSdkVersion="10" /> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".MainActivity" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".AlarmDetails" android:label="Details of the alarm"> <intent-filter> <action android:name="net.learn2develop.AlarmDetails" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity> </application> </manifest> |
So basically, the user will select the date and time for the alarm to go off. Once the alarm goes off, it will display the AlarmDetails activity. The next section shows how to create the alarm.
Creating an Alarm
To schedule an alarm in Android, you can make use of the AlarmManager class. The AlarmManager class allows you to schedule an alarm to be triggered at a particular date and time.
Add the following statements in bold to the MainActivity.java file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
package net.learn2develop.UsingNotifications; import android.app.Activity; import android.os.Bundle; import java.util.Calendar; import android.app.AlarmManager; import android.app.PendingIntent; import android.content.Intent; import android.view.View; import android.widget.Button; import android.widget.DatePicker; import android.widget.TimePicker; public class MainActivity extends Activity { TimePicker timePicker; DatePicker datePicker; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); //---Button view--- Button btnOpen = (Button) findViewById(R.id.btnSetAlarm); btnOpen.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { timePicker = (TimePicker) findViewById(R.id.timePicker); datePicker = (DatePicker) findViewById(R.id.datePicker); //---use the AlarmManager to trigger an alarm--- AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE); //---get current date and time--- Calendar calendar = Calendar.getInstance(); //---sets the time for the alarm to trigger--- calendar.set(Calendar.YEAR, datePicker.getYear()); calendar.set(Calendar.MONTH, datePicker.getMonth()); calendar.set(Calendar.DAY_OF_MONTH, datePicker.getDayOfMonth()); calendar.set(Calendar.HOUR_OF_DAY, timePicker.getCurrentHour()); calendar.set(Calendar.MINUTE, timePicker.getCurrentMinute()); calendar.set(Calendar.SECOND, 0); //---PendingIntent to launch activity when the alarm triggers- PendingIntent displayIntent = PendingIntent.getActivity( getBaseContext(), 0, new Intent("net.learn2develop.AlarmDetails"), 0); //---sets the alarm to trigger--- alarmManager.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), displayIntent); } }); } } |
The above code uses the Calendar class to obtain the current date and time. It then sets the date and time specified by the user and uses this information to arm the AlarmManager object. The PendingIntent object (displayIntent) is used to invoke the AlarmDetails activity when the alarm is triggered.
To test the application, press F11 to deploy the application on the Android emulator. Figure 4 shows the application running. Select a date and time that is later than the current time (a suggestion is to set it to one minute later) and wait for the alarm to trigger.
Once the alarm is triggered, you will see the AlarmDetails activity (see Figure 5).
Creating Notifications
The previous example shows how you can schedule an activity to be launched at a particular time. However, the experience can be a little disruptive. Suppose you scheduled the alarm to trigger half an hour later. When the alarm is triggered, you might be doing something else and immediately, the screen will cut into the AlarmDetails activity. A better way to manage the experience is to put a notification into the status bar informing the user that the alarm has been triggered. The user can then respond to the notification at his convenience. Hence, in this section you will modify the project that you have created in the previous section to send a notification to the status bar when the alarm is triggered.
Add a new Class file to the package name and name it DisplayNotifications.java. Populate it as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
package net.learn2develop.UsingNotifications; import android.app.Activity; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Intent; import android.os.Bundle; public class DisplayNotification extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //---get the notification ID for the notification; // passed in by the MainActivity--- int notifID = getIntent().getExtras().getInt("NotifID"); //---PendingIntent to launch activity if the user selects // the notification--- Intent i = new Intent("net.learn2develop.AlarmDetails"); i.putExtra("NotifID", notifID); PendingIntent detailsIntent = PendingIntent.getActivity(this, 0, i, 0); NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); Notification notif = new Notification( R.drawable.icon, "Time's up!", System.currentTimeMillis()); CharSequence from = "AlarmManager - Time's up!"; CharSequence message = "This is your alert, courtesy of the AlarmManager"; notif.setLatestEventInfo(this, from, message, detailsIntent); //---100ms delay, vibrate for 250ms, pause for 100 ms and // then vibrate for 500ms--- notif.vibrate = new long[] { 100, 250, 100, 500}; nm.notify(notifID, notif); //---destroy the activity--- finish(); } } |
This activity will be called by the AlarmManager object when the alarm is triggered. Its main use is to add a notification to the status bar through the use of the NotificationManager class. Here, when a notification is added to the status bar, you will also cause the device to vibrate, through the use of the vibrate property of the Notification object. Note that every notification you create in your application has a unique identifier so that later on you can use it to programmatically dismiss the notification. In this activity, the identifier is passed in from the MainActivity activity. The PendingIntent object (detailIntent) in this activity allows the user to invoke another activity when the notification is tapped. In this case, tapping the notification in the status bar will invoke the AlarmDetails activity. Finally, as this activity does not have an UI, you can simply call the finish() method at the end of the onCreate() method to destroy the activity.
Modify the MainActivity.java as shown in bold below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
package net.learn2develop.UsingNotifications; import android.app.Activity; import android.os.Bundle; import java.util.Calendar; import android.app.AlarmManager; import android.app.PendingIntent; import android.content.Intent; import android.view.View; import android.widget.Button; import android.widget.DatePicker; import android.widget.TimePicker; public class MainActivity extends Activity { TimePicker timePicker; DatePicker datePicker; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); //---Button view--- Button btnOpen = (Button) findViewById(R.id.btnSetAlarm); btnOpen.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { timePicker = (TimePicker) findViewById(R.id.timePicker); datePicker = (DatePicker) findViewById(R.id.datePicker); //---use the AlarmManager to trigger an alarm--- AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE); //---get current date and time--- Calendar calendar = Calendar.getInstance(); //---sets the time for the alarm to trigger--- calendar.set(Calendar.YEAR, datePicker.getYear()); calendar.set(Calendar.MONTH, datePicker.getMonth()); calendar.set(Calendar.DAY_OF_MONTH, datePicker.getDayOfMonth()); calendar.set(Calendar.HOUR_OF_DAY, timePicker.getCurrentHour()); calendar.set(Calendar.MINUTE, timePicker.getCurrentMinute()); calendar.set(Calendar.SECOND, 0); //---PendingIntent to launch activity when the alarm triggers--- Intent i = new Intent("net.learn2develop.DisplayNotification"); //---assign an ID of 1--- i.putExtra("NotifID", 1); PendingIntent displayIntent = PendingIntent.getActivity( getBaseContext(), 0, i, 0); //---sets the alarm to trigger--- alarmManager.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), displayIntent); } }); } } |
Here, you simply make the AlarmManager invoke the DisplayNotification activity when the alarm is triggered. You use an ID of 1 to identify the notification. If you want to display multiple notifications, you must use a unique ID for each notification.
Modify the AlarmDetails.java as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
package net.learn2develop.UsingNotifications; import android.app.Activity; import android.app.NotificationManager; import android.os.Bundle; public class AlarmDetails extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.alarmdetails); //---look up the notification manager service--- NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); //---cancel the notification--- nm.cancel(getIntent().getExtras().getInt("NotifID")); } } |
The AlarmDetails activity will be displayed when the user taps on the notification on the status bar. In this case, once the activity is displayed, you will dismiss the notification using the cancel() method of the NotificationManager object. This method takes in the ID of the notification, which you have passed in earlier through the MainActivity and DisplayNotification activities.
Add the following code to the AndroidManifest.xml file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="net.learn2develop.UsingNotifications" android:versionCode="1" android:versionName="1.0"> <uses-sdk android:minSdkVersion="8" /> <uses-permission android:name="android.permission.VIBRATE"></uses-permission> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".MainActivity" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".AlarmDetails" android:label="Details of the alarm"> <intent-filter> <action android:name="net.learn2develop.AlarmDetails" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity> <activity android:name=".DisplayNotification" > <intent-filter> <action android:name="net.learn2develop.DisplayNotification" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity> </application> </manifest> |
Finally, you can now test the application. Press F11 in Eclipse to test the application on the Android emulator. When the application is loaded, select a time and click the Set Alarm button. Once this is done, click the Back button to dismiss the activity. When the alarm is triggered, you will see the notification on the status bar (see Figure 6).
Clicking and dragging the status bar down will reveal the details of the notifications (see Figure 7).
Clicking on the notification will display the details of the alarm (see Figure 8). At the same time, the notification is also removed.
Interesting, for this example, you should test this on an Android 2.2 emulator. Testing this on the Android 2.3.3 emulator will result in a runtime error. The program crashed on this line:
1 2 |
//---cancel the notification--- nm.cancel(getIntent().getExtras().getInt("NotifID")); |
It seems like the notification ID was not passed in correctly through the Intent object.
Summary
In this article, you have seen how to display a status notification in your Android application. Through this example, you have also got the chance to see how to use the AlarmManager class to set an alarm in your application.