2.1: App widgets

Contents:

An app widget is a miniature app view that appears on the Android home screen and can be updated periodically with new data. App widgets display small amounts of information or perform simple functions such as showing the time, summarizing the day's calendar events, or controlling music playback.  Android app widgets

In this chapter you learn how to provide an app widget for your Android app, how to update that app widget with new data, and how to provide click actions for that widget or any of its parts.

Note: The term "widget" in Android also commonly refers to the user interface elements (views) you use to build an app, such as buttons and checkboxes. In this chapter, all instances of the word widget refer to app widgets.

About app widgets

App widgets are miniature apps that appear on any Android home screen panel. Widgets provide an at-a-glance view of your app's most important data and features, even if that app is not running.

When the user installs your app on a device, that app's associated widgets are listed on the widget install screen. To get to the widget install screen, touch & hold on an empty spot on a home screen and tap Widgets. Touch & hold any widget to pick it up and add it to a home screen, move it around, or resize it, if it is resizeable.

Note: As of Android 5.0, widgets can only be placed on the Android home screen. Previous versions of Android (4.2/API 17 to 4.4/API 19) also allowed widgets to appear on the lock screen (keyguard). Although the app widget tools and APIs still occasionally mention lock screen widgets, that functionality is deprecated. This chapter discusses only the home-screen app widgets.

App widgets can display data, be interactive, or both. Data widgets can have a simple flat layout (such as a clock or weather widget) or use a scrolling list for a collection of items (such as a calendar or to do list widget). App widget controls can provide shortcut actions (such as play/pause for a music player). Even simple app widgets may include an action to open their associated app when tapped.

As a general rule, app widgets should be small, simple, and provide limited information or functionality.

The components of an app widget

App widgets are add-ons for an existing app and are made available to users when the app is installed on a device. Your app can have multiple widgets. You cannot create a stand-alone app widget without an associated app.

App widgets include the following components in your Android Studio project. You learn more about these components in the latter part of this chapter.

  • Provider-info file: App widgets include an XML file that defines metadata about the widget. That metadata includes the widget's initial or minimum size, its update interval, configuration activity (if any), and preview image.
  • Layout: You define the user interface for your app widget in an XML layout file, just as you do for regular apps. However, app widgets support a more limited set of layouts and views than regular apps.
  • Widget-provider class: The app widget provider contains the Java code for your app widget. App widgets are actually broadcast receivers—they extend the AppWidgetProvider class, which in turn extends from BroadcastReceiver. AppWidgetProvider objects receive only broadcasts relevant to the app widget, including intents for update, enabled, disabled, and deleted. As with all broadcast receivers, app widget providers are declared in the Android manifest.
  • Configuration activity (optional): If your app widget must be configured before it is used, you can implement an activity that provides that configuration. The configuration activity is then listed in the app widget's provider-info file and is automatically launched when the user adds a widget to the home screen. You can see this behavior with the Android News & Weather widget, which asks you to choose whether to display weather and stories, weather only, or stories only.

App widgets exist within a system framework controlled by the app widget manager and the app widget host.

The app widget manager (AppWidgetManager) manages widget updates and sends the broadcast intents that app widget providers receive to do the actual work of updating.

The app widget host (AppWidgetHost) is an app component that can hold and display other app widgets. The Android home screen is by far the most frequently used app widget host, although it is possible to create your own app widget host.

App widget updates

App widgets that display information can be updated regularly for new or updated information. The data a widget contains can be updated in two ways:

  • The widget updates itself at regular intervals. You can define the interval in the widget's provider-info file, but that interval must be at least 30 minutes.
  • The widget's associated app can request a widget update explicitly.

In both these cases, the update occurs because the app widget manager sends a broadcast intent with the action ACTION_APPWIDGET_UPDATE. The app widget-provider class receives that intent and calls the onUpdate() method.

Implement the onUpdate() method in your widget-provider class to update your widget. For simple widgets, you might just update the data in the widget's views. More complex widgets may need to interact with additional services to retrieve data from content providers or other data storage locations.

Update performance

Widget updates consume device resources such as battery life. Updates occur whether or not the underlying app is running, and widgets can wake a sleeping device to perform the update. It is a best practice to design your widgets so that they do not need frequent updates, to conserve these device resources.

Note: If your widget must update more frequently than every 30 minutes or you do not need to update while the device is asleep, use an AlarmManager. Set the alarm type to either ELAPSED_REALTIME or RTC, which will only deliver the alarm when the device is awake.

Configurable app widgets

Some widgets need to be configured before they can be used. For example, a widget that displays a single photo would need to prompt the user to pick the photo from a gallery. A stock-ticker widget would need a list of stock symbols to display.

The configuration activity appears when the user first adds your widget onto the home screen.  Example app widget configuration screen

With configurable widgets, the user can place multiple copies of the same widget on their home screens, with different configurations. If any of those widgets receives an update request, you must update all the installed widgets at once.

You learn more about widget configuration activities in Using a configuration activity, below.

Adding an app widget to an app

To add a widget to your app project in Android Studio, select File > New > Widget > App Widget.

The New Android Component dialog appears for your new widget:  App widget configuration screen

In the above figure:

  • Class Name is the name for your widget's provider class. This class should contain the word "Widget."
  • Placement should almost always be Home-screen only.
  • Resizeable indicates whether your widget should be resizeable after it is placed. You can restrict the resizability of your widget to horizontal or vertical only, or make your widget not resizeable.
  • Minimum Width and Minimum Height are the minimum sizes for your widget, measured in cells on the home screen.
  • Configuration Screen is an option to include an initial widget configuration activity.

After you click Finish, several files are added to or modified in your project. For example, if you name your app widget NewAppWidget:

  • A provider class called NewAppWidget.java is added to your project.
  • A new layout file is added for the widget in res/layouts/new_app_widget.xml.
  • A new provider-info file is added in res/xml/new_app_widget_info.xml.
  • If you selected Configuration Screen, a new configuration activity class called NewAppWidgetConfigurationActivity.java is added to your project.
  • The Android manifest is updated to include the provider and configuration activity classes.

Updating the widget provider-info file

The provider-info file defines metadata and initial properties for your app widget, including the widget's initial size, update interval, and configuration activity. These properties are used to display your widget in the widget picker, to configure the widget (if configuration is needed), and to place the widget in the right number of cells on the home screen.

The provider info is an XML resource located in the res/xml/ folder, and it contains a single <appwidget-provider> element. Android Studio creates a template provider-info file when you add a new widget, based on the values you provided in the New Android Component dialog.

Here's an example of a provider-info file.

<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider   
   xmlns:android="http://schemas.android.com/apk/res/android"
   android:minHeight="40dp"
   android:minWidth="40dp"
   android:initialLayout="@layout/new_app_widget"
   android:updatePeriodMillis="86400000"
   android:previewImage="@drawable/new_appwidget_preview"
   android:resizeMode="horizontal|vertical"
   android:widgetCategory="home_screen"
   android:configure=
      "com.example.android.widgettest.MyAppWidgetConfigureActivity">
</appwidget-provider>

The following sections describe key attributes of the file. See the AppWidgetProviderInfo class for more information on the attributes for the <appwidget-provider> element.

android:minHeight and android:minWidth attributes

The android:minHeight and android:minWidth attributes define the minimum initial size of the widget, in dp. Android Studio provides these values in the provider-info file based on the number of grid spaces you specify when you create the widget.

The Android home screen provides a grid of available spaces (cells) into which users can place widgets and icons. The available cells in the grid vary by device—phones may only allow a 4x4 grid, but tablets can offer a larger grid. When your widget is added to a user's home screen, it is stretched both horizontally and vertically to occupy as many grid cells as satisfy the minWidth and minHeight values.

The rule for how many dp fit into a grid cell is based on the equation 70 × grid_size − 30, where grid_size is the number of cells you want your widget to take up. Generally speaking, you can use this table to determine what your minWidth and minHeight should be:

# of columns or rows minWidth or minHeight
1 40 dp
2 110 dp
3 180 dp
4 250 dp

android:initialLayout attribute

The android:initialLayout attribute defines the XML layout file for your widget, for example @layout/my_app_widget. You will learn more about widget layout in the next section.

android:updatePeriodMillis attribute

The android:updatePeriodMillis attribute defines update interval in milliseconds for the app widget, that is, how often the app widget manager should send a broadcast intent to update the widget. The default update interval is 86,400,000 milliseconds, or 24 hours. The minimum possible update interval is 1,800,000 milliseconds or 30 minutes. Because widget updates use device resources and may even awaken a sleeping device, design your widget to update as infrequently as possible (use a larger value for android:updatePeriodMillis).

To disable automatic updates, set android:updatePeriodMillis to 0.

Note: The android:updatePeriodMillis value determines how often the app widget manager requests an update for an app widget. A running app can update its associated widget at any time.

android:previewImage attribute

The android:previewImage attribute specifies an image or drawable that previews what the app widget will look like when it has been configured and placed on the user's home screen. The user sees the preview image in the widget picker when they choose an app widget.

The widget preview can be any image or drawable in the res/drawable folder. When you create a new widget, Android Studio provides an "example" widget preview image for you (@drawable/new_appwidget_preview)  Default widget preview image

Replace this sample preview with any image or drawable that better represents your app widget. If you do not include the android:previewImage line in the provider-info file, the launcher icon for your app is used instead.

android:resizeMode attribute

If your app widget is resizeable, the android:resizeMode attribute specifies whether your widget can be resized horizontally, vertically, both, or neither. The possible values are "horizontal", "vertical", "horizontal|vertical", and "none".

Users touch & hold a widget to show the widget's resize handles, then drag the handles to change the size of the widget on the home screen grid.

android:widgetCategory attribute

The android:widgetCategory attribute declares whether your app widget can be displayed on the home screen ("home_screen"), the lock screen ("keyguard"), or both. Android Studio provides this attribute based on the value you choose for the Placement drop-down menu when you create the widget. For Android 5.0 and higher, only home_screen is valid. Keyguard (lock screen) widgets were only available in Android 4.2 (API 17) to Android 4.4.4 (API 19.0).

android:configure attribute

The android:configure attribute defines the app widget's configuration activity, if you selected the Configuration Screen checkbox in the Add Android Component dialog. The configuration activity is specified in this attribute with its fully qualified class name. You will learn more about configuration activities later in this chapter.

Defining the widget layout

You can create app widget layouts in the Android Studio Design tab, the same way you create activity layouts. However, app widget layouts are based on RemoteViews object hierarchies, not View object hierarchies. Remote views do not support every kind of layout or view that standard views support, and remote views do not support custom views.

How are remote views different from regular views? A remote view is a view hierarchy that can be displayed in a process different from the original app but with the same permissions. Widgets and custom notifications are the primary examples for this situation.

Each time your widget updates, you create a RemoteViews object from the layout, update the views in the layout with data, and then pass that remote view to the app widget manager to display.

Because app widgets are typically small in size and display limited information, you should design your widgets with simple layouts. The Widgets and App Widget Design Guidelines pages provide guidelines and suggestions for good app widget designs.

Here's a simple example layout for a 1x2 weather widget (one cell wide and two cells high). This app widget shows an image, a text view, and a button, in a linear layout.  Simple app widget layout

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:background="@color/colorPrimary"
   android:orientation="vertical"
   android:padding="@dimen/widget_margin">

   <ImageView
       android:id="@+id/imageView"
       android:layout_width="wrap_content"
       android:layout_height="0dp"
       android:paddingTop="8dp"
       android:layout_gravity="center"
       android:layout_weight="2"
       android:src="@drawable/sun" />

   <TextView
       android:id="@+id/textView"
       android:layout_width="match_parent"
       android:layout_height="0dp"
       android:layout_weight="1"
       android:gravity="center"
       android:text="45F"
       android:textSize="18dp"
       android:textStyle="bold" />

   <Button
       android:id="@+id/actionButton"
       android:layout_width="match_parent"
       android:layout_height="0dp"
       android:layout_weight="1"
       android:text="Refresh" />
</LinearLayout>

Supported views

A RemoteViews object (and, consequently, an app widget) can support the following layout classes:

And the following view classes:

Descendants of these classes are not supported. The RemoteViews class also supports ViewStub, which is an invisible, zero-sized View. You can use ViewStub to lazily inflate layout resources at runtime.

Widget margins

App widgets look best with a little extra space around the edges so that the widgets do not display edge-to-edge on the user's home screen. Before Android 4.0 (API 14), you had to add that extra space to your widget layout yourself. After API 14 the system adds the space for you. If your app targets a version of Android higher than API 14, do not add space to the edges of your app widgets.

When you use Android Studio to create your app widget, Android Studio automatically creates a layout and dimension resources that support both older (pre-API 14) and newer Android versions. Specifically, the layout template that Android Studio creates includes an android:padding attribute:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:background="#09C"
   android:padding="@dimen/widget_margin">
...
</RelativeLayout>

The widget_margin value is defined in the default dimens.xml file (res/values/dimens.xml):

<dimen name="widget_margin">8dp</dimen>

When Android Studio creates your widget it also adds a dimens.xml (v14) (res/values-v14/dimens.xml) file, which removes the extra space for API 14 and higher versions:

<dimen name="widget_margin">0dp</dimen>

You do not need to do anything additional to support widget padding.

Implementing the widget-provider class

The widget-provider class implements the widget's behavior such as updating the widget's data at regular intervals or providing on-click behavior for the widget itself or any of its components.

Widget providers are subclasses of the AppWidgetProvider class. The AppWidgetProvider class extends BroadcastReceiver as a convenience class to receive and handle broadcasts specific to app widgets, such as when the app widget is updated, deleted, enabled, and disabled.

To create a widget provider:

  1. Extend AppWidgetProvider for your own class.
  2. Override the onUpdate() method, at minimum, to construct the widget layout and manage widget updates.
  3. Optionally, implement additional widget actions.
  4. Declare the widget provider as a broadcast receiver in the app manifest (AndroidManifest.xml).

When you create a new widget, Android Studio provides a template widget-provider class for you that does most of this implementation for you.

Create a widget-provider class

This is a skeleton widget-provider class:

public class NewAppWidget extends AppWidgetProvider {
   // Standard onUpdate() method override
   @Override
   public void onUpdate(Context context, 
      AppWidgetManager appWidgetManager, int[] appWidgetIds) {   

      // There may be multiple widgets active, so update all of them
      for (int appWidgetId : appWidgetIds) {
         // Update the app widget
       }
   }
}

In addition to onUpdate(), the AppWidgetProvider class includes other handler methods for different events including onDeleted(), onEnabled(), and onDisabled(). See AppWidgetProvider for more details on these methods.

Declare the widget provider in the Android manifest

The app widget-provider class you create is a broadcast receiver, and it must be declared in the Android manifest the same way as any other receiver. Android Studio adds a <receiver> element to your manifest when you add a new widget:

<receiver android:name="NewAppWidgetProvider" >
    <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>

The key parts of this element are as follows:

  • The name attribute in <receiver> contains the class name for the widget provider.
  • The <intent-filter> element declares an action of APPWIDGET_UPDATE. This element indicates that the receiver can receive broadcast intents from the app widget manager to update the widget.
  • The <meta-data> element specifies the location of the widget provider-info file.

Implement widget updates

For your widget to receive updated data, implement the onUpdate() method in your provider class. The onUpdate() method is called when the user adds the app widget, and every time the widget receives an update broadcast intent from the app widget manager or from your associated app. Perform all essential widget setup in the onUpdate() method, such as creating the widget layout, defining event handlers for views, or starting services.

Note: If your app widget includes a configuration activity, the onUpdate() method is not called when the user adds the app widget, but it is called for all subsequent updates. Your configuration activity must request the first update when configuration is complete. See Using a configuration activity for details.

Here is a simple onUpdate() method:

@Override
public void onUpdate(Context context, 
      AppWidgetManager appWidgetManager, int[] appWidgetIds) {   

   // There may be multiple widgets active, so update all of them
   for (int appWidgetId : appWidgetIds) {
     // Construct the RemoteViews object
     RemoteViews views = new RemoteViews(
         context.getPackageName(), R.layout.new_app_widget2);
     // Update a text view to display the app widget ID
     views.setTextViewText(R.id.appwidget_id,
       String.format("%s",appWidgetId));
     // Instruct the widget manager to update the widget
     appWidgetManager.updateAppWidget(appWidgetId, views);
}

The core of the onUpdate() method is a loop that iterates over an array of widget IDs. All instances of your widget that are currently active are identified by an internal ID. When onUpdate() is called, it is passed and array of IDs for the widgets that need updating. Your widget may have multiple instances active for several reasons, including most typically one widget with different configurations. For example, multiple weather widgets may be active to display the current weather in different locations. Make sure your onUpdate() method loops through all the widgets by ID and updates each widget individually.

In each update, you also need to reconstruct the widget's layout by creating a new RemoteViews object and updating any data that the remote view contains. The last line in the loop tells the app widget manager to update the widget with the current RemoteViews object.

Provide app widget actions

Some app widgets only display information. Other widgets perform actions when tapped, such as launching the associated app. You can create click-event handlers to perform actions for the widget as a whole, or for any view of the widget layout such as a button.

Unlike activities where the click-event handlers run methods in your activity code, you attach actions to your widget with pending intents. The setOnClickPendingIntent() method (defined in the RemoteViews class) enables you to connect a PendingIntent object to one or more views in your widget. The Android system delivers those intents to your app (or to any other app).

Add click-event handlers to your onUpdate() method. For example, this code launches the widget's associated app.

public void onUpdate(Context context, 
   AppWidgetManager appWidgetManager, int[] appWidgetIds) {   
   ...
   // Create a new explicit intent object
   Intent intent = new Intent(context, MainActivity.class);
   // Wrap that intent in a pending intent that starts a new activity
   PendingIntent configPendingIntent = 
      PendingIntent.getActivity(context, 0, intent, 0);
   // Attach the pending intent to a view in the layout file 
   // for the widget (here, appwidget_layout is the entire widget)
   views.setOnClickPendingIntent(
      R.id.appwidget_layout, configPendingIntent);
   ...
}

Using a configuration activity

For some widgets it makes sense to let the user configure the settings for a new widget when they add that widget. For example, a stock-ticker widget needs the stock symbol that the user is interested in.

To provide widget configuration, you can add an optional widget configuration activity to your app's project. The configuration activity is launched when the user first adds your widget onto the home screen, and typically never appears again. (If the user needs to change the configuration of a widget, they have to delete the current widget and add a new one.)

Add a configuration activity to your project

To add the widget configuration activity to your project when you initially create the widget in Android Studio, select the Configuration Screen checkbox.  Configuration Screen checkbox

You can also create a configuration activity manually, but adding it with the Android Studio wizard provides a lot of template code that can save you a lot of work.

The configuration activity is declared in the Android manifest file, with an intent-filter that accepts the ACTION_APPWIDGET_CONFIGURE action. For example:

<activity android:name=".ExampleAppWidgetConfigure">
    <intent-filter>
        <action 
           android:name=
           "android.appwidget.action.APPWIDGET_CONFIGURE"/>
    </intent-filter>
</activity>

The configuration activity must also be declared in the provider-info XML file, using the android:configure attribute. (See Updating the provider info, above.) For example:

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:configure="com.example.android.ExampleAppWidgetConfigure"
    ... >
</appwidget-provider>

Both of these declarations (in the manifest and in the provider-info file) are done for you automatically when you add a new widget using the Android Studio wizard.

Implement the configuration activity

Implement your widget's configuration activity the same way you implement any other activity. If you created the configuration activity when you used the wizard to add a widget to your project, the generated configuration activity class can serve as a starting point.

It is the activity's responsibility to update the app widget when configuration is complete. Here's a summary of the procedure to properly update the app widget and close the configuration activity.

  1. In your onCreate() method, get the widget ID from the intent that launched the activity. You'll need it to update the widget when configuration is complete. The app widget ID is in the incoming intent extras, with the key AppWidgetManager.EXTRA_APPWIDGET_ID.
     Intent intent = getIntent();
     Bundle extras = intent.getExtras();
     if (extras != null) {
         mAppWidgetId = extras.getInt(
            AppWidgetManager.EXTRA_APPWIDGET_ID,
            AppWidgetManager.INVALID_APPWIDGET_ID);
     }
    
  2. Also in onCreate(), set the default activity result to RESULT_CANCELED. This covers the case where the user backs out of widget configuration before it is complete. A canceled result alerts the widget host (the Android home screen) that the widget should not be added.
     // Set the result to CANCELED. This causes the widget host to cancel
     // the widget placement if the user presses the back button.
     setResult(RESULT_CANCELED);
    
  3. Get the widget configuration data from the user as needed, and store that data so the widget can access it later. For example, store the data in shared preferences.
  4. The method that closes the activity is often a click-event handler for a button that the user clicks when the app widget configuration is complete. In that method, request an update for the widget. Here is the code to do that:
     // Get an instance of the AppWidgetManager:
     AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(this);
     // Create a new RemoteViews object with the widget layout resource
     RemoteViews views = new RemoteViews(context.getPackageName(),
        R.layout.example_appwidget);
     // Update the app widget with the widget ID and the new remote view:
     appWidgetManager.updateAppWidget(mAppWidgetId, views);
    
  5. To finish and close the activity, create a new intent and add the original app widget ID to the intent extras with the key AppWidgetManager.EXTRA_APPWIDGET_ID. Set the result to RESULT_OK and call the finish() method:
     Intent resultValue = new Intent();
     resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, 
        mAppWidgetId);
     setResult(RESULT_OK, resultValue);
     finish();
    

The related practical documentation is in Building app widgets.

Learn more

Android developer documentation:

Android API Reference:

results matching ""

    No results matching ""