Featured Posts

How-to: An Android widget

Introduction

A widget is a mini application that can be embedded in other applications, most often the Home screen.

Requirements

Ideally, you should have a background service running, so that the widget can communicate with it directly using Intents, instead of calling the main program’s activity.

Advantages and disadvantages

Having a widget is a good addition to your existing app. It allows the user to interact with the app, or its running services, without opening the main app. This creates convenience, and will mean the user is more likely to use our app over our competitors.

 

The disadvantage here is that widgets are hard to design well, because of space constraints. In addition, you have to be selective with what you display, making sure that the information being displayed is relevant and most interesting to the user, without having to check the main app for the full details.

 

Steps

Creating a widget has 2 steps. The first is to specify your Widget object in your manifest file.

<receiver android:name="ExampleAppWidgetProvider" >
    <intent-filter>
        <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
    </intent-filter>
    <meta-data android:name="android.appwidget.provider"
               android:resource="@xml/example_appwidget_info" />
</receiver>

Android:name here is the name of the class extending AppWidgetProvider that you have/will implement. The only filter you need to add is android.appwidget.action.APPWIDGET_UPDATE. You will be sending intents directly to the widget, so declaring other actions your widget will handle is not necessary here.

 

In the meta-data section, android:name=”android:appwidget.provider” signals that this is the descriptor for your app’s widget. The android:resource attribute specifies the resource that will be used to define the basics of your widget. This file should be in the directory res/xml/file.xml

 

An example:

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="294dp"
    android:minHeight="72dp"
    android:updatePeriodMillis="86400000"
    android:previewImage="@drawable/preview"
    android:initialLayout="@layout/example_appwidget"
    android:configure="com.example.android.ExampleAppWidgetConfigure"
    android:resizeMode="horizontal|vertical">
</appwidget-provider>

The most interesting attributes are updatePeriodMilis, previewImage and configure. UpdatePeriodMilis specify how often your app will update (beware that there is a 30 min gap added to this, meaning using this attribute will not allow your app to update at a frequency of more than twice per hour). PreviewImage points to an image of your widget, shown when the user selects your widget. If not specified, it will use your app icon. Configure specifies an activity that will be used to configure your widget. ResizeMode specify whether your widget can be resized or not. Note that resizeMode is introduced in Android 3.1.

Next up is defining your widget’s layout. Note that for widgets, only these are allowed (not their subclasses):

Layouts: FrameLayout, LinearLayout, RelativeLayout

Classes: AnalogClock, Button, Chronometer, ImageButton, ImageView, ProgressBar, TextView, ViewFlipper, ListView, GridView, StackView, AdapterViewFlipper

 

In addition to this, you should not allow your widget to extend all the way to the edges. This means it may touch other widgets, which will not look nice. Therefore, a padding of 8dp is advised.

 

Now, you will need to define your widget class. What you need to do is subclass AppWidgetProvider and override some of the following methods:

 

onUpdate(): this is called at intervals defined in the XML file. This is also called when the user adds the widget, so you should have setup codes here, unless you also override onEnabled. The codes to update your widget GUI should also be here.

 

onEnabled(): This is called when the user add the first instance of your widget. Note that this is only called for the first time. Subsequent instances will not trigger this. If you need a global setup for all your widgets, this is the place.

 

onDisabled(): Called when the last instance of your widget is removed. This is where the final clean up work should be.

 

onDeleted(): Called every time the user removes an instance of your widget.

 

onReceive(): Called for each broadcast and before all the above methods. Android already handles intent filtering and appropriate method calls, so this is usually not needed.

 

That’s the basics of a widget! However, what if you need a widget that updates itself more often than once every 30 minutes? Like a media player widget that updates once a second? Here’s how to do it:

 

First, change updatePeriodMillis to 0. Next up, you should override onEnabled. The strategy here is to use AlarmManager to set up a repeating alarm that will run an intent. Something like this:

 

public void onEnabled(Context context) {
      AlarmManager manager = (AlarmManager) context
                  .getSystemService(Context.ALARM_SERVICE);
      Intent intent = new Intent(context, MyPlayerWidget.class);
      PendingIntent pIntent = PendingIntent.getBroadcast(context, 0, intent,
                  PendingIntent.FLAG_UPDATE_CURRENT);
      manager.setRepeating(AlarmManager.RTC,
                  System.currentTimeMillis() + 1000, 1000, pIntent);
}

 

You’ve just created an alarm that will go off every second! SetRepeating is the method to use in order to set a repeating alarm. The parameters are: alarm type, timestamp for the first alarm occasion, interval in milliseconds, and the PendingIntent you’ll use. For alarm type, there are 2 main types, the first being normal ones and the second being ones ending with WAKE_UP. The first type, when the device is not being used, will not wake the device to perform the intent. Instead, it will execute the intent when the device next wakes up. WAKE_UP will wake the device to execute the intent.

 

I recommend setting the type to RTC, because it uses the system’s current time using System.currentTimeMilis(). It is more familiar compared to ELAPSED_REALTIME, which is the time since boot.

 

Next, you will need to override onReceive to call your onUpdate method every time the alarm triggers and deliver the intent. Example:

 

public void onReceive(Context context, Intent intent) {
      super.onReceive(context, intent);
      ComponentName thisAppWidget = new ComponentName(
                  context.getPackageName(), getClass().getName());
      AppWidgetManager appWidgetManager = AppWidgetManager
                  .getInstance(context);
      int ids[] = appWidgetManager.getAppWidgetIds(thisAppWidget);
      onUpdate(context, appWidgetManager, ids);
}

 

And of course, you should have your onUpdate method update your GUI with relevant information every time the alarm ticks. You can do that using the code snippet below:

 

public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
      ...
      ComponentName thisWidget = new ComponentName(context,
                  MyPlayerWidget.class);

 

      RemoteViews views = new RemoteViews(context.getPackageName(),
                        R.layout.widget);
      views.setOnClickPendingIntent(R.id.widget_play_pause, pIntent);

 

      views.setTextViewText(R.id.widget_textView, "Updated text!");
      ... more update stuff ...
      appWidgetManager.updateAppWidget(thisWidget, views);
}

References:

Google (2012). App Widgets. Available: http://developer.android.com/guide/topics/appwidgets/index.html Last accessed 7th May 2012.

Standard

Leave a comment