1

I am building a widget that has multiple buttons, each one sending off its own intent to a broadcast receiver. The broadcast receiver is suppose to display a Toast message based on which button was pushed. The code currently looks like this:

public class WidgetProvider extends AppWidgetProvider {

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

        ComponentName thisWidget = new ComponentName(context, WidgetProvider.class);
        int[] allWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget);
        for (int widgetId : allWidgetIds) {
            RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_layout);

            SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);

            // Set the text of the buttons
            remoteViews.setTextViewText(R.id.widgetPreset1Button, prefs.getString("widget1", "Not set"));
            remoteViews.setTextViewText(R.id.widgetPreset2Button, prefs.getString("widget2", "Not set"));
            remoteViews.setTextViewText(R.id.widgetPreset3Button, prefs.getString("widget3", "Not set"));
            remoteViews.setTextViewText(R.id.widgetPreset4Button, prefs.getString("widget4", "Not set"));

            // Register the buttons with an OnClick event
            Intent intent1 = new Intent("myapp.SendWidgetPreset");
            intent1.putExtra("Widget", 1);
            PendingIntent pendingIntent1 = PendingIntent.getBroadcast(context, 0, intent1, PendingIntent.FLAG_UPDATE_CURRENT);
            remoteViews.setOnClickPendingIntent(R.id.widgetPreset1Button, pendingIntent1);

            Intent intent2 = new Intent("myapp.SendWidgetPreset");
            intent2.putExtra("Widget", 2);
            PendingIntent pendingIntent2 = PendingIntent.getBroadcast(context, 0, intent2, PendingIntent.FLAG_UPDATE_CURRENT);
            remoteViews.setOnClickPendingIntent(R.id.widgetPreset2Button, pendingIntent2);

            Intent intent3 = new Intent("myapp.SendWidgetPreset");
            intent3.putExtra("Widget", 3);
            PendingIntent pendingIntent3 = PendingIntent.getBroadcast(context, 0, intent3, PendingIntent.FLAG_UPDATE_CURRENT);
            remoteViews.setOnClickPendingIntent(R.id.widgetPreset3Button, pendingIntent3);

            Intent intent4 = new Intent("myapp.SendWidgetPreset");
            intent4.putExtra("Widget", 4);
            PendingIntent pendingIntent4 = PendingIntent.getBroadcast(context, 0, intent4, PendingIntent.FLAG_UPDATE_CURRENT);
            remoteViews.setOnClickPendingIntent(R.id.widgetPreset4Button, pendingIntent4);

            new WidgetBroadcastReceiver();

            appWidgetManager.updateAppWidget(widgetId, remoteViews);
        }
    }
}

and the BroadcastReceiver:

public class WidgetBroadcastReceiver extends BroadcastReceiver{

    public WidgetBroadcastReceiver(){
    }

    @Override
    public void onReceive(Context context, Intent arg1) {
        int widget = arg1.getIntExtra("Widget", -1);

        Toast.makeText(context, "Widget pressed: " + widget, Toast.LENGTH_SHORT).show();    
    }
}

My problem is it always displays Widget pressed: 4 regardless of which button is pressed. If I put the four lines intent4, intent4.putExtra(), pendingIntent4, and remoteViews.setOnClickPendingIntent() above all of the other intents, it will then always say Widget pressed: 3. In other words, whatever the last intent registration is, that is the widget displayed in the Toast message.

Anyone know why this isn't working how I want it?

BarryBostwick
  • 919
  • 2
  • 12
  • 21

3 Answers3

18

you need to provide seperate request code for each pendingintent ex:

PendingIntent pendingIntent1 = PendingIntent.getBroadcast(context, 0, intent1, PendingIntent.FLAG_UPDATE_CURRENT); 
PendingIntent pendingIntent1 = PendingIntent.getBroadcast(context, 1/*request code*/, intent1, PendingIntent.FLAG_UPDATE_CURRENT);
TronicZomB
  • 8,667
  • 7
  • 35
  • 50
Muthu Selvam
  • 350
  • 3
  • 12
1

Your PendingIntents are being overwritten by the next one. THis is because they compare the Intent being encapsulated, and extras are not considered when comparing Intents. Do this for each intent:

Intent intent1 = new Intent("myapp.SendWidgetPreset");
intent1.putExtra("Widget", 1);

// This line makes your intents different
intent1.setData(Uri.parse(intent1.toUri(Intent.URI_INTENT_SCHEME)));

PendingIntent pendingIntent1 = PendingIntent.getBroadcast(context, 0, intent1, PendingIntent.FLAG_UPDATE_CURRENT);
remoteViews.setOnClickPendingIntent(R.id.widgetPreset1Button, pendingIntent1);
Karakuri
  • 38,365
  • 12
  • 84
  • 104
  • Interesting! I tried adding the .setData() line for each intent but now the Toast messages don't display at all. Any other ideas on what to try? – BarryBostwick Jun 17 '13 at 21:06
  • Anyone? I am still having trouble figuring this out. – BarryBostwick Jun 18 '13 at 13:03
  • @BarryBostwick if you use .setData(), you need to update your Intent Filter within AndroidManifest.xml to match whatever you put into setData(). I'll post an answer with details. – Baker Oct 18 '15 at 21:51
  • @Karakuri re: Baker's comment, what element would you need to add in the AndroidManifest given the "...setData()" code line you recommend above? – AJW Mar 21 '21 at 03:12
1

Seems like PendingIntent.getBroadcast() will ignore requestCode (unlike PendingIntent.getActivity).

Thus, to make unique PendingIntents you could supply Data for the Intent.

Example:

public static Intent makeUniqueIntent(String action, int requestCode) {
        Intent intent = new Intent(action);
        intent.setData(Uri.parse("http://"+ String.valueOf(requestCode)));
        return intent;
    }

Then make your Pending Intent as per usual, including the requestCode.

PendingIntent.getBroadcast(ctx, request_code,
                makeUniqueIntent(NotificationReceiver.INTENT, request_code),
                PendingIntent.FLAG_CANCEL_CURRENT);

With a Data element in an Intent, a matching Intent Filter within AndroidManifest.xml must also have a Data element:

<receiver android:name=".service.NotificationReceiver">
       <intent-filter>
           <action android:name="my.package.my_action_string"/>
           <data android:scheme="http"/>
       </intent-filter>
</receiver>

The above intent-filter works as only a scheme (i.e. "http") was identified. So any Uri with that scheme will match this filter's "data" element and the corresponding Receiver class will be called.

Notes:

  • NotificationReceiver is my class, extending BroadcastReceiver
  • NotificationReceiver.INTENT is a String constant I declared in NotificationReceiver. In this example it would equal "my.package.my_action_string";
  • request_code can be anything. Make it unique & save it if you want to reference this same Pending Intent in the future (say for canceling an alarm that uses it).

More info on Data test with Intent Filters:

http://developer.android.com/guide/components/intents-filters.html#DataTest

Baker
  • 24,730
  • 11
  • 100
  • 106
  • Is it enough to use "http" for the scheme in the element of the intent-filter? Or should the "http://" you show in the setData() method be used? – AJW Mar 26 '21 at 17:20
  • 1
    According to official Android examples, "http" without slashes is the correct syntax: https://developer.android.com/training/app-links/deep-linking – Baker Mar 26 '21 at 18:00