AppWidget setFillInIntent can't handle data set in setData or componentName?
Question
I want to launch different applications from my app widget. The widget is a list of items and clicking an item should open an app like Google Plus. When pressing an item in the list nothing happens. When I remove setData and setComponentName it pops up a dialog asking what app to use. Is it not possible to merge data and componentname with the PendingIntent on appWidget collections?
My AppWidgetProvider looks like this:
public class StreamsWidgetProvider extends AppWidgetProvider {
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
final int N = appWidgetIds.length;
for (int i=0; i<N; i++) {
int appWidgetId = appWidgetIds[i];
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget);
...
//set service for list view
Intent listIntent = new Intent(context, StreamsWidgetService.class);
listIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
listIntent.setData(Uri.parse(listIntent.toUri(Intent.URI_INTENT_SCHEME)));
views.setRemoteAdapter(R.id.streamItemsList, listIntent);
...
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
views.setPendingIntentTemplate(R.id.streamItemsList, pendingIntent);
appWidgetManager.updateAppWidget(appWidgetId, views);
}
super.onUpdate(context, appWidgetManager, appWidgetIds);
}
}
}
And the StreamsWidgetService looks like this:
public class StreamsWidgetService extends RemoteViewsService {
private static final String TAG = "StreamsWidgetService";
@Override
public RemoteViewsFactory onGetViewFactory(Intent intent) {
return new StreamItemWidgetFactory(intent);
}
private class StreamItemWidgetFactory implements RemoteViewsService.RemoteViewsFactory {
@Override
public RemoteViews getViewAt(int position) {
StreamItem item = mItems.get(position);
RemoteViews rv = new RemoteViews(getPackageName(), R.layout.widget_item);
...
Intent intent = new Intent();
intent.setData(ContentUris.withAppendedId(StreamItems.CONTENT_URI, item.getId()));
AccountType at = item.getAccountType();
intent.setComponent(new ComponentName(at.getPackageName(), at.getViewStreamItemActivity()));
rv.setOnClickFillInIntent(R.id.streamItemRowId, intent);
Log.d(TAG, "FillIntent set for " + item.getId() + " at position " + position + " with data " + intent.getDataString() + " and component " + intent.getComponent().toString());
return rv;
}
...
}
}
When using a normal activity without fillIntent this works:
final AccountType at = item.getAccountType();
final Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(ContentUris.withAppendedId(StreamItems.CONTENT_URI, item.getId()));
intent.setClassName(at.getPackageName(), at.getViewStreamItemActivity());
startActivity(intent);
How do I solve this? What is it that I'm doing wrong?
Solution
The PendingIntent
does not allow the component to be filled-in unless you specifically request it.
PendingIntent pendingIntent = PendingIntent.getActivity(context,
0, intent, Intent.FILL_IN_COMPONENT);
Note that this is different from the way the other FILL_IN_*
flags work.
OTHER TIPS
I solved this by using a broadcast intent to my own AppWidgetProvider. I have my own Action that I check for in onReceive. I then set the fillInIntent with parameters for the package name, class name and data id. Using this in the onReceive method I can create a new intent that looks exactly like the intent in my ListActivity.
In the Service:
Intent intent = new Intent();
intent.putExtra(StreamsWidgetProvider.BROADCAST_PARAM_ITEM_ID, item.getId());
AccountType at = item.getAccountType();
intent.putExtra(StreamsWidgetProvider.BROADCAST_PARAM_CLASS_NAME, at.getViewStreamItemActivity());
intent.putExtra(StreamsWidgetProvider.BROADCAST_PARAM_PACKAGE_NAME, at.getPackageName());
rv.setOnClickFillInIntent(R.id.streamItemRowId, intent);
In the AppWidgetProvider:
public static final String ACTION_START_ACTIVITY = "startActivity";
public static final String BROADCAST_PARAM_ITEM_ID = "intentId";
public static final String BROADCAST_PARAM_PACKAGE_NAME = "intentPackage";
public static final String BROADCAST_PARAM_CLASS_NAME = "intentClassName";
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
final int N = appWidgetIds.length;
for (int i=0; i<N; i++) {
int appWidgetId = appWidgetIds[i];
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget);
...
Intent intent = new Intent(context, StreamsWidgetProvider.class);
intent.setAction(ACTION_START_ACTIVITY);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
views.setPendingIntentTemplate(R.id.streamItemsList, pendingIntent);
appWidgetManager.updateAppWidget(appWidgetId, views);
}
super.onUpdate(context, appWidgetManager, appWidgetIds);
}
@Override
public void onReceive(Context context, Intent intent) {
if(ACTION_START_ACTIVITY.equals(intent.getAction())) {
long itemId = intent.getLongExtra(BROADCAST_PARAM_ITEM_ID, 0);
String packageName = intent.getStringExtra(BROADCAST_PARAM_PACKAGE_NAME);
String className = intent.getStringExtra(BROADCAST_PARAM_CLASS_NAME);
Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(ContentUris.withAppendedId(StreamItems.CONTENT_URI, itemId));
i.setComponent(new ComponentName(packageName, className));
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
}
super.onReceive(context, intent);
}