I found recently that after I renamed my class that extends AppWidgetProvider (as part of a code tidy-up), and installed the modified app, existing widgets would break, displaying just "Problem loading widget".
Adding a fresh widget to the home screen works fine, it's just existing widgets that break. Whilst for me as developer I don't mind deleting existing widget and adding a new one, the users of my app would not take kindly to having to do this because each widget takes time to reconfigure.
So, is there a way of safely renaming an AppWidgetProvider class without breaking existing widgets? I could of course just keep the name as it is, but it gives me an uneasy feeling that I'm stuck with the (in hindsight slightly confusing) name forever.
Unfortunately, there is no way to fix it. The problem is in changed ComponentName. AppWidgetHost just trying to get your widgets from AppWidgetManager, and AppWidgetManager trying to get previous widgets by ComponentName, so after changing package name of yours AppWidgetProvider, AppWidgetHost can't restore your previous widgets, because he got saved old ComponentName. So as you can see, there is no way to change a package name of AppWidgetProvider.
Related
I'm trying to update my AppWidget from my activity.
Similar to this question:
Programmatically update widget from activity/service/receiver
I'm using AppWidgetManager to get the ids and send an update broadcast but it doesn't work for me.
I suspect that it's because my AppWidgetProvider is in a library project and thus under a different package than my main app.
Any ideas on how to make it work?
I tried all kind of contexts (this, getApplication() , etc.) , nothing worked.
(I don't get the ids of the widgets).
Thanks.
I had to use the package name of the app in component name and the full classname in the library in the class.
I have 2 versions of an app, free and paid, but have been maintaining the code separately. I've finally moved the code into a library referenced by both to make maintaining the code easier.
I found that changing the AppwidgetProvider caused the launcher to delete any existing widgets, so I moved those classes back out of the library to keep the provider the same so users don't have to recreate their widgets. The launcher no longer deletes the widgets, but instead, they simply don't appear after updating.
If I call AppWidgetManager.getAppWidgetIds for the componentname, as it's always been, the appwidgetid is still there. The appwidgetprovider and service still get called to update the widget, and /data/system/appwidgets.xml still shows the widget, but the launcher never displays it.
It's not that it's invisible, as long pressing in the widget location brings up the wallpaper chooser. I can create new widgets just fine, but I don't want to frustrate users by asking them to recreate their widgets. The logs don't show any errors thrown by the launcher or AppwidgetService.
Any ideas why the widget stops rendering after updating? It's somehow related to moving most of the code into a separate library. Thanks!
Edit: I'm testing on an emulator, api level 15, stock launcher
OK, I found a solution, but I feel sick for what it is...
After reading about how classes declared in the manifest should never change, I went and creating each class in the manifest as a class in the app, extending the corresponding class in the library. Then, I had to change every Intent to include the correct class, using forName. So, an example of this scenario is:
app package: com.sample.package
activity: MyActivity
library: com.sample.package.core
activity: MyActivity
There's a MyActivity in the app, and a MyActivity in the library. The MyActivity in the app simply extends com.sample.package.core.MyActivity.
Then, any occurrence of...
new Intent(context, MyActivity.class)
...in the library must become...
new Intent(context, Class.forName(context.getPackageName() + "." + MyActivity.class.getSimpleName());
If there's a way to do this through the manifest, please let me know!
I have a class which extends AppWidgetProvider, and is responsible fro showing widget.
In my onUpdate method I initialize list of widgets add put all it id's in linked list.
and I also have a method updateWidget() which is iterate through this linked list of widgets and update it every 10 seconds.
So when I have one widget instance everything works great, but when I 'am trying to add several widget I got following problems:
To add second widget to that linked list, I need first add it, then delete it, then add it again< and only after that this widget id will be added to that linked list, so only after that it will be up-datable.
What can be the issue, how can i get a normal workflow of my widget?
Thank you on advance.
It seems that you have a list of widgets (mAppWidgetList) per widget. You should probably consider changing it to a static or something.
I have answered the similar question yesterday. Please check that
Android : Alarm for update more widget
You dont have to use the widget ids to update all the instances of the widget.
Use public void updateAppWidget (ComponentName provider, RemoteViews views) to update the widget.
I'm trying to turn the stock ICS launcher into a standalone app. I'm nearly there - the only things not working are the search icon and dropping widgets onto the screen, which causes a crash.
The crash is because the stock launcher uses appWidgetManager.bindAppWidgetId(appWidgetId, componentName); to add widgets, which apparently only system apps have permission to do.
So my question is, what is the correct way for a non-system app to add widgets and acheive the same UI experience as the stock ICS launcher?
Timmmm,
Your issue is that you are looking to the wrong object. You can't really control the AppWidgetManager. Its not your job, its the System's. What you CAN do is control an AppWidgetHost, it just requires a few semantics. Here are the basics.
EDIT: Extra Background on the Widget Binding Process
The AppWidgetManager is a singleton object that runs when the System is started. This means that every instance of every launcher uses the same AppWidgetManager. What differentiates them is their AppWidgetHost and the RemoteViews they are currently holding. The AppWidgetManager basically keeps a list of all of the active hosts and the widgets they are holding. An AppWidgetHost is not a priveleged object. That is, any activity may have a single host. Thus, an entire application may be nothing but Widgets, if they so choose.
When you instantiate the Host, you must then add Views to it. So, basically it is a list of child Views with no mandatory parental bounds, except what your Activity gives it. First, you ask for an ID (via myHost.allocateAppWidgetId()). Then you use your Pick Widget Activity/Dialog. The Dialog returns the WidgetInfo. The View is retrieved when you ask the Host to create the View (via createView) with the WidgetInfo and the ID you asked for. It then asks the widget for its RemoteView.
Finally, you bind the widget by placing the View in your Activity as a Child. This is done via the addView() method of the ViewGroup that holds all of your Widgets.
The Process in Action (EDITED)
First, you have to make sure you have this in your android manifest:
<uses-permission android:name="android.permission.BIND_APPWIDGET" />
Next, you have to create an AppWidgetHost (I extend my own for my launcher). The key to the Host is to keep a reference to the AppWidgetManager via AppWidgetManager.getInstance();.
AppWidgetHost myHost = new AppWidgetHost(context, SOME_NUMERICAL_CONSTANT_AS_AN_ID);
Now, get your ID:
myHost.allocateAppWidgetId()
The next step is done by whatever method you use to get the widget info. Most times it is returned via an Intent through onActivityResult. Now, all you really have to do is use the appInfo and create the view. The WidgetId is normally provided by the pick widget activity result.
AppWidgetProviderInfo withWidgetInfo
= AppWidgetManager.getInstance().getAppWidgetInfo(forWidgetId);
AppWidgetHostView hostView
= myWidgetHost.createView(myContext, forWidgetId, withWidgetInfo);
hostView.setAppWidget(forWidgetId, withWidgetInfo);
Now you just bind the View as a child to whatever you want to bind it to.
myViewGroup.addView(hostView);
Of course, you always have to consider where and how to place it, etc. Also, you have to make sure that your AppWidgetHost is listening before you start adding widgets.
myHost.startListening()
To Summarize
The Widget binding process spans many methods and steps, but all occurs through the AppWidgetHost. Because Widgets are coded outside of your namespace you don't have any control except for where you put them and how you size the View. Since they are ultimately code that runs in your space but outside of your control, the AppWidgetManager acts as a neutral mediator, while the AppWidgetHost serves as the facilitator on your app's behalf. Once this is understood, your task is simple. The steps above are all the required steps for any custom launcher (including my own).
EDIT: Final Clarification
The ICS Launcher does this as well. The appWidgetManager they use is just a wrapper housing the AppWidgetHost and the calls to the AppWidgetManager. I forget that very little of this is explained on the Android Development Central website.
Hope this helps! Let me know if you need anymore details.
FuzzicalLogic
I now know the definitive answer. In Android 4.0, you can't do it. I ended up making my users pick the widget twice, which sucks, but there is no way around it.
In Android 4.1 they fixed the problem!
SDK apps can now host widgets and don't have to use the rubbish widget picker API! You can look into the Jellybean Launcher2 source code for details, but basically, when you first try to bind a widget, Android will pop up a dialog box saying "Do you want to allow this app to bind widgets", and then the user can decide to give it permission or not.
I'm not sure why they went for the modal permission-granting dialog box rather than the all-permissions-on-install model they've used for everything else, but whatever, it works!
Now we just have to wait 4 or 5 years until everyone has Android 4.1 or greater!
I just found this tutorial on how to add appwidgets to normal apps, which might help: http://coderender.blogspot.com/2012/01/hosting-android-widgets-my.html
This tutorial still uses the "AppWidget Picker" list, so it might not work for you since ICS has the widgets picker inside the app drawer itself.
Still, was worth to mention since tutorials on hosting widgets are very rare :)
Cheers,
Yuvi
Fuzzical Logic,with your code below,
AppWidgetProviderInfo withWidgetInfo
= AppWidgetManager.getInstance().getAppWidgetInfo(forWidgetId);
AppWidgetHostView hostView
= myWidgetHost.createView(myContext, forWidgetId, withWidgetInfo);
hostView.setAppWidget(forWidgetId, withWidgetInfo);
if have not the permission of bind_widget,widgethost got nothingļ¼cus withwidgetinfo is null,widgethost create nothing.
I have an app with multiple activities and multiple layouts. However, one piece of layout is included on several activities. I also have a thread which updates this layout. However, when i switch activity it doesn't work. Since the layout is included the elements have the same ID's, shouldn't it just work? Or do I really need to fetch an object for each element in the layout and feed it into my thread in order to make it update the elements in a new activity?
You should run the update code for each Activity/View, although the XML included is the same, each is a different instance.
My suggestion is on Restart verify is there is any modification to do in each activity, a simple way is to each Activity extend a BaseActivity that has this code.
I include a layout for adverts in my app, but on each activity that uses it, the adverts need to be reloaded.
If I call an activity from one that is using the same included layout when I go back to the previous activity it's still there.
I guess this is what you are seeing....
So you can also save that data inside sharedPreferences (if it is little data and primitive objets or parceable objects).
Also you can extend the Application class and store the data there and update every activity inside the onResume() method. that i believe is the best way to handle this. and this is quite simple to do.
Ask google about extending the application class and he will provide tons of results on how to do it. its an easy way to pass data between activities and/or keep a reference to a single object which you will use throughout the app. Just be carefull to clear it when you wont need it anymore because it will stay in existance untill the application is finished() (which comes with the application extension living thru the whole application lifetime).