In my AppWidgetProvider class, I'm programmatically adding some Button to the main layout through RemoteViews:
static void updateAppWidget(......) {
..
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.main_widget);
for (int i=0; i<maxViews, 4); ++i) {
RemoteViews button = new RemoteViews(context.getPackageName(), R.layout.my_button);
String number = numbers.get(i).getCode();
// Test1
Intent actionIntent = new Intent("MyIntent");
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, actionIntent, 0);
button.setOnClickPendingIntent(R.id.image, pendingIntent);
// Test2
Intent actionIntent = new Intent("MyIntent");
actionIntent.setData(Uri.parse("tel:" + number));
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, actionIntent, 0);
button.setOnClickPendingIntent(R.id.image, pendingIntent);
// Test3
Intent actionIntent = new Intent("MyIntent");
actionIntent.putExtra("number", number);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, actionIntent, 0);
button.setOnClickPendingIntent(R.id.image, pendingIntent);
views.addView(R.id.content_layout, button);
}
...
}
@Override
public void onReceive(Context context, Intent intent) {
System.out.println("Action: "+intent.getAction());
...
}
Here the manifest:
<receiver android:name=".widget.EmergencyWidget" >
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action android:name="MyIntent" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/my_widget_info" />
</receiver>
Using the code in Test1, when clicking on the button, the onReceive
method is correctly fired.
Using the code in Test2, when clicking on the button, the onReceive
method is not fired.
Using the code in Test3, when clicking on the button, the onReceive
method fired but intent.hasExtra("number")
returns false.
What's wrong in my code?
Test1 works because the action matches your Manifest, as you'd expect and the implicit intent is handled correctly.
Per IntentFilter, if you declare both an action and data, then both need to match for the intent to match - as you do not declare a <data>
element in your manifest, it will never match.
You could fix your Manifest by instead using:
<receiver android:name=".widget.EmergencyWidget" >
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action android:name="MyIntent" />
</intent-filter>
<intent-filter>
<action android:name="MyIntent" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="tel" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/my_widget_info" />
</receiver>
Or by adding setClassName(context, EmergencyWidget.class)
to your Intent
- this would make it an explicit Intent
and you wouldn't need to add anything to the Manifest (<intent-filter>
is only used for implicit Intents where Android needs to figure out what component needs to be called).
Your Test3 runs into one optimization of android related to PendingIntent: if two PendingIntents match another by Intent.filterEquals then it will reuse the existing PendingIntent
- extras are not considered on whether they are equal so the system probably has a PendingIntent without any extras which it is reusing (they are only cleared upon restarting your device). In this case, you can use
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, actionIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
The FLAG_UPDATE_CURRENT tells Android to replace the extras when building the PendingIntent
.