Skip to content

September 28, 2012

Android Home Screen Widget: Creating the AppWidgetProvider

Extending AppWidgetProvider is a simple way to encapsulate your app’s management of your home screen widget.  It provides several widget life cycle methods you can override to handle such events as a user adding or removing the widget from their home screen.  It also provides an onUpdate method that ties into the periodic updates the Android system will trigger for your widget.  For my purposes, I disabled this periodic updating because I wanted to control it myself via a Service.

Your AppWidgetProvider interacts with the widget via the RemoteViews class.  The widget is itself hosted in a separate process from the one in which your app runs, and RemoteViews provides methods to cross that gap, update the views and add click listeners.  Note that RemoteViews methods provide only a limited subset of view functionality, and this may restrict what you’d like to do with your widget.  For example, animations are not supported.

I’m going to describe how I created my web community widget.  The java classes involved are:

  • MRPWidget (extends AppWidgetProvider)
  • WidgetUpdate (extends IntentService)
  • MRPAlarmReceiver (extends BroadcastReceiver)

My widget class is named MRPWidget, and it extends AppWidgetProvider.  All visual updates to the widget are handled by this class, as well as starting the service that will perform the updates.  For example, when the widget is enabled it should start the service:

@Override
public void onEnabled(Context context) {
    super.onEnabled(context);

    startWidgetUpdateService(context);
}

protected static void startWidgetUpdateService(Context context) {
    Intent serviceIntent = new Intent(context, WidgetUpdate.class);
    context.startService(serviceIntent);
}

Next, I added a method to interact with the widget layout views via RemoteViews. This method will be called from the WidgetUpdate service. It looks like this:

public void updateMRP(Context context, List<IntentPost> posts, List<Member> activeMembers, WidgetAlert pmAlert, WidgetAlert replyAlert) {
    // These three lines retrieve the widget IDs, which are Android identifiers
    // pointing to your widgets. It may only be one widget, but the user
    // can add multiple instances to the home screen. In my case it wouldn't
    // make sense for a user to do so, but Android allows for multiple
    // widget instances.
    ComponentName thisWidget = new ComponentName(context, MRPWidget.class);
    AppWidgetManager mgr = AppWidgetManager.getInstance(context);
    int[] widgetIds = mgr.getAppWidgetIds(thisWidget);

    // loop through and update each widget instance
    for (int widgetId : widgetIds) {
        // create the RemoteViews object, passing your widget's layout
        // (in this case my layout is in a file called mrp_widget under res/layout
        RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.mrp_widget);

        // ** VIEW CODE OMITTED HERE **
        // this is where you would place all your calls to RemoteViews to update
        // your widget. Something like:
        views.setTextViewText(R.id.mrp_title_text, "This is a title");
        views.setViewVisibility(R.id.mrp_icon, View.VISIBLE);
        // You'll have to research RemoteViews a little to see what is possible

        // to add a click listener to a view in the widget layout, you would
        // do something like:
        PendingIntent click = PendingIntent.getActivity(context, 0, myIntent, 0);
        views.setOnClickPendingIntent(R.id.mrp_widget_top, click);
        // where myIntent is the Intent you want to fire

        // You must call updateAppWidget, passing in the widget ID,
        // to "commit" your changes.
        mgr.updateAppWidget(widgetId, views);
    }
}

In order to configure this widget, there must be a configuration XML file which you place in res/xml. My file is named mrp_widget_info.xml and looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<appwidget-provider
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:initialLayout="@layout/mrp_widget"
  android:minWidth="110dp"
  android:minHeight="40dp"
  android:label="My Widget"
  android:updatePeriodMillis="0"
  />
  • initialLayout specifies which layout to use, and this will get inflated onto the screen before your AppWidgetProvider has a chance to fill in any data or make changes. So in my layout I have some of the text fields preset to dummy values (“—“) so that the user knows the widget hasn’t yet updated itself with real data.
  • minWidth and minHeight specify how many cells your widget requires on the screen. Look up possible values here. 110dp by 40dp specifies a 2×1 cell area.
  • label is not used as far as I can tell, because Android uses the label specified in the Android manifest file (which I’ll get to next).
  • updatePeriodMillis specifies how frequently the system should call upon your AppWidgetProvider to update (i.e. trigger its onUpdate method). Because I’m using a service instead to handle the updates, I set it to zero to cancel this functionality.

The next step is to register your widget in the Android manifest file. An AppWidgetProvider is actually a type of Broadcast Receiver, and you register it likewise:

<receiver android:name="com.kingdom.android.widget.MRPWidget"
  android:label="MRP Widget">
  <intent-filter>
    <!-- this is the intent filter for Android's widget-related events -->
    <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
  </intent-filter>
  <meta-data
    android:name="android.appwidget.provider"
    android:resource="@xml/mrp_widget_info" />
</receiver>

Of course, the final thing you need to complete this picture is the layout file itself, located in res/layout. Widget layouts are not much different than Activity layouts, except you may only use a sub-set of the built-in view types. The details of creating a layout are beyond the scope of this example.

Next, I will describe how my widget is updated via a service to keep the data fresh while preserving battery life and minimizing data usage.  See updating an Android widget with a service only when it’s visible.

Read more from Android Development

Share your thoughts, post a comment.

(required)
(required)

Note: HTML is allowed. Your email address will never be published.

Subscribe to comments