5

I'm implementing a Widget that has a ConfigurationActivity, and must stay compatible to Eclair(2.1).

The documentation about AppWidgetProviders onUpdate method states clearly that:

... .However, if you have declared a configuration Activity, this method is not called when the user adds the App Widget, but is called for the subsequent updates. It is the responsibility of the configuration Activity to perform the first update when configuration is done. (See Creating an App Widget Configuration Activity below.)

Unfortunately this isn't true (at least for my Nexus S with JellyBean). In Fact onUpdate gets called before onCreate of my ConfigurationActivity triggers. I want to know, if there is similar behavior on other phones, and if it is possible to prevent the onUpdate call inside my Provider?

My Workaround is to store a flag in SharedPreferences inside my WidgetConfigurationActivity with that particular AppWidgetId. If it isn't there, I can assume that the ConfigurationActivity wasn't called first. This works, but is in my point of View really ugly. If I cannot prevent onUpdate from triggering, is there a better solution?

Mogsdad
  • 44,709
  • 21
  • 151
  • 275
Rafael T
  • 15,401
  • 15
  • 83
  • 144

3 Answers3

4

Yes, onUpdate() gets called when you add a widget on the homescreen. See here:

http://developer.android.com/reference/android/appwidget/AppWidgetProvider.html

http://developer.android.com/reference/android/appwidget/AppWidgetManager.html#ACTION_APPWIDGET_UPDATE

I'm not sure if there is a way to not trigger it when a widget is created. But you can stop it from triggering again by setting the "updatePeriodMillis" field in Widget Info XML file to 0 or just leaving it blank.

Another way would be to store the widget IDs somewhere. Now whenever a new widget is added, in your Receiver class, check if the ID already exists or not. If it does not exist (meaning a new widget), then dont execute any code. Also, remove widget ID from your records whenever a widget is deleted. Since there is a possibility that you removed a widget, then later added a new widget which has the same ID as the old one.

Hope that helps!

EDIT: In the onReceive() method in your Receiver class, do this:

public void onReceive(Context context, Intent intent) {
    // TODO Auto-generated method stub

    int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
    if( appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID )
    {
        super.onReceive(context, intent);
    }

}

When a widget is first selected from the Widgets list, its appWidgetId will be equal to INVALID_APPWIDGET_ID until it is added on the home screen. Since "super.onReceive()" calls the onUpdate() method when a widget is selected, onUpdate() will not be called the first time.

Shubham
  • 780
  • 3
  • 13
  • 32
  • I'm really wondering: Update-Doc: `This may be sent in response to a new instance for this AppWidget provider having been instantiated, the requested update interval having lapsed, or the system booting.` **VS** `if you have declared a configuration Activity, this method is not called when the user adds the App Widget.` I have already set updateInterval to "0", and I already do, what you suggested (storing Id's inside SharedPreferences). – Rafael T Aug 06 '12 at 15:37
  • Found a way to not call onUpdate() the first time. Check my EDITED answer. Its working fine I just implemented the same in my application. – Shubham Aug 09 '12 at 07:20
  • I didn't get `AppWidgetManager.INVALID_APPWIDGET_ID` the first time my Provider triggers. In Fact it is the correct Id, the same, that my `AppWidgetConfigActivity` will recieve. (JellyBean Galaxy Nexus) – Rafael T Aug 09 '12 at 16:02
  • 1
    INVALID_APPWIDGET_ID always gives out a value which will never be used as a valid ID for a widget. Its value was 0 when I checked. This was returned by **getIntExtra** when the widget was created. After that whenever **onReceive()** was called for the same widget, it returned the valid ID of that widget. Maybe the difference is coming because I am working on Android 2.3.3 (API 10) and you are on Jelly Bean. – Shubham Aug 09 '12 at 18:10
  • 2
    I'm working on Android 2.3.3 too, and what Rafael said seems correct - I don't think I ever got widget ID 0. Besides, leaving updatePeriodMillis blank doesn't prevent onUpdate from being called every time the screen is unlocked. – Ilya Kogan Nov 23 '12 at 09:07
0

I have got this error too.

Even i set updatePeriodMillis to 0. My onUpdate is running before i start Widget activity configuration. I think this is a bug in android widget.

Łukasz Woźniczka
  • 1,625
  • 3
  • 28
  • 51
0

I solved this problem by adding an action to the intent in onUpdate then making an if check onReceive to see if widget was clicked:

I added intentClick.setAction(WIDGET_CLICKED); which only gets triggered when clicking the widget.

public class WidgetProvider extends AppWidgetProvider {

    private static final String TAG = WidgetProvider.class.getSimpleName();
    private static String WIDGET_CLICKED = "widget_clicked";

public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
    Log.e(TAG, "onUpdate()");

    remoteViews = new RemoteViews(context.getPackageName(), R.layout.main);
    watchWidget = new ComponentName(context, WidgetProvider.class);

    Intent intentClick = new Intent(context, WidgetProvider.class);
    intentClick.setAction(WIDGET_CLICKED);
    intentClick.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, "" + appWidgetIds[0]);

    PendingIntent pendingIntent = PendingIntent.getBroadcast(context, appWidgetIds[0], intentClick, 0);
    remoteViews.setOnClickPendingIntent(R.id.img_btn, pendingIntent);
    remoteViews.setInt(R.id.img_btn, "setBackgroundResource", R.drawable.play);

    appWidgetManager.updateAppWidget(watchWidget, remoteViews);
}

@Override
public void onReceive(Context context, Intent intent) {
    super.onReceive(context, intent);

    Log.e(TAG, "onReceive()");
    Bundle extras = intent.getExtras();

    //If AND ONLY IF WIDGET_CLICKED
    if (extras != null && intent.getAction().equals(WIDGET_CLICKED)) {
        remoteViews = new RemoteViews(context.getPackageName(), R.layout.main);

        //If isPlaying
        if (isPlaying) {
            //setBackground as PLAY
            remoteViews.setInt(R.id.img_btn, "setBackgroundResource", R.drawable.play);
            isPlaying = false;

            Intent i = new Intent(context, MediaPlayerService.class);
            i.putExtra("command", "stopSong");
            context.startService(i);

            //If NOT playing
        } else {
            //setBackground as STOP
            remoteViews.setInt(R.id.img_btn, "setBackgroundResource", R.drawable.stop);
            isPlaying = true;

            Intent i = new Intent(context, MediaPlayerService.class);
            i.putExtra("command", "playRandomSong");
            context.startService(i);
        }

        watchWidget = new ComponentName(context, WidgetProvider.class);

        (AppWidgetManager.getInstance(context)).updateAppWidget(watchWidget, remoteViews);
    }
}
Jeka
  • 1,600
  • 3
  • 22
  • 36