Somewhere here on Stack Overflow, I had found the following code some day which I adjusted to my application a bit:
private void updateWidget() {
AppWidgetManager widgetManager = AppWidgetManager.getInstance(ctx);
ComponentName widgetComponent = new ComponentName(ctx, MyAppWidgetProvider.class);
int[] widgetIds = widgetManager.getAppWidgetIds(widgetComponent);
Intent update = new Intent();
update.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, widgetIds);
update.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
ctx.sendBroadcast(update);
}
This should programmatically refresh all instances of the application's widget. ctx is the Activity's context (this) that I set once in onCreate(). The method above is called in the Activity's onStop() method.
Unfortunately, when it is called, it replaces the app's widget by other apps' widgets (e.g. AP News) - at least for a while.
How can this happen? Is there something wrong in the code?
Thank you!
Edit #1: To point this out more clearly: I've already defined an interval for automatic refreshing. But in addition to that, I would like to update the widget from the Activity from time to time. This question suggests that it is possible as well.
Edit #2: I've just seen that the wrong widget is only shown for some seconds. After that, my own app's widget is shown again.
I had some problems with widgets in the past, and have solved them. I really do not know the reason why my solution worked for me. And as it worked for Marco W., so I place it here: all I did was moved the update code to a service. When I need to update, start that service. Again, I'm not sure about that but the problems were solved.
Thanks Marco, I got some more experiences with Android. It's funny :-)
I solved this problem by using notifyAppWidgetViewDataChanged method which allows to update all my widgets, the explanation I can give is that the method works so as to update the collection view (StackView in my case) after modifying the data that it contains.
Is there a reason you are running an update function outside of the widget? It seems to me that it would make more sense to uses the updatePeriodMillis property? You can set that property to how often you want the widget to call onUpdate() which can then provide the updated information.
http://developer.android.com/guide/topics/appwidgets/index.html#MetaData
Related
I am writing a launcher activity to host appwidgets. Basically here is what I am doing.
My container view for all appwidgets is some customized ViewGroup.
When adding widgets, I do call config activity first for the widget being added.
I am calling AppWidgetHost.startListening() in main activity.onStart() and stopListening() in onStop()
When adding widget, here is what I do
application.getAppWidgetManager().bindAppWidgetIdIfAllowed(appWidgetId, componentName);
AppWidgetHostView hostView = application.getAppWidgetHost().createView(context, appWidgetId, appWidgetInfo);
hostView.setAppWidget(widgetId, appWidgetInfo);
myViewGroup.addView(hostView);
I am not seeing RemoteException in logs and calling AppWidgetHost.startListening() aggressively doesn't help.
In addition, not all widgets fail to update, the stock clock widget, for example, works fine. This happens especially on Minimalistic Text Widget and some others. Once the widget has been added, it is able to update for 1~2 minutes before stop working. The other related observation is that when I tap the Minimalistic widget, modify the layout and save. The new layout won't be applied to the existing widget. That makes me feel there is something buggy in my code, but I cannot figure out where.
Thanks for help in advance!
Sorry I cannot paste detail code here because they are scattered around. But I can provide any detail information if you need. Thanks again!
Never mind. I figured out eventually. The reason is that I was using application context instead of activity context when creating AppWidgetManager and AppWidgetHost.
I am having an issue with updating AppWidgets and a confluence of limitations/bugs in android is preventing me finding a workaround.
My app widget is themeable so I need to be able to update the imageviews on it at runtime (as well as the textviews and intents). There are up to 9 imageviews that I am updating but the size of the graphics I'm using are fairly modest (max 11kb pngs). The total size of data I am pushing through the RemoteViews object should be under 100kb, well within the limit of 1mb. I think that actual drawable resources on screen end up being larger in size than the original pngs, but 10% of the limit seems reasonable enough to account for that. However, with certain phones and launchers, I am getting "FAILED BINDER TRANSACTION" errors.
There are two solutions that I have found for this:
1) Use setImageViewUri (which calls setImageURI) instead of setImageViewResource with the RemoteViews object, this gets rid of sending drawables through the RemoteViews object and loads the image from URI instead. Solves the memory error (see https://groups.google.com/forum/#!topic/android-developers/KKEyW6XdDvg/discussion )
2) Divide the updates to the app widget into multiple RemoteViews calls. So I update 3 of the images, call updateAppWidget, create a new RemoteViews object, update the next 3 images, call updateAppWidget, etc.
The problem with the 1st approach is that it only works for medium density devices. All other devices scale the images incorrectly due to a bug in the android codebase (see bug report: http://code.google.com/p/android/issues/detail?id=22590 )
The problem with the 2nd approach is that when the screen orientation changes, android destroys and recreates widget views using the last RemoteViews call which it stores in some secret place. This meams the appwidget doesn't get fully redrawn because I split my RemoteView calls up and the widget ends up unusable. There appears to be no reliable way to figure out when android is destroying and recreating the appwidgets - onConfigurationChanged in the service doesn't always fire and no Activity level call is ever made (ie. onUpdate, onReceive) that I can find. In order for the widgets to be able to be redrawn fully and correctly on orientation change, I can only use one RemoteViews (updateAppWidget) call so this solution won't work.
Does anyone have any idea how to work around this? I'm wondering if it'd be possible to implement my own RemoteViews that called a non-buggy setImageURI function (in a custom ImageView class) so that the scaling happened correctly. It seems like a lot of work just update a few widgets and I am not sure android will let me extend RemoteViews/ImageView that way.
Is there anyway to intercept android when the screen orientation changes and force it to redraw the whole widget?
Would love to hear any other suggestions or workaround ideas! Thanks.
I've encountered the same problems when developing collections' AppWidgets. Many times the following would happened:
On rotation, the ListView entirely dissapeared;
The button listeners (actually called PendingIntents) stopped responding;
Changes in images via setImageViewResource didn't get updated;
.. and so on. I came to the same conclusion as you did: android destroys and recreates widget views using the last RemoteViews call which it stores in some secret place. After acknowledging that, it was a question of fine-timing the calls to AppWidgetManager.getInstance(context).updateAppWidget() because I wanted to disable the clickListeners (or PendingIntents) after telling the ListView to refresh itself (it was a network operation of calling web-services and retrieving a non-trivial amount of data) and wanted to enabled the listeners after the ListView finished loading while maintaining the RemoteViews fully loaded in order to handle a rotation.
I was this close to giving up and using as an alternative..
Is there anyway to intercept android when the screen orientation changes and force it to redraw the whole widget?
.. a service that does intercept Android rotation changes, unlike an AppWidget. If you're running out of options, I'd suggest you'd try it out:
public class MyWidget extends AppWidgetProvider {
#Override
public void onConfigurationChanged(Configuration newConfig)
{
// insert code here
}
}
Find out more about the service here in StackOverflow.
According to the documentation the lifecycle of the AppWidgetProvider class is managed by the Android platform. The documentation also states that the lifecycle method onEnabled() is only called once. But how about widgets that were removed because the Android platfrom reclaimed its memory? In that case when the widget is activated again (e.g. some intent was received, or someone clicked on the widget), will the onEnabled() method be called again?
NO!
Answer is Simple No. I have started working on widgets recently. and i have learned the basics and according to that . the onEnabled()is jst like onCreate Method whenever you close and reopen the widget/activity the onEnabled/onCreate method is called.
onEnabled(Context context) : Called when the first App Widget is created. Global initialization should take place here, if applicable.
Reference: http://www.developer.com/ws/article.php/3833306/Creating-a-Home-Screen-App-Widget-on-Android.htm
Thanks:
Hopefully i Helped U. Dont Mark this Answer as Correct So that other also knows the correct Answer!
I have a issue that I have been struggling with for some time and its becoming quite frustrating...
I have an app that a user can modify the background image of a widget in a configure activity. Once they select what image they want I save a SharedPreferences file with the widgetsID and the value for the image... however in the onEnabled of the WidgetProvider I do not know the widget ID, so I can not do a look up of which image to use... Since there can be multiple instances of this widget I do not know which id goes with which onEnabled...
any suggestions?
Can be accomplished simply by:
int[] allids = AppWidgetManager .getInstance(context) .
getAppWidgetIds(new ComponentName(context, AwarenessWidget.class));
I can get all the IDs of my app and call onupdate and update each ones views based upon the saved preferences
I can elaborate more if anyone needs...
Baffels me no one was able to figure that one out and help me! seems very straight forward now!
I have a bit of a strange bug with a widget I've coded- after the screen rotates the widget stops responding to onClick events. The code is exactly the same as in the Android Developer Documentation for App Widgets here. I've noticed other widgets from the market don't have this problem- is there a known workaround perhaps? I've tried tapping all over the place after a rotation so I don't think its the onClickPendingIntent not being resized after a rotation; it doesn't seem to be present at all.
I can't find an onRotation() kind of trigger for the AppWidgetProvider to redo the listening code in the event of a rotation so I'm quite unsure how to proceed...
Thanks!
I received the following response from a post I made on Google Groups which resolved my issue. I cannot say whether it would resolve the original poster's problem, but I though I would post it, in case someone else runs across this problem. The link to my Google Groups post is:
http://groups.google.com/group/android-developers/browse_thread/thread/ca8c2958b6dc086c#
There is no onUpdate on configuration changes. The home screen recreates
your widget, then takes the most recent RemoteViews and applies it to the
widget.
I figured that it was recreating the Widget on rotation. The problem
is, I don't seem to be getting any messages to that effect, and have
no way (that I can see) of re-establishing the connection. How can I
determine that a rotate has happened and set up a new onClick
connection?
Like I said, you don't (determine or respond to an orientation change).
What you do, is make sure that every time your code pushes a RemoteViews
object into the home application for your widget, it's complete in all
respects:
Has image resource ids;
Has text stings;
Has pending intents.
Don't do "incremental" widget updates, like you would do with a regular
activity - don't set the intents first, then the images, then the text
reflecting current information.
The home app runs as a separate process, and its state can get out-of-step
with your widget receiver. When it does, the only thing it has for
re-creating your widget is your most recent RemoteViews object. If it's
complete, and has all the parts, everything will work just fine. If it only
has the most recent text or image change, the earlier updates which had the
intents will be lost.
http://kmansoft.wordpress.com/2010/05/23/widgets-and-orientation-chan...
-- Kostya
Firstly, ensure that your RemoteViews is a FULL representation of the state of the widget if you're calling AppWidgetManager.updateAppWidget(). Set up all pending intents, view data, etc. This state will be re-used when the launcher wants to restore your widget from state, eg. when rotation changes.
When you want to update your remote views but you don't want to supply a complete RemoteViews representation, ie. you just want to change an existing remoteView state, you may use AppWidgetManager.partiallyUpdateAppWidget().
This update differs from updateAppWidget(int, RemoteViews) in that the
RemoteViews object which is passed is understood to be an incomplete
representation of the widget, and hence is not cached by the
AppWidgetService. Note that because these updates are not cached, any
state that they modify that is not restored by restoreInstanceState
will not persist in the case that the widgets are restored using the
cached version in AppWidgetService. Use with
RemoteViews.showNext(int), RemoteViews.showPrevious(int),
RemoteViews.setScrollPosition(int, int) and similar commands.
For example, when advancing a ViewPager for a widget outside of onUpdate:
final RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.app_widget_4x2);
remoteViews.showNext(R.id.appWidget_viewFlipper);
appWidgetManager.partiallyUpdateAppWidget(widgetId, remoteViews);
The following code seems like being able to resolve the problem.
#Override
public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions) {
super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions);
onUpdate(context, appWidgetManager, new int[] {appWidgetId});
}