Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android: How do I force the update of all widgets of a particular kind

Tags:

android

widget

I have seen many questions along these lines, and I keep seeing the same answer over and over. I don't want to have to have a Service for every kind of widget my app has, especially seeing as my app already has 2 persistent services.

Specifically, if one of my existing services sees that data has changed, I want to update my widgets. Doing this on a timer is annoying, as it could be days between updates or there might be several within one hour. I want my widgets to ALWAYS show up to date information.

Android widget design seems to work on the basis that your widget pulls information when it wants it, I think there are many sensible scenarios where an activity may wish to push data to a widget.

Read the answer below for how I worked out how to do precisely this. As far as I can see there are no adverse effects if it is done properly.

like image 617
Chris Noldus Avatar asked Nov 29 '11 00:11

Chris Noldus


People also ask

How do you update widgets?

Full update: Call AppWidgetManager. updateAppWidget(int, android. widget. RemoteViews) to fully update the widget.


1 Answers

To force our widgets for a particular widget provider to be updated, we will need to do the following:

  • Set up our provider to receive a specialized broadcast
  • Send the specialized broadcast with:
    1. All the current widget Id's associated with the provider
    2. The data to send
    3. Keys that only our provider responds to (Important!)
  • Getting our widget to update on click (without a service!)

Step 1 - Set up our AppWidgetProvider

I will not go through the details of creating the info xml file or changes to the Android Manifest - if you don't know how to create a widget properly, then there are plenty of tutorials out there you should read first.

Here is an example AppWidgetProvider Class:

public class MyWidgetProvider extends AppWidgetProvider {

public static final String WIDGET_IDS_KEY ="mywidgetproviderwidgetids";
public static final String WIDGET_DATA_KEY ="mywidgetproviderwidgetdata";

}

@Override
public void onReceive(Context context, Intent intent) {
    if (intent.hasExtra(WIDGET_IDS_KEY)) {
        int[] ids = intent.getExtras().getIntArray(WIDGET_IDS_KEY);
        if (intent.hasExtra(WIDGET_DATA_KEY)) {
           Object data = intent.getExtras().getParcelable(WIDGET_DATA_KEY);
           this.update(context, AppWidgetManager.getInstance(context), ids, data);
        } else {
            this.onUpdate(context, AppWidgetManager.getInstance(context), ids);
        }
    } else super.onReceive(context, intent);
}

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

//This is where we do the actual updating
public void update(Context context, AppWidgetmanager manager, int[] ids, Object data) {

    //data will contain some predetermined data, but it may be null
   for (int widgetId : ids) {
        .
        .
        //Update Widget here
        .
        .
        manager.updateAppWidget(widgetId, remoteViews);
    }
}

Step 2 - Sending the broadcast

Here we can create a static method that will get our widgets to update. It is important that we use our own keys with the widget update action, if we use AppWidgetmanager.EXTRA_WIDGET_IDS we will not only break our own widget, but others as well.

public static void updateMyWidgets(Context context, Parcelable data) {
    AppWidgetManager man = AppWidgetManager.getInstance(context);
    int[] ids = man.getAppWidgetIds(
            new ComponentName(context,MyWidgetProvider.class));
    Intent updateIntent = new Intent();
    updateIntent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
    updateIntent.putExtra(MyWidgetProvider.WIDGET_ID_KEY, ids);
    updateIntent.putExtra(MyWidgetProvider.WIDGET_DATA_KEY, data);
    context.sendBroadcast(updateIntent);
}

If using this method with multiple providers MAKE SURE they use different keys. Otherwise you may find widget a's code updating widget b and that can have some bizarre consequences.

Step 3 - updating on click

Another nice thing to do is to get our widget to update magiacally whenever it is clicked. Add the following code into the update method to acheive this:

RemoteViews views = 
      new RemoteViews(context.getPackageName(),R.layout.mywidget_layout);

Intent updateIntent = new Intent();
updateIntent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
updateIntent.putExtra(myWidgetProvider.WIDGET_IDS_KEY, ids);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
      context, 0, updateIntent, PendingIntent.FLAG_UPDATE_CURRENT);

views.setOnClickPendingIntent(R.id.view_container, pendingIntent);

This code will cause the onUpdate method to be called whenever the widget is clicked on.

Notes

  • Any call to updateWidgets() will cause all instances of our widget to be updated.
  • Clicking the widget will cause all instances of our widget to be updated
  • NO SERVICE REQUIRED
  • Of course, be careful not to update to often - widget updates use battery power.
  • Bear in mind that the broadcast is received by ALL widget providers, and its our special keys that ensure it is only our widgets that actually update.
like image 196
Chris Noldus Avatar answered Sep 20 '22 08:09

Chris Noldus