How to create an app widget with a configuration activity, and update it for the first time?
The drawback of doing the update through the AppWidgetManager is that you have to provide the RemoteViews which - from a design point of view - doesn't make sense as the logic related to RemoteViews should be encapsulated within the AppWidgetProvider (or in your case in the RemoteViewsService.RemoteViewsFactory).
SciencyGuy's approach to expose the RemoteViews logic via a static method is one way to deal with that but there's a more elegant solution sending a broadcast directly to the widget:
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE, null, this, ChecksWidgetProvider.class);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] {mAppWidgetId});
sendBroadcast(intent);
As a consequence the AppWidgetProvider's onUpdate() method will be called to create the RemoteViews for the widget.
You are correct that the onUpdate-method isn't triggered after the configuration activity finishes. It is up to your configuration activity to do the initial update. So you need to build the initial view.
This is the gist of what one should do at the end of the configuration:
// First set result OK with appropriate widgetId
Intent resultValue = new Intent();
resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
setResult(RESULT_OK, resultValue);
// Build/Update widget
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(getApplicationContext());
// This is equivalent to your ChecksWidgetProvider.updateAppWidget()
appWidgetManager.updateAppWidget(appWidgetId,
ChecksWidgetProvider.buildRemoteViews(getApplicationContext(),
appWidgetId));
// Updates the collection view, not necessary the first time
appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.notes_list);
// Destroy activity
finish();
You already set the result correctly. And you call ChecksWidgetProvider.updateAppWidget(), however updateAppWidget() does not return the correct result.
updateAppWidget() at current returns an empty RemoteViews-object. Which explains why your widget is completely empty at first. You haven't filled the view with anything. I suggest that you move your code from onUpdate to a static buildRemoteViews() method which you can call from both onUpdate and updateAppWidget():
public static RemoteViews buildRemoteViews(final Context context, final int appWidgetId) {
final RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.checks_widget);
rv.setRemoteAdapter(android.R.id.list, intent);
// The empty view is displayed when the collection has no items. It should be a sibling
// of the collection view.
rv.setEmptyView(android.R.id.list, android.R.id.empty);
// Here we setup the a pending intent template. Individuals items of a collection
// cannot setup their own pending intents, instead, the collection as a whole can
// setup a pending intent template, and the individual items can set a fillInIntent
// to create unique before on an item to item basis.
final Intent toastIntent = new Intent(context, ChecksWidgetProvider.class);
toastIntent.setAction(ChecksWidgetProvider.TOAST_ACTION);
toastIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
toastIntent.setData(Uri.parse(toastIntent.toUri(Intent.URI_INTENT_SCHEME)));
final PendingIntent toastPendingIntent = PendingIntent.getBroadcast(context, 0, toastIntent, PendingIntent.FLAG_UPDATE_CURRENT);
rv.setPendingIntentTemplate(android.R.id.list, toastPendingIntent);
return rv;
}
public static void updateAppWidget(final Context context, final AppWidgetManager appWidgetManager, final int appWidgetId) {
final RemoteViews views = buildRemoteViews(context, appWidgetId);
appWidgetManager.updateAppWidget(appWidgetId, views);
}
@Override
public void onUpdate(final Context context, final AppWidgetManager appWidgetManager, final int[] appWidgetIds) {
super.onUpdate(context, appWidgetManager, appWidgetIds);
// Perform this loop procedure for each App Widget that belongs to this provider
for (int appWidgetId: appWidgetIds) {
RemoteViews rv = buildRemoteViews(context, appWidgetId);
appWidgetManager.updateAppWidget(appWidgetIds[i], rv);
}
}
That should take care of the widget initialization.
The last step before calling finish() in my sample code is updating the collection view. As the comment says, this isn't necessary the first time. However, I include it just in case you intend to allow a widget to be re-configured after it has been added. In that case, one must update the collection view manually to make sure the appropriate views and data get loaded.
I didn't see your appwidgetprovider.xml and AndroidManifest.xml, but my guess is that you didn't set up your configuration activity properly.
Here's how to do it:
add the following attribute to your appwidgetprovider.xml:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" ... android:configure="com.full.package.name.ChecksWidgetConfigureActivity" ... />
Your configuration activity should have an appropriate
intent-filter
:<activity android:name=".ChecksWidgetConfigureActivity"> <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/> </intent-filter> </activity>
If configuration activity is configured correctly, onUpdate()
is only triggered after it finishes.