I'm taking a swing at quickly producing a fully fledged AppWidget that will augment my forthcoming Daisy Chase game. I've encountered a couple of issues.
There's one definite bug: AppWidgetProvider fails to handle ACTION_APPWIDGET_DELETED actions properly. Browsing the source I can see that AppWidgetService broadcasts an ACTION_APPWIDGET_DELETED Intent with an EXTRA_APPWIDGET_ID, but the AppWidgetProvider is only looking for EXTRA_APPWIDGET_IDS. The result is that you never receive a call to onDeleted().
The workaround is simple: don't use AppWidgetProvider, but the question is which end is wrong? Nothing is specified in the docs as far as I can tell. Do we need to watch out for either extra for both updates and deletions?
Additionally (and I haven't investigated this yet) there appears to be a possible deficiency in the timeliness of ACTION_APPWIDGET_UPDATED broadcast cessation. If my debug output is to be believed, what I am seeing is that the deletion is broadcast but I continue to receive update events for the deleted widget until I add another widget from the same provider; that is, I remove my widget on the home-screen and continue to receive updates for it (indefinitely it seems) until I add another. The second issue I've encountered probably isn't a bug, but I can't work out the expected behaviour. My widgets contain an ImageView. The image is small (80x80) and updates every hour. I decided that the most efficient approach would be to write a ContentProvider that serves pngs of the widget images from disk via openFile(). My RemoteViews for each widget then queues a setImageURI call with the appropriate content URI.
This works up-to a point. The problem is that when the widget is updated and a new image is generated, how can I kick the ImageView into reloading the image? The obvious solution which I expected to work, was to push another RemoteViews object (new instance - identical layout) to the AppWidgetManager but that doesn't work. The image is not requested again. What is causing this, over-aggressive optimization in the handling of RemoveViews by the widget framework, or something else?
(Again the workaround is simple: decorate the Uri with something distinctive like a timestamp so that the Uri is unique on each request.)
> There's one definite bug: AppWidgetProvider fails to > handle ACTION_APPWIDGET_DELETED actions properly.
Good point, the backend service is sending the wrong extras--fixing that now.
Another simple way to fix would be to override onReceive to handle this special-case early before passing to AppWidgetProvider through super.
> Additionally (and I haven't investigated this yet) there appears to be a > possible deficiency in the timeliness of ACTION_APPWIDGET_UPDATED > broadcast cessation.
Hmm, that seems odd. It might be related to how updates are scheduled; what interval are you using? Widget updates are handled using the new AlarmManager.setInexactRepeating(), which can help save battery, but can be a little unpredictable.
> The image is not requested again. What is causing > this, over-aggressive optimization in the handling of RemoveViews by the > widget framework, or something else?
It might be an optimization in ImageView, since it already has that Uri loaded. It might be enough to have two RemoteView actions, one to unset it and another to set it back.
> (Again the workaround is simple: decorate the Uri with something distinctive > like a timestamp so that the Uri is unique on each request.)
Thanks for the clarification on the extras.
WRT the ImageView, I tested it with setting null followed by setting the
URI, and that fails with an NPE somewhere during marshalling the RemoteViews
into a Parcel. It doesn't like the null URI. Setting the image resource id
to zero first is just as good and works. I'm still confused by how the
widget service decides which views to recycle and in what way.
When I'm developing the app, I set the update interval for the widget to 30s
and the rate at which calls are made to my provider is steady (every 30s
within handful of milliseconds each time). When I log out the intents being
received by my provider, it is clear that I'm receiving a perpetual string
of updates that include the id that should be delete, here's a simple
summary, the widget with id 36 has just been added:
There's another odd thing that I've noticed too (don't know if it's my
mistake, but I'm starting to think not), when my widget is added,
the APPWIDGET_UPDATE broadcast is received _before_ the configuration
activity is displayed. If I cancel the activity (ie. return a
RESULT_CANCELED as the result) I will continue to receive update events for
this new phantom widget - it's not visible anywhere but continues to be
included in update requests. Any ideas?
> > There's one definite bug: AppWidgetProvider fails to
> > handle ACTION_APPWIDGET_DELETED actions properly.
> Good point, the backend service is sending the wrong extras--fixing that
> now.
> Another simple way to fix would be to override onReceive to handle
> this special-case early before passing to AppWidgetProvider through
> super.
> > Additionally (and I haven't investigated this yet) there appears to be a
> > possible deficiency in the timeliness of ACTION_APPWIDGET_UPDATED
> > broadcast cessation.
> Hmm, that seems odd. It might be related to how updates are
> scheduled; what interval are you using? Widget updates are handled
> using the new AlarmManager.setInexactRepeating(), which can help save
> battery, but can be a little unpredictable.
> > The image is not requested again. What is causing
> > this, over-aggressive optimization in the handling of RemoveViews by the
> > widget framework, or something else?
> It might be an optimization in ImageView, since it already has that
> Uri loaded. It might be enough to have two RemoteView actions, one to
> unset it and another to set it back.
> > (Again the workaround is simple: decorate the Uri with something
> distinctive
> > like a timestamp so that the Uri is unique on each request.)
> I'm still confused by how the > widget service decides which views to recycle and in what way.
It recycles entire layouts. If you send a RemoteViews with the same layout, it just applies the new set of actions over the top of the existing layout, including any current state. So to be safe you'll want to cover all cases when pushing an update. For example, if you hide a view in one special case, you'll want to explicitly set it visible again for all other cases, because you can't know the current state you're recycling against. (Or you can just use a different layout for each case, possibly taking advantage of <include /> as needed.)
> I'm receiving a perpetual string > of updates that include the id that should be delete,
Yes, that does look like a bug. It looks like the extras bundle in that broadcast lagging behind because we're not resetting it on delete. If you /add/ a widget, it should force a refresh of that broadcast bundle and remove any deleted IDs.
> when my widget is added, > the APPWIDGET_UPDATE broadcast is received _before_ the configuration > activity is displayed.
Yes, that's correct. The configure activity is launched by the AppWidgetHost (in this case Launcher), instead of by the AppWidgetService. This allows a custom AppWidgetHost to have pre-configuration placement on their home screen. Also, it forces developers to push an explicit update when their configuration is finished. (This makes sure they are ready for AppWidgetHosts that can launch configure at any time, not just during first-insert.)
> If I cancel the activity (ie. return a > RESULT_CANCELED as the result) I will continue to receive update events for > this new phantom widget - it's not visible anywhere but continues to be > included in update requests.
Are you passing back the appWidgetId in the result extras bundle when sending back the RESULT_CANCELED? Launcher should try cleaning up any "canceled" widgets like that, but only if the EXTRA_APPWIDGET_ID is passed back.
I wasn't passing back the widget id when configuration is cancelled (the
widget example in the SDK doesn't do this either when the back button is
pressed). The APPWIDGET_UPDATE broadcast before configuration doesn't
surprise me, but having to pass the widget id back does. It seems like state
that my caller should be retaining. Also the result of not passing back the
widget id is very surprising: an application performs a misdemeanour
(forgetting to include an intent extra) and the consequence is an invisible
widget that leaches resources but cannot be removed by the user nor the
application.
Thanks for all the help,
> > I'm still confused by how the
> > widget service decides which views to recycle and in what way.
> It recycles entire layouts. If you send a RemoteViews with the same
> layout, it just applies the new set of actions over the top of the
> existing layout, including any current state. So to be safe you'll
> want to cover all cases when pushing an update. For example, if you
> hide a view in one special case, you'll want to explicitly set it
> visible again for all other cases, because you can't know the current
> state you're recycling against. (Or you can just use a different
> layout for each case, possibly taking advantage of <include /> as
> needed.)
> > I'm receiving a perpetual string
> > of updates that include the id that should be delete,
> Yes, that does look like a bug. It looks like the extras bundle in
> that broadcast lagging behind because we're not resetting it on
> delete. If you /add/ a widget, it should force a refresh of that
> broadcast bundle and remove any deleted IDs.
> > when my widget is added,
> > the APPWIDGET_UPDATE broadcast is received _before_ the configuration
> > activity is displayed.
> Yes, that's correct. The configure activity is launched by the
> AppWidgetHost (in this case Launcher), instead of by the
> AppWidgetService. This allows a custom AppWidgetHost to have
> pre-configuration placement on their home screen. Also, it forces
> developers to push an explicit update when their configuration is
> finished. (This makes sure they are ready for AppWidgetHosts that can
> launch configure at any time, not just during first-insert.)
> > If I cancel the activity (ie. return a
> > RESULT_CANCELED as the result) I will continue to receive update events
> for
> > this new phantom widget - it's not visible anywhere but continues to be
> > included in update requests.
> Are you passing back the appWidgetId in the result extras bundle when
> sending back the RESULT_CANCELED? Launcher should try cleaning up any
> "canceled" widgets like that, but only if the EXTRA_APPWIDGET_ID is
> passed back.
Sadly, supplying the widget id on the intent doesn't fix the problem - the
cancellation of widget creation is never broadcast back to the provider.
It's actually a pretty obvious bug in Launcher (when you're looking for it
that is):
In onActivityResult(), the cancellation is checked for request
code REQUEST_PICK_APPWIDGET whereas it should be checking
for REQUEST_CREATE_APPWIDGET.
I'd judge it to be a fairly significant issue. Do I need to start adding a
workaround now (a dead list of widgetIds that my provider ignores?) or will
a fix be quick coming?
> I wasn't passing back the widget id when configuration is cancelled (the
> widget example in the SDK doesn't do this either when the back button is
> pressed). The APPWIDGET_UPDATE broadcast before configuration doesn't
> surprise me, but having to pass the widget id back does. It seems like state
> that my caller should be retaining. Also the result of not passing back the
> widget id is very surprising: an application performs a misdemeanour
> (forgetting to include an intent extra) and the consequence is an invisible
> widget that leaches resources but cannot be removed by the user nor the
> application.
> Thanks for all the help,
>> > I'm still confused by how the
>> > widget service decides which views to recycle and in what way.
>> It recycles entire layouts. If you send a RemoteViews with the same
>> layout, it just applies the new set of actions over the top of the
>> existing layout, including any current state. So to be safe you'll
>> want to cover all cases when pushing an update. For example, if you
>> hide a view in one special case, you'll want to explicitly set it
>> visible again for all other cases, because you can't know the current
>> state you're recycling against. (Or you can just use a different
>> layout for each case, possibly taking advantage of <include /> as
>> needed.)
>> > I'm receiving a perpetual string
>> > of updates that include the id that should be delete,
>> Yes, that does look like a bug. It looks like the extras bundle in
>> that broadcast lagging behind because we're not resetting it on
>> delete. If you /add/ a widget, it should force a refresh of that
>> broadcast bundle and remove any deleted IDs.
>> > when my widget is added,
>> > the APPWIDGET_UPDATE broadcast is received _before_ the configuration
>> > activity is displayed.
>> Yes, that's correct. The configure activity is launched by the
>> AppWidgetHost (in this case Launcher), instead of by the
>> AppWidgetService. This allows a custom AppWidgetHost to have
>> pre-configuration placement on their home screen. Also, it forces
>> developers to push an explicit update when their configuration is
>> finished. (This makes sure they are ready for AppWidgetHosts that can
>> launch configure at any time, not just during first-insert.)
>> > If I cancel the activity (ie. return a
>> > RESULT_CANCELED as the result) I will continue to receive update events
>> for
>> > this new phantom widget - it's not visible anywhere but continues to be
>> > included in update requests.
>> Are you passing back the appWidgetId in the result extras bundle when
>> sending back the RESULT_CANCELED? Launcher should try cleaning up any
>> "canceled" widgets like that, but only if the EXTRA_APPWIDGET_ID is
>> passed back.
> In onActivityResult(), the cancellation is checked for request > code REQUEST_PICK_APPWIDGET whereas it should be checking > for REQUEST_CREATE_APPWIDGET.
Oops, you're right, thanks for catching that. For the example widgets I've been writing that use configuration steps, I've been keeping an internal CONFIGURED flag which I use to skip updates. Because I was skipping non-configured widget updates, I never noticed this myself. (I also used this flag to skip the first update before the configuration step has been completed.)
Also, a quick update on the fixes: most of them will be delayed until the next platform release because the device-bound code is already frozen. We could fix it for the SDK, but it would be bad to have different behavior between the two. We definitely have bugs filed for these, and they will be fixed.
So just a heads up that we won't be able to fix the onDeleted() bug
for the 1.5 SDK. Here is a quick workaround that you can patch
against your class that extends AppWidgetProvider to correctly catch
and handle the onDeleted() event.
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (AppWidgetManager.ACTION_APPWIDGET_DELETED.equals(action)) {
final int appWidgetId = extras.getInt
(AppWidgetManager.EXTRA_APPWIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID);
if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
this.onDeleted(context, new int[] { appWidgetId });
}
} else {
super.onReceive(context, intent);
}
}
j
On Apr 20, 4:43 pm, Jeff Sharkey <jshar...@android.com> wrote:
> > In onActivityResult(), the cancellation is checked for request
> > code REQUEST_PICK_APPWIDGET whereas it should be checking
> > for REQUEST_CREATE_APPWIDGET.
> Oops, you're right, thanks for catching that. For the example widgets
> I've been writing that use configuration steps, I've been keeping an
> internal CONFIGURED flag which I use to skip updates. Because I was
> skipping non-configured widget updates, I never noticed this myself.
> (I also used this flag to skip the first update before the
> configuration step has been completed.)
> Also, a quick update on the fixes: most of them will be delayed until
> the next platform release because the device-bound code is already
> frozen. We could fix it for the SDK, but it would be bad to have
> different behavior between the two. We definitely have bugs filed for
> these, and they will be fixed.
> So just a heads up that we won't be able to fix the onDeleted() bug
> for the 1.5 SDK. Here is a quick workaround that you can patch
> against your class that extends AppWidgetProvider to correctly catch
> and handle the onDeleted() event.
> @Override
> public void onReceive(Context context, Intent intent) {
> final String action = intent.getAction();
> if (AppWidgetManager.ACTION_APPWIDGET_DELETED.equals(action)) {
> final int appWidgetId = extras.getInt
> (AppWidgetManager.EXTRA_APPWIDGET_ID,
> AppWidgetManager.INVALID_APPWIDGET_ID);
> if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
> this.onDeleted(context, new int[] { appWidgetId });
> }
> } else {
> super.onReceive(context, intent);
> }
> }
> j
> On Apr 20, 4:43 pm, Jeff Sharkey <jshar...@android.com> wrote:
> > > In onActivityResult(), the cancellation is checked for request
> > > code REQUEST_PICK_APPWIDGET whereas it should be checking
> > > for REQUEST_CREATE_APPWIDGET.
> > Oops, you're right, thanks for catching that. For the example widgets
> > I've been writing that use configuration steps, I've been keeping an
> > internal CONFIGURED flag which I use to skip updates. Because I was
> > skipping non-configuredwidgetupdates, I never noticed this myself.
> > (I also used this flag to skip the first update before the
> > configuration step has been completed.)
> > Also, a quick update on the fixes: most of them will be delayed until
> > the next platform release because the device-bound code is already
> > frozen. We could fix it for the SDK, but it would be bad to have
> > different behavior between the two. We definitely have bugs filed for
> > these, and they will be fixed.
On Sat, May 2, 2009 at 6:02 PM, AndroidApp <zl25dre...@gmail.com> wrote:
> 'won't be able to fix' for 1.5 SDK, what about on the cupcake update?
> Dont really care about whether it's fixed for the SDK.
> On Apr 23, 2:02 pm, Jeff Sharkey <Jeffrey.Shar...@gmail.com> wrote:
>> So just a heads up that we won't be able to fix the onDeleted() bug
>> for the 1.5 SDK. Here is a quick workaround that you can patch
>> against your class that extends AppWidgetProvider to correctly catch
>> and handle the onDeleted() event.
>> @Override
>> public void onReceive(Context context, Intent intent) {
>> final String action = intent.getAction();
>> if (AppWidgetManager.ACTION_APPWIDGET_DELETED.equals(action)) {
>> final int appWidgetId = extras.getInt
>> (AppWidgetManager.EXTRA_APPWIDGET_ID,
>> AppWidgetManager.INVALID_APPWIDGET_ID);
>> if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
>> this.onDeleted(context, new int[] { appWidgetId });
>> }
>> } else {
>> super.onReceive(context, intent);
>> }
>> }
>> j
>> On Apr 20, 4:43 pm, Jeff Sharkey <jshar...@android.com> wrote:
>> > > In onActivityResult(), the cancellation is checked for request
>> > > code REQUEST_PICK_APPWIDGET whereas it should be checking
>> > > for REQUEST_CREATE_APPWIDGET.
>> > Oops, you're right, thanks for catching that. For the example widgets
>> > I've been writing that use configuration steps, I've been keeping an
>> > internal CONFIGURED flag which I use to skip updates. Because I was
>> > skipping non-configuredwidgetupdates, I never noticed this myself.
>> > (I also used this flag to skip the first update before the
>> > configuration step has been completed.)
>> > Also, a quick update on the fixes: most of them will be delayed until
>> > the next platform release because the device-bound code is already
>> > frozen. We could fix it for the SDK, but it would be bad to have
>> > different behavior between the two. We definitely have bugs filed for
>> > these, and they will be fixed.
>> > Thanks for digging in and finding them. :)
>> > --
>> > Jeff Sharkey
>> > jshar...@google.com
-- Romain Guy
Android framework engineer
romain...@android.com
Note: please don't send private questions to me, as I don't have time
to provide private support. All such questions should be posted on
public forums, where I and others can see and answer them
> Same thing, won't be fixed in the cupcake update.
> On Sat, May 2, 2009 at 6:02 PM, AndroidApp <zl25dre...@gmail.com> wrote:
> > 'won't be able to fix' for 1.5 SDK, what about on the cupcake update?
> > Dont really care about whether it's fixed for the SDK.
> > On Apr 23, 2:02 pm, Jeff Sharkey <Jeffrey.Shar...@gmail.com> wrote:
> >> So just a heads up that we won't be able to fix the onDeleted() bug
> >> for the 1.5 SDK. Here is a quick workaround that you can patch
> >> against your class that extends AppWidgetProvider to correctly catch
> >> and handle the onDeleted() event.
> >> @Override
> >> public void onReceive(Context context, Intent intent) {
> >> final String action = intent.getAction();
> >> if (AppWidgetManager.ACTION_APPWIDGET_DELETED.equals(action)) {
> >> final int appWidgetId = extras.getInt
> >> (AppWidgetManager.EXTRA_APPWIDGET_ID,
> >> AppWidgetManager.INVALID_APPWIDGET_ID);
> >> if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
> >> this.onDeleted(context, new int[] { appWidgetId });
> >> }
> >> } else {
> >> super.onReceive(context, intent);
> >> }
> >> }
> >> j
> >> On Apr 20, 4:43 pm, Jeff Sharkey <jshar...@android.com> wrote:
> >> > > In onActivityResult(), the cancellation is checked for request
> >> > > code REQUEST_PICK_APPWIDGET whereas it should be checking
> >> > > for REQUEST_CREATE_APPWIDGET.
> >> > Oops, you're right, thanks for catching that. For the example widgets
> >> > I've been writing that use configuration steps, I've been keeping an
> >> > internal CONFIGURED flag which I use to skip updates. Because I was
> >> > skipping non-configuredwidgetupdates, I never noticed this myself.
> >> > (I also used this flag to skip the first update before the
> >> > configuration step has been completed.)
> >> > Also, a quick update on the fixes: most of them will be delayed until
> >> > the next platform release because the device-bound code is already
> >> > frozen. We could fix it for the SDK, but it would be bad to have
> >> > different behavior between the two. We definitely have bugs filed for
> >> > these, and they will be fixed.
> Note: please don't send private questions to me, as I don't have time
> to provide private support. All such questions should be posted on
> public forums, where I and others can see and answer them
On Apr 24, 4:02 am, Jeff Sharkey <Jeffrey.Shar...@gmail.com> wrote:
> So just a heads up that we won't be able to fix the onDeleted() bug
> for the 1.5 SDK. Here is a quick workaround that you can patch
> against your class that extends AppWidgetProvider to correctly catch
> and handle the onDeleted() event.
hi all.
can anyone please help me in this.
i wish to update a view of an application from another.
eg i wish to change the text of a textview in a layout from another
application of mine.. is it possible.??
On May 3, 6:33 pm, AndroidApp <zl25dre...@gmail.com> wrote:
> On May 2, 9:11 pm, Romain Guy <romain...@google.com> wrote:
> > Same thing, won't be fixed in the cupcake update.
> > On Sat, May 2, 2009 at 6:02 PM, AndroidApp <zl25dre...@gmail.com> wrote:
> > > 'won't be able to fix' for 1.5 SDK, what about on the cupcake update?
> > > Dont really care about whether it's fixed for the SDK.
> > > On Apr 23, 2:02 pm, Jeff Sharkey <Jeffrey.Shar...@gmail.com> wrote:
> > >> So just a heads up that we won't be able to fix the onDeleted() bug
> > >> for the 1.5 SDK. Here is a quick workaround that you can patch
> > >> against your class that extends AppWidgetProvider to correctly catch
> > >> and handle the onDeleted() event.
> > >> On Apr 20, 4:43 pm, Jeff Sharkey <jshar...@android.com> wrote:
> > >> > > In onActivityResult(), the cancellation is checked for request
> > >> > > code REQUEST_PICK_APPWIDGET whereas it should be checking
> > >> > > for REQUEST_CREATE_APPWIDGET.
> > >> > Oops, you're right, thanks for catching that. For the example widgets
> > >> > I've been writing that use configuration steps, I've been keeping an
> > >> > internal CONFIGURED flag which I use to skip updates. Because I was
> > >> > skipping non-configuredwidgetupdates, I never noticed this myself.
> > >> > (I also used this flag to skip the first update before the
> > >> > configuration step has been completed.)
> > >> > Also, a quick update on the fixes: most of them will be delayed until
> > >> > the next platform release because the device-bound code is already
> > >> > frozen. We could fix it for the SDK, but it would be bad to have
> > >> > different behavior between the two. We definitely have bugs filed for
> > >> > these, and they will be fixed.
> > >> > Thanks for digging in and finding them. :)
> > Note: please don't send private questions to me, as I don't have time
> > to provide private support. All such questions should be posted on
> > public forums, where I and others can see and answer them- Hide quoted text -
There are basically two ways to end up with phantom widgets being supplied
to your WidgetProvider.
In the first instance, its because the user chose to add a widget, but then
cancelled its configuration. To partially overcome this problem, you can
keep a list of all the widgets you know have been configured and not deleted
(or something functionally equivalent), and then ignore updates for widgets
not on that list.
In the second instance, widgets that have been removed by the user continue
to be supplied until a new widget from the same provider is added. There's
no way of detecting that this is occurring - there's no workaround*, mainly
because the framework is doing its job and is properly insulating the widget
provider from the widget host.
*One utterly evil workaround that just entered my head as I typed that
sentence could be to include an ImageView with a source that pointed back to
a content provider in your application. Absence of a requests to the content
provider while the phone is powered could be used to infer that the widget
is phantom. This cure is almost certainly worse than the disease.