Host Widgets In Android Application

Tutorial on how to using android widgets in your application, We’re going to learn how to add/remove and reattach widgets after reboot.

Initialization

You start by creating two objects. The first is an AppWidgetManager, which will give you the data you need about installed widgets. The second one is an AppWidgetHost, which will keep in memory your widget instances. Latter, your app will handle only the view that will draw the widget:


_appWidgetManager = AppWidgetManager.getInstance(_activity);
_homeWidgetHost = new AppWidgetHost(_activity, R.id.APPWIDGET_HOST_ID);

Selecting the Widget

You start by asking to the AppWidgetHost to allocate resources for a widget instance. It will return an ID for that. Then, you need to start an activity to let the user select which widget he wants to add to your app. You need to give this ID to the activity and stored in persistent storage to add selected widget again to host after system reboot.


// Class HomeWidgetManager.java implements View.OnLongClickListener
private final Activity _activity;
private final AppWidgetManager _appWidgetManager;
private final AppWidgetHost _homeWidgetHost;
private final ViewGroup _widgetContainer;
private int _widgetId;

public HomeWidgetManager(Activity activity){
    _widgetId = -1;
    _activity = activity;
    _widgetContainer = (ViewGroup) _activity.findViewById(R.id.homeClockContainer);
    _homeWidgetHost = new AppWidgetHost(_activity, R.id.APPWIDGET_HOST_ID);
    _appWidgetManager = AppWidgetManager.getInstance(_activity);
    _homeWidgetHost.startListening();
}
@Override
public boolean onLongClick(View v) {
    selectWidget();
    return true;
}
public void selectWidget() {
    int appWidgetId = _homeWidgetHost.allocateAppWidgetId();
    Intent pickIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_PICK);
    pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
    addEmptyData(pickIntent);
    _activity.startActivityForResult(pickIntent, R.id.REQUEST_PICK_APPWIDGET);
}
private void addEmptyData(Intent pickIntent) {
    ArrayList<AppWidgetProviderInfo> customInfo = new ArrayList<AppWidgetProviderInfo>();
    pickIntent.putParcelableArrayListExtra(AppWidgetManager.EXTRA_CUSTOM_INFO, customInfo);
    ArrayList<Bundle> customExtras = new ArrayList<Bundle>();
    pickIntent.putParcelableArrayListExtra(AppWidgetManager.EXTRA_CUSTOM_EXTRAS, customExtras);
}

Unfortunately, any kind of software has bugs, and here is one of the Android SDK. The Widget API supports that you merge custom widgets of your application with the installed ones. But if you don’t add anything, the Activity that shows the list of widgets to the user crashes with a NullPointerException. The addEmptyData() method above adds some dummy data to avoid this bug. More on this bug here. If you want to add a custom widget, start looking at this point of the AHSSC.

Configuring the Widget

If the user successfully selects a widget from the list (he didn’t pressed “back”), it will return an OK to you as an activity result. The data for this result contains the widget ID. Use it to retrieve the AppWidgetProviderInfo to check if the widget requires any configuration (some widgets does need). If it requires, you need to launch the activity to configure the widget. If not, jump to the next step.


// Your Activity Class
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if(requestCode == R.id.REQUEST_PICK_APPWIDGET || requestCode == R.id.REQUEST_CREATE_APPWIDGET){
        _homeWidgetManager.onActivityResult(requestCode, resultCode, data);
    }
}

// Class HomeWidgetManager.java
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (resultCode == Activity.RESULT_OK ) {
        if (requestCode == R.id.REQUEST_PICK_APPWIDGET) {
            configureWidget(data);
        }
        else if (requestCode == R.id.REQUEST_CREATE_APPWIDGET) {
            createWidget(data);
        }
    }
    else if (resultCode == Activity.RESULT_CANCELED && data != null) {
        int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
        if (appWidgetId != -1) {
            _homeWidgetHost.deleteAppWidgetId(appWidgetId);
        }
    }
}

private void configureWidget(Intent data) {
    Bundle extras = data.getExtras();
    int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
    AppWidgetProviderInfo appWidgetInfo = _appWidgetManager.getAppWidgetInfo(appWidgetId);
    if (appWidgetInfo.configure != null) {
        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE);
        intent.setComponent(appWidgetInfo.configure);
        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
        _activity.startActivityForResult(intent, R.id.REQUEST_CREATE_APPWIDGET);
    } else {
        createWidget(data);
    }
}

Creating and Adding it to Your Views

Now is time to create the widget itself. You will use the Widget ID and the AppWidgetProviderInfo to ask to the AppWidgetHost “could you please create a view of this widget for me?“. It will return an AppWidgetHostView which is a derived class from View. This one you can handle as any other view from the Framework.


// Class HomeWidgetManager.java
public void createWidget(Intent data) {
    Bundle extras = data.getExtras();
    int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
    if(appWidgetId < 0){
        return;
    }

    // Note : Here You Must Save appWidgetId For Future Use Otherwise You Cant Reattach Widget After Reboot

    AppWidgetProviderInfo appWidgetInfo = _appWidgetManager.getAppWidgetInfo(appWidgetId);
    AppWidgetHostView hostView = _homeWidgetHost.createView(_activity, appWidgetId, appWidgetInfo);
    hostView.setAppWidget(appWidgetId, appWidgetInfo);
    _widgetContainer.addView(hostView);
}

Updating

The widget is now working, but is not being updated by your app. If the widget is a clock, it will be stuck at the time you added it. To register the widget to receive the events it needs, call startListening() on the AppWidgetHost. To avoid wasting battery with unnecessary updates while your app is not visible, call it during the onStart() method of your activity, and call stopListening() during the onStop() method.


// Class HomeWidgetManager.java
public void startListener(){
    _homeWidgetHost.startListening();
}

public void stopListener(){
    _homeWidgetHost.stopListening();
}

// Activity Class
@Override
protected void onStart() {
    super.onStart();
    _homeWidgetManager.startListener();
}
@Override
protected void onStop() {
    super.onStop();
    _homeWidgetManager.stopListener();
}

Releasing the Widget

The widget should be working now. But if you want to remove the widget, you need to ask to the AppWidgetHost to release it. If you do not release it, you’ll get a memory leak (your app will consume unnecessary memory).


// Class HomeWidgetManager.java
public void removeWidget(AppWidgetHostView hostView) {
    _homeWidgetHost.deleteAppWidgetId(hostView.getAppWidgetId());
    _widgetContainer.removeView(hostView);
}

Reattaching the Widgets

To reattach selected widget all you need is appWidgetId assigned to the widget.


public void restoreWidget(){
    if(_widgetId < 0){
        return;
    }
    AppWidgetProviderInfo appWidgetInfo = _appWidgetManager.getAppWidgetInfo(_widgetId);
    AppWidgetHostView hostView = _homeWidgetHost.createView(_activity, _widgetId, appWidgetInfo);
    hostView.setAppWidget(_widgetId, appWidgetInfo);
    _widgetContainer.addView(hostView);
}