Best practices for widget development (Android)
An application widget is a small application view that can be embedded in other applications, such as the home screen, to receive periodic updates. This document describes how to publish an Android widget using an App Widget provider.
Overview
These application views are called widgets, which you can publish with an App Widget provider. An application component that can hold other widgets, such as the Launcher, is called an App Widget host. The following figure shows examples of clock and weather widgets. For more information about widget design specifications, see App Widgets Overview.
To create a widget, you must first understand the following concepts.
AppWidgetProviderInfo
Describes the metadata for a widget, such as its layout, update frequency, and the AppWidgetProvider class.
AppWidgetProvider
Handles broadcast events for the widget. It receives broadcasts when the widget is updated, enabled, disabled, or deleted.
View layout
Defines the initial layout of the widget in XML.
Other
You can also create a configuration activity for your widget. This activity starts after the widget is added and allows users to modify its settings.
Create a widget
Declare the AppWidgetProvider class in your application's manifest file, AndroidManifest.xml. The following code provides an example.
<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>The following list describes the code configuration.
The receiver element requires the android:name attribute. This attribute specifies the AppWidgetProvider class that the widget uses.
The intent-filter element must include an action element with the android:name attribute. This attribute specifies that the AppWidgetProvider class accepts the ACTION_APPWIDGET_UPDATE broadcast. This is the only broadcast that you must explicitly declare. The AppWidgetManager automatically sends all other widget broadcasts to the AppWidgetProvider as needed.
The meta-data element specifies the AppWidgetProviderInfo resource and requires the following attributes.
android:name specifies the metadata name. Use `android.appwidget.provider` to identify the data as an AppWidgetProviderInfo descriptor.
android:resource specifies the resource location of the AppWidgetProviderInfo.
Add the AppWidgetProviderInfo metadata.
The AppWidgetProviderInfo defines the basic configuration of the widget, such as its minimum layout dimensions, initial layout resource, update frequency, and an optional configuration Activity to launch at creation. You can define the AppWidgetProviderInfo object in an XML resource using a single element and save it in the project's res/xml folder.
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" android:minWidth="40dp" android:minHeight="40dp" android:updatePeriodMillis="86400000" android:previewImage="@drawable/preview" android:initialLayout="@layout/example_appwidget" android:configure="com.example.android.ExampleAppWidgetConfigure" android:resizeMode="horizontal|vertical" android:widgetCategory="home_screen"> </appwidget-provider>The following describes the code configuration.
The minWidth and minHeight attributes specify the minimum size that the widget occupies by default. To ensure that your widget adapts to various screens, the minimum size must not be larger than 4 × 4 cells. For more information, see the App Widget design guidelines.
The updatePeriodMillis attribute defines the frequency at which the widget framework requests an update from the AppWidgetProvider by calling the
onUpdate()callback method. Using this value does not guarantee that the update will occur on time. To conserve the battery, do not update more frequently than once per hour.The initialLayout attribute points to the layout resource that defines the widget layout.
The configure attribute is optional. It defines the Activity to launch when the user adds the widget, which allows the user to configure its properties.
The previewImage attribute specifies a preview of the configured widget, which is visible to the user when selecting it. If this attribute is not provided, the user sees your application's launcher icon instead.
The resizeMode attribute specifies the rules for resizing the widget, such as horizontally, vertically, or in both directions.
The minResizeHeight and minResizeWidth attributes specify the minimum height and width in dp to which the widget can be resized.
NoteFor more information about element properties, see AppWidgetProviderInfo.
Create the widget layout.
You can define the initial layout for your widget in XML and save it in the project's res/layout directory.
Widget layouts are based on RemoteViews. A RemoteViews object, which is typically a widget, can support the following layout classes and view components.
Layout classes
FrameLayout
LinearLayout
RelativeLayout
GridLayout
NoteOnly these classes are supported. Child classes are not supported.
View components
AnalogClock
Button
Chronometer
ImageButton
ImageView
ProgressBar
TextView
ViewFlipper
ListView
GridView
StackView
AdapterViewFlipper
Other
RemoteViews also supports ViewStub. This is an invisible, zero-sized view that you can use to delay the rendering of layout resources at runtime.
The following example shows how to design a FrameLayout layout class. For more information, see the App Widget Design Guidelines.
Go to the res/layout/ directory and create an XML layout file, such as `appwidget_provider_layout.xml`.
Add the FrameLayout layout class. The following code provides an example.
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="OK" tools:ignore="HardcodedText" /> </FrameLayout>
Add margins between widgets.
To improve the user experience, Android 4.0 and later automatically adds padding between the widget frame and the widget's bounding box. For versions earlier than Android 4.0, you must set this padding manually. This document does not cover versions earlier than Android 4.0 because few devices use them. You can search for this information if you need it.
Create the AppWidgetProvider class.
Finally, create the `ExampleAppWidgetProvider` class that you declared in the manifest. For example, if you want a widget with a clickable button, the following code shows an example implementation that uses AppWidgetProvider.
/* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ public class ExampleAppWidgetProvider extends AppWidgetProvider { public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { final int N = appWidgetIds.length; // Perform this loop procedure for each App Widget that belongs to this provider. for (int i=0; i<N; i++) { int appWidgetId = appWidgetIds[i]; // Create an Intent to launch ExampleActivity. // Intent intent = new Intent(context, ExampleActivity.class); Intent intent = new Intent(); PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0); // Get the layout for the App Widget and attach an on-click listener // to the button. appwidget_provider_layout is the name of the created XML layout file. RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.appwidget_provider_layout); views.setOnClickPendingIntent(R.id.button, pendingIntent); // Tell the AppWidgetManager to perform an update on the current app widget. appWidgetManager.updateAppWidget(appWidgetId, views); } } }The following list describes the code configuration.
This AppWidgetProvider only defines the
onUpdate()method. It also defines a PendingIntent to start an Activity and attaches it to the widget's button usingsetOnClickPendingIntent(int, PendingIntent).The following list describes how to use AppWidgetProvider.
The AppWidgetProvider class is an extension of BroadcastReceiver that handles widget broadcasts. AppWidgetProvider receives only event broadcasts that are related to the widget, such as when it is updated, deleted, enabled, or disabled. When these broadcast events occur, AppWidgetProvider receives the following method calls:
If your widget needs to accept user interaction events, you must register event handlers in the
onUpdate()callback. If your widget does not need to create temporary files, create a database, or perform other cleanup work, you only need to implement theonUpdate()method. You can ignore other lifecycle callbacks.NoteBecause AppWidgetProvider is a child class of BroadcastReceiver, your process is not guaranteed to continue running after the callback method returns. For more information about the broadcast lifecycle, see BroadcastReceiver. If your widget's setup process takes several seconds, such as when making a web request, and you require the process to continue, you can start a Service from the
onUpdate()method to handle the time-consuming operation. You can perform your own updates to the widget from within the Service. This prevents the AppWidgetProvider from being closed due to an Application Not Responding (ANR) error.
Request APIs
The following APIs are commonly used during widget development.
Retrieve the device list (added to the widget)
path: /iotx/ilop/queryComponentProduct version: 1.0.0 params: @{}Retrieve device properties
path: /iotx/ilop/queryComponentProperty version: 1.0.0 params: @{@"productKey":productKey,@"iotId":iotId,@"query":@{@"dataType":@"BOOL", @"I18Language":@"zh-CN"}}Update device properties
path: /iotx/ilop/updateComponentProduct version: 1.0.0 params: updated device listRetrieve the scenario list (added to the widget)
path: /living/appwidget/list version: 1.0.0 params: @{}Execute a scenario
path: /scene/fire version: 1.0.1 params: @{@"sceneId":sceneId}Update widget scenarios
path: /living/appwidget/create version: 1.0.0 params: @{@"sceneIds": @[]}
For more information about the APIs, see Scenario Service and Thing Specification Language Model Service.
Listen for device property updates
To develop a widget to view and control a device from your mobile phone, you also need to understand the app-side Thing Specification Language model, which includes properties, events, and services. The following code example shows how to use the Thing Specification Language model SDK to listen for device property change events.
The following code example demonstrates how to use the Thing Specification Language model SDK to subscribe to device property change events. For more information, see Thing Specification Language model SDK.
PanelDevice panelDevice = new PanelDevice(iotId);
panelDevice.subAllEvents(new IPanelEventCallback() {
@Override
public void onNotify(String s, String s1, Object o) {
Log.d(TAG, "onNotify: " + s);
Log.d(TAG, "onNotify: " + s1);
Log.d(TAG, "onNotify: " + JSON.toJSONString(o));
// Update the UI
}
}, new IPanelCallback() {
@Override
public void onComplete(boolean b, Object o) {
/* */
}
});
panelDevice.init(this, new IPanelCallback() {
@Override
public void onComplete(boolean b, Object o) {
Log.e(TAG, "panelDevice.init:" + b);
}
});