Mastering Android Widget Development - Part1


SDK Version: 
M3

In Days to Xmas tutorial you can see a simple widget example, which demonstrates what widgets are used for, and shows an example how they can work. Now I begin a series of tutorials to fully explain the working of widgets.
We will also create a sample application, during the tutorials, which will show a countdown to a given date in secunds, but things that are not required for this specific example applications will be explained too.

For this first part I will go though mainly the parts described in http://developer.android.com/guide/topics/appwidgets/index.html but I try to give more explanation and advice.

Some general thoughts at first:

Most of the applications has a launcher activity, and the running begins with that, but its not necessary to have one. If you don't have one, the application wont show among the other installed programs giving the user the opportunity to run it.

If an application has a class that implements the AppwidgetProwider class, it will be showed among the available widgets.

Our application will be made up from the widget itself and a configuration activity where you can set the date to countdown to. In the architecture of widgets a "configuration" activity can be defined exactly for this, so we will not need the application to have a launcher activity, if there is a configuration defined it will automatically launched when a new widget is placed on the home screen.

When working with widgets you must keep in mind that the users can place multiple instances of the same widget to the home screen. The functionality of this case must be planned and coded. In the countdown widget id would be fine to the different instances to count to a different date. It will be our goal.

Lets see the basics of how widgets work:

Widgets use 4 intents
•ACTION_APPWIDGET_UPDATE
•ACTION_APPWIDGET_DELETED
•ACTION_APPWIDGET_ENABLED
•ACTION_APPWIDGET_DISABLED

You have to create an XML file with some metadata about the widget. For example countwidget_info.xml :

  1. <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
  2.         android:minWidth="294dp"
  3.         android:minHeight="72dp"
  4.         android:updatePeriodMillis="86400000"
  5.         android:initialLayout="@layout/countdownwidget&quot;
  6.         android:configure="com.helloandroid.countdownexample.countdownConfigure&quot; >
  7. </appwidget-provider>

As you can see here we set:
•the widget size (you cant size it as you like, for details see App Widget Design Guidelines)
•the layout used
•the configuration activity mentioned before
•and the updateperiod

The update period is limited, for example in 1.5 it is said to refres every 30 min even if you set a shorter period.
If you set this an ACTION_APPWIDGET_UPDATE intent will be generated to perform the update.
If the device is asleep when it is time for an update then the device will wake up in order to perform the update.
Because the period can not be set as short as you like, and it wakes up the device we wont use it, we will generate the ACTION_APPWIDGET_UPDATE intents ourself using the AlarmManager class.

The widget must be registered in the AndroidMaifest.xml like this:

  1. <receiver android:name="CountdownWidget" >
  2.     <intent-filter>
  3.         <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
  4.     </intent-filter>    
  5.         <meta-data android:name="android.appwidget.provider"              
  6.         android:resource="@xml/countwidget_info&quot; />
  7. </receiver>

Here we set the CountWidget class to capture the APPWIDGET_UPDATE intent and attach the previously described metadata xml to it.
The system automatically cares about to the DELETE, ENABLE and DISABLE broadsets get captured too.

Now create the CountWidget class which implements the AppWidgetProvider.
At firs lest see the empty methods that we can owerride from the AppWidgetProvider.
In the comments I explain what they are used for.

  1. package com.helloandroid.countdownexample;
  2.  
  3. import android.appwidget.AppWidgetManager;
  4. import android.appwidget.AppWidgetProvider;
  5. import android.content.Context;
  6. import android.content.Intent;
  7.  
  8. public class CountdownWidget extends AppWidgetProvider {
  9.  
  10.         @Override
  11.         public void onDeleted(Context context, int[] appWidgetIds) {
  12.                 //called when widgets are deleted
  13.                 //see that you get an array of widgetIds which are deleted
  14.                 //so handle the delete of multiple widgets in an iteration
  15.                 super.onDeleted(context, appWidgetIds);
  16.         }
  17.  
  18.         @Override
  19.         public void onDisabled(Context context) {
  20.                 super.onDisabled(context);
  21.                 //runs when all of the instances of the widget are deleted from
  22.                 //the home screen
  23.                 //here you can do some setup
  24.         }
  25.  
  26.         @Override
  27.         public void onEnabled(Context context) {
  28.                 super.onEnabled(context);
  29.                 //runs when all of the first instance of the widget are placed
  30.                 //on the home screen
  31.         }
  32.  
  33.         @Override
  34.         public void onReceive(Context context, Intent intent) {
  35.                 //all the intents get handled by this method
  36.                 //mainly used to handle self created intents, which are not
  37.                 //handled by any other method
  38.                
  39.                
  40.                 //the super call delegates the action to the other methods
  41.                
  42.                 //for example the APPWIDGET_UPDATE intent arrives here first
  43.                 //and the super call executes the onUpdate in this case
  44.                 //so it is even possible to handle the functionality of the
  45.                 //other methods here
  46.                 //or if you don't call super you can overwrite the standard
  47.                 //flow of intent handling
  48.                 super.onReceive(context, intent);
  49.         }
  50.  
  51.         @Override
  52.         public void onUpdate(Context context, AppWidgetManager appWidgetManager,
  53.                         int[] appWidgetIds) {
  54.                 //runs on APPWIDGET_UPDATE
  55.                 //here is the widget content set, and updated
  56.                 //it is called once when the widget created
  57.                 //and periodically as set in the metadata xml
  58.                
  59.                 //the layout modifications can be done using the AppWidgetManager
  60.                 //passed in the parameter, we will discuss it later
  61.                
  62.                 //the appWidgetIds contains the Ids of all the widget instances
  63.                 //so here you want likely update all of them in an iteration
  64.                
  65.                 //we will use only the first creation run
  66.                 super.onUpdate(context, appWidgetManager, appWidgetIds);
  67.         }
  68.  
  69. }

And one last thing you must know for the beginning, there is a bug in android 1.5 that the onDeleted method is not called. The code below placed in the onRecive fixes the problem.
  1. final String action = intent.getAction();
  2.     if (AppWidgetManager.ACTION_APPWIDGET_DELETED.equals(action)) {
  3.         final int appWidgetId = extras.getInt
  4. (AppWidgetManager.EXTRA_APPWIDGET_ID,
  5.                 AppWidgetManager.INVALID_APPWIDGET_ID);
  6.         if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
  7.             this.onDeleted(context, new int[] { appWidgetId });
  8.         }
  9.     } else {
  10.         super.onReceive(context, intent);
  11.     }

In the next weeks tutorial we will start coding the application.