0

I have a fairly simple home screen widget application that shows a Toast on a button click. On fortunately it display a Toast only on a power-up (or just after I update the apk in the emulator). The button click has a pending intent that send a ACTION_APPWIDGET_UPDATE but the onReceive() function of the base class doesn't handle it.

Here's my code:

public class WordWidget extends AppWidgetProvider {

    @Override
    public void onReceive(Context ctxt, Intent intent) {

        Log.d("WordWidget.onReceive", "onReceive");
        Log.d("WordWidget.onReceive", intent.getAction());

        if (intent.getAction()==null) {
            ctxt.startService(new Intent(ctxt, UpdateService.class));
        } else {
            super.onReceive(ctxt, intent);
        }
    }

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

        // To prevent any ANR timeouts, we perform the update in a service
        Log.d("WordWidget.onUpdate", "onUpdate");

        context.startService(new Intent(context, UpdateService.class));
    }

    public static class UpdateService extends Service {
        @Override
        public void onStart(Intent intent, int startId) {
            Log.d("UpdateService.onStart", "onStart");

            // Build the widget update for today
            RemoteViews updateViews = buildUpdate(this);

            // Push update for this widget to the home screen
            ComponentName thisWidget = new ComponentName(this, WordWidget.class);
            AppWidgetManager manager = AppWidgetManager.getInstance(this);
            manager.updateAppWidget(thisWidget, updateViews);
        }

        public RemoteViews buildUpdate(Context context) {

            Log.d("UpdateService.buildUpdate", "buildUpdate");

            RemoteViews updateViews = new RemoteViews(context.getPackageName(), R.layout.widget_word);

            Intent defineIntent = new Intent(context, WordWidget.class);
            defineIntent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);

            PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, defineIntent, 0);

            updateViews.setOnClickPendingIntent(R.id.widget, pendingIntent);

            showToast(context);

            return updateViews;
        }

        private void showToast(Context context){
            Log.d("UpdateService.showToast", "showToast");
            Toast.makeText(context, "It is working...", Toast.LENGTH_LONG).show();          
        }

        @Override
        public IBinder onBind(Intent intent) {
            // We don't need to bind to this service
            return null;
        }
    }
}

On a power-up (or just after being re-installed) my widget shows the Toast, here's the LogCat:

01-17 13:03:10.855: I/ActivityManager(72): Start proc com.example.android.simplewiktionary for broadcast com.example.android.simplewiktionary/.WordWidget: pid=564 uid=10036 gids={3003}
01-17 13:03:11.045: D/WordWidget.onReceive(564): onReceive
01-17 13:03:11.045: D/WordWidget.onReceive(564): android.appwidget.action.APPWIDGET_UPDATE
01-17 13:03:11.054: D/WordWidget.onUpdate(564): onUpdate
01-17 13:03:11.075: D/UpdateService.onStart(564): onStart
01-17 13:03:11.075: D/UpdateService.buildUpdate(564): buildUpdate
01-17 13:03:11.094: D/UpdateService.showToast(564): showToast
01-17 13:03:12.655: D/dalvikvm(72): GC_EXPLICIT freed 971K, 47% free 13550K/25159K, paused 7ms+52ms

Now once started, if I click on the button, here's the LogCat:

01-17 13:04:16.095: D/dalvikvm(126): GC_EXPLICIT freed 72K, 14% free 14016K/16135K, paused 3ms+3ms
01-17 13:04:16.277: D/WordWidget.onReceive(564): onReceive
01-17 13:04:16.277: D/WordWidget.onReceive(564): android.appwidget.action.APPWIDGET_UPDATE
01-17 13:04:21.365: D/dalvikvm(564): GC_EXPLICIT freed 71K, 5% free 6307K/6595K, paused 3ms+2ms

As you can see the onUpdate() is not called by the onReceive() base class function...

Could you help me with this?

Thanks.

DavidH
  • 208
  • 3
  • 16

2 Answers2

4

I finally found the reason why my own onUpdate() wasn't executed by the super.onReceive() on a button click.

Creating an intent with the action set to ACTION_APPWIDGET_UPDATE is not enough to trigger the call of my override onUpdate() function by the super.onReceive() call. According to the code of the onReceive() function of the AppWidgetProvider class, the intent must have some data in the extras field. So that is why the onUpdate() is call properly the first time (when it is a real ACTION_APPWIDGET_UPDATE intent from the system) and not when I click on the button...

Here's the code from the AppWidgetProvider.java file:

// BEGIN_INCLUDE(onReceive)
public void  [More ...] onReceive(Context context, Intent intent) {
 // Protect against rogue update broadcasts (not really a security issue,
 // just filter bad broacasts out so subclasses are less likely to crash).
 String action = intent.getAction();
 if (AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
     Bundle extras = intent.getExtras();
     if (extras != null) {
         int[] appWidgetIds = extras.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_IDS);
         if (appWidgetIds != null && appWidgetIds.length > 0) {
             this.onUpdate(context, AppWidgetManager.getInstance(context), appWidgetIds);
         }
     }
 }
 else if (AppWidgetManager.ACTION_APPWIDGET_DELETED.equals(action)) {
     Bundle extras = intent.getExtras();
     if (extras != null && extras.containsKey(AppWidgetManager.EXTRA_APPWIDGET_ID)) {
         final int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID);
         this.onDeleted(context, new int[] { appWidgetId });
     }
 }
 else if (AppWidgetManager.ACTION_APPWIDGET_ENABLED.equals(action)) {
     this.onEnabled(context);
 }
 else if (AppWidgetManager.ACTION_APPWIDGET_DISABLED.equals(action)) {
     this.onDisabled(context);
 }
}

Thanks.

DavidH
  • 208
  • 3
  • 16
0

I would recommend that instead of trying to force the onUpdate to be called, you set the PendingIntent to start the service:

Intent intent = new Intent(context, UpdateService.class);    
PendingIntent pi = PendingIntent.getService(context, 0, intent, 0);

Let me know if this does not work or if it is not a viable solution for you.

Reed
  • 14,703
  • 8
  • 66
  • 110