19
votes

I have some problems with my widgets. Here is the description:

Context:

I have a home widget.

When I add it, it pops a configuration Activity for setting some parameters for the widget.

If I call setResult(RESULT_OK, resultValue); before finishing the configuration Activity, the widget is added to the Home.

If I delete the widget by dragging it to the trash bin, public void onDeleted(Context context, int[] appWidgetIds) from my AppWidgetProvider class gets called. So far so good.

Problem: If the configuration Activity exits with result code RESULT_CANCELED (setResult(RESULT_CANCELED);), public void onDeleted(Context context, int[] appWidgetIds) from my AppWidgetProvider class is not called and the widget remains in the active widgets list. When I restart the phone, onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) from my AppWidgetProvider class is called and in int[] appWidgetIds I have all widgets (the ids) that supposed to be canceled (deleted before being added) + the active ones (the ones that are actually displayed on Home). The Widgets that were deleted by dragging to the trash bin are not displayed in this list. With time this list of widgets ids keeps getting bigger and bigger if the user is canceling from the configuration Activity.

The API reference says something like: "If you return RESULT_OK using Activity.setResult(), the AppWidget will be added, and you will receive an ACTION_APPWIDGET_UPDATE broadcast for this AppWidget. If you return RESULT_CANCELED, the host will cancel the add and not display this AppWidget, and you will receive a ACTION_APPWIDGET_DELETED broadcast."

Can anyone give me some hints on this? Thank you.

Here is my manifest:

<application android:icon="@drawable/icon" android:label="@string/app_name" android:debuggable="true">
    <receiver android:name=".MytWidget" android:label="@string/app_name">
        <intent-filter>
            <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
        </intent-filter>
        <meta-data android:name="android.appwidget.provider"
                    android:resource="@xml/my_widget_provider" />
    </receiver>
    <activity android:name=".ConfigurationActivity">
        <intent-filter>
            <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
        </intent-filter>
    </activity>
</application>

The rest of the code is not relevant since it was explained above (and I don't have permission to post it).

4
It seems that I have the problem documented here: code.google.com/p/android/issues/detail?id=2539Flav

4 Answers

14
votes

I had this same problem, i did this on the onPause event

public void removeWidget(int appWidgetId) {
    AppWidgetHost host = new AppWidgetHost(Config.this, 1);
    host.deleteAppWidgetId(appWidgetId);
}

Checked the widget ids, the widget is removed. The host id is not important if you only have one app widget host.

private boolean canceled = true;

@Override
protected void onPause() {
    if(canceled) {
        removeWidget(appWidgetId);
    }
    super.onPause();
}

In the OK click, i set the canceled false

8
votes

Are you sure your code isn't relevant? Everything in your manifest is out of the book and looks good. Your code should look very similar to this:

    public void configCancelOnClick(View v) {
    MyLog.d(TAG, "configCancelOnClick");
    Intent intent = new Intent();
    intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
    setResult(RESULT_CANCELED, intent);
    finish();
}

The putExtra is required to tell the os which widget you are not creating... The finish is required to shut down correctly. If you don't have it, you won't get the intent passed to the operating system correctly and lose the setResult.

Finally, when config doesn't exit correctly (back key, home key, or bad code), a ghost widget is created. Even with perfect code, if the user hits the home key while in config, you will have a widget queued to the system that does not really exist on any home screen. That's why I call them ghosts. Once a widget has successfully completed config, it will invoke onDeleted when removed from the home screen. You are left with the problem that, if ghosts have been created, onDisabled will never, ever, run.

One last check. Since config runs, you have the following in your info xml file. But just in case, it looks like this:

    android:configure=your.package.name.ConfigurationActivity"
0
votes

I solved this problem like this,

in the widget provider's onUpdate() method I check if the widget was configured, and if it wasn't I don't do anything, so no ghost widgets. At the end of the configuration I just set it to true and I'm good to go. Just don't forget to remove it from sharedpreference when the widget is deleted.

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

    for(final int appWidgetId : appWidgetIds)
    {
        final StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(appWidgetId);
        stringBuilder.append("_conf");
        if(context.getSharedPreferences("settings",0).getBoolean(stringBuilder.toString(),false)) 
            updateAppWidget(context,appWidgetId,appWidgetManager);

    }


}
0
votes

There seems to be many issues with widgets since ages. That said while searching a solution to one of them, I stumbled across this post: https://code.google.com/p/android/issues/detail?id=2539#c15

Basically the idea is to remove the app configuration activity from the manifest and check during onUpdate if the widget has been configured, if not open the configuration activity.

It's entirely transparent to the user and is said to fix the "add/zombie widget" issue. Will try that now.


However it doesn't solve the issue with removed widgets being updated upon reboot, even though it seems to have been fixed in recent Android versions (4.4+ not sure which one?).

On older versions of Android, I observed that all widgets I've manually removed from the launcher transforms into zombies upon rebooting, the OS effectively calling onUpdate for each of those widgets, hence I had to keep a list of deleted widget.

Now deleted widget IDs are actually reused for new widgets, so I have to stop tracking deleted widgets. Sadly I have no clue when (which Android version) this issue was fixed and IDs started to get re-used.