Android: Creating a two color LED notification
Posted by Dimitri | Apr 13th, 2012 | Filed under Featured, Programming
Another programming tutorial, this time, showing how to make the built-in notification LED on a Android device continuously alternate between two colors. That said, by the time this article is being written, it’s recommended to try this code on a real Android device that has a notification LED instead of running the application on the emulator. Also, the Activity featured below has been created to work on devices with Android 2.0 or later. All code featured in this article is available for download at the end of the post.
To continuously change the colors of the LED, it’s necessary to create and initialize a Notification object that changes the LED colors and post this notification using a handle to the system’s notification service (using a instance of the NotificationManager class). The notification is then canceled, its LED color is changed and the notification is posted again and the process is repeated over and over making the colors swap back and forth. Since this code is going to run continuously, it’s better to place it inside an AsyncTask. Then, all pending notifications must be cancelled and the AsyncTask background code execution must be stopped if the user presses the back button. Here’s the code:
package fortyonepost.com.twocolorledblinker; import android.app.Activity; import android.app.Notification; import android.app.NotificationManager; import android.os.AsyncTask; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.CheckBox; public class TwoColorLEDblinkerActivity extends Activity { //An object that acts as a handle to the system notification service private NotificationManager notificationManager; //The object responsible for sending a notification private Notification notification; //Sets whether the LED should blink indeterminately or at fixed rate private boolean indeterminate = true; //The time (in milliseconds) that each color remains active private int msInterval = 850; //The number of times that colors are swapped private int transitions = 4; //User interface elements private Button startBT; private Button stopBT; private CheckBox indeterminateCB; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); //Get a handle to the system's notification service notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); //Create a new notification notification = new Notification(); //The notification should turn on the device's notification LED notification.flags = Notification.FLAG_SHOW_LIGHTS; //Initialize the user interface buttons and the check box startBT = (Button) findViewById(R.id.startBT); stopBT = (Button) findViewById(R.id.stopBT); indeterminateCB = (CheckBox) findViewById(R.id.inteterminateCB); //Set what happens when the start button is pressed startBT.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { //Instantiate a TwoColorBlink AsynkTask object and call the execute() method new TwoColorBlink().execute(); //LED is blinking, disable the 'Start' button startBT.setEnabled(false); //Enable 'Stop' button stopBT.setEnabled(true); //Disable the 'Indeterminate' check box indeterminateCB.setEnabled(false); } }); //Set what happens when the stop button is pressed stopBT.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { //Set the indeterminate variable to false. indeterminate = false; //Cancel the notification notificationManager.cancel(0); //LED isn't blinking, enable start button startBT.setEnabled(true); //Disable 'Stop' button stopBT.setEnabled(false); //Disable the 'Indeterminate' check box indeterminateCB.setEnabled(true); } }); } @Override protected void onPause() { //Set the indeterminate variable to false. indeterminate = false; //Cancel the notification notificationManager.cancel(0); super.onPause(); } private class TwoColorBlink extends AsyncTask<Void, Void, Void> { //Initialize an integer (that will act as a counter) to zero private int counter = 0; @Override protected void onPreExecute() { //Set 'indeterminate' boolean according to the check box if(indeterminateCB.isChecked()) { indeterminate = true; } else { indeterminate = false; } } @Override protected Void doInBackground(Void... arg0) { try { // Get the current background thread's token synchronized (this) { //Set the initial LED color to red notification.ledARGB = 0xFFFF0000; //Check if the indeterminate boolean is true if (TwoColorLEDblinkerActivity.this.indeterminate) { while(indeterminate) { //Post the notification notificationManager.notify(0, notification); //Wait the number of milliseconds defined by 'msInterval' this.wait(msInterval); //Cancel the notification notificationManager.cancel(0); //Add one to the counter while obtaining the remaining part of its division by 2 and checking if it's equal to zero if (counter++ % 2 == 0) { //Change color to green notification.ledARGB = 0xFF00FF00; } else { //Change color to red notification.ledARGB = 0xFFFF0000; } } } else //Notification LED should blink a predefined number of times { //While the counter is smaller or equal to the number of transitions while (counter <= transitions) { //Post the notification notificationManager.notify(0, notification); //Wait the number of milliseconds defined by 'msInterval' this.wait(msInterval); //Cancel the notification notificationManager.cancel(0); if (counter % 2 == 0) { // Change color to green notification.ledARGB = 0xFF00FF00; } else { // Change color to red notification.ledARGB = 0xFFFF0000; } //Add one to the counter counter++; } } } } catch (InterruptedException e) { e.printStackTrace(); } return null; } @Override protected void onPostExecute(Void result) { //Cancel the notification notificationManager.cancel(0); //Reset the counter to zero counter = 0; //Enable the 'Start' button and the 'Indeterminate' check box... startBT.setEnabled(true); indeterminateCB.setEnabled(true); //... and disable the 'Stop' button. stopBT.setEnabled(false); } } }
The first couple of member variables being declared at the above Activity are a NotificationManager object and a Notification object. The latter is the notification itself, the one that will actually turn on the LED at the desired color. The former, as previously stated, is a handle to the system’s notification service (lines 16 and 18). Then, there’s the indeterminate boolean, that flags if the LED should change colors indefinitely or if it should swap colors a predefined number of times (line 20).
The amount of time each color should be displayed and the number of times that the colors should change are defined by the msInterval and transitions variables, declared and initialized at lines 22 and 24. The last three variables being declared are just three View elements that are there to allow the user some control over the application by adding a pair of buttons to start or stop the two color blinking notifications and a check box to set if the blinking should change colors indefinitely (lines 27 through 29).
Finally, inside the onCreate() method where the NotificationManager object is initialized by obtaining a handle to the system’s notification service (line 39). The Notification object is also initialized and configured to use only the device’s LED (lines 42 and 44).
Also, all three View elements are being inflated from the main.xml file (lines 47 through 49). The last two blocks of code inside the onCreate() method define what should these buttons do when they are pressed (lines 53 through 90).
The stopBT button just cancels the notification, sets the indeterminate boolean to false and enables the start button and the ‘indeterminate’ check box. In other words: it cancels any pending notifications making the LED stop change colors (if it’s already doing so) and sets the user interface elements.
The startBT button does almost the opposite: it disables the user interface elements and initializes and calls the execute() method of the TwoColorBlink asynchronous task, that’s later defined in the code (line 60).
This inner class is a child of AsyncTask and is responsible for making the LED blink in two different colors using a background thread, releasing the UI thread from a continuous code execution, avoiding a ‘Application not Responding‘ error. This child class has only a single member: the counter integer. As the name suggests, it’s a variable that gets incremented and it’s value is later read to make the LED change between two colors (line 106).
The first method being overridden in this class is the onPreExecute(). As the name suggests, this method runs before the background thread code execution is started. There, a if statement tests whether the check box is marked and updates the value of the indeterminate boolean accordingly (lines 110 through 121).
The doInBackground() method is where the code that needs to be executed on a background thread is placed. In this case, the code that makes the LED switch between two different colors. It works by first setting the initial color of the LED, which is done at line 132. The color is a integer composed of four hexadecimal pairs in the ARGB format. The first two values correspond to the alpha channel, the other three value pairs are, respectively, the red, green and blue color channels. That said, the 0xFFFF0000 value is: 255 (FF) alpha, 255 (FF) red, 0 (00) green and 0 (00) blue.
After defining the initial color, the if statement checks the value of the indeterminate variable. If it’s true, a while loop condition is set with that variable, meaning that it will run indeterminately until indeterminate is changed to false (line 137). Inside this loop, the notification is posted by calling the notify() method from the notificationManager (line 140). It takes two arguments: the notification ID and a Notification object. These parameters are filled with 0 (zero) for the ID and the object notification for the Notification.
To make the LED temporarily light up in a certain color, the background thread is set to wait the amount of milliseconds defined by the msInterval variable (ine 142). Since the color of the LED can’t be changed after the notification has been posted, the notification must be canceled (line 144), the LED color is changed to different one, and the notification is posted again using the NotificationManager.
To achieve this alternate color change, the thread execution resumes after the specified amount of time, the counter variable is incremented, and the rest of its division by two is compared to zero. Case this computed value is zero, the LED color changes to green, if it isn’t the color changes to red again (lines 146 through 155). The code executes over and over again, posting the notification, cancelling it after a certain amount of time swapping the colors and posting it again, effectively making the LED alternate between two colors.
If the indeterminate variable is false, basically the same code is executed, except that the while checks whether the counter is smaller or equal to the value of the transitions variable. This makes the code change the LED color the exact number of times defined by the transitions variable. For example, in the above code, the LED color will change four times after it’s set to the initial color (lines 159 through 184).
Finally, the code on the onPostExecute() method runs after the background thread execution and it resets the View elements, cancels the notification (if it hasn’t been already cancelled) and makes counter equal to zero.
That’s it for the TwoColorBlink inner class. Now back to the Activity, the onPause() has to be overridden, so that any pending notifications and the indeterminate boolean can be set to false, if the user quits the application.
That’s it! Here’s a video of the application in action:
You don’t need to call notificationManager.cancel(0) in every iteration in the loop, notify will do the job of changing the led color just fine.
Call cancel on post execute (which you do)
Besides that, replace the counter with a boolean value in the case of intermediate, as the counter might over lap, which will cause the crash of your application:
private boolean changeColor = false;
and change it’s value in every loop, like that:
changeColor = !changeColor;
if(changeColor)
notification.ledARGB = 0xFF00FF00;
else
notification.ledARGB = 0xFFFF0000;
Hi DimasTheDriver, this tutorial is very usefull. But when i run this, led in my device not light.My devices has support led(Google Nexus).Please tell me why? Thank you very much!
Hey Quang,
I don’t know why this doesn’t work on the Nexus. Unfortunately, I don’t have a Nexus, so I can’t test this code.
Have you tried different ARGB hex colors than the ones described on the post?
Hi
LED lights work on Google Nexus only then if screen is off (tested my self). So try to run blink code with some delay (so you have time to turn of screen) or put blinker code in OnCreate method and execute app from your developer IDE while your phones screen is off.