Android's RemoteView class provides the method setOnClickPendingIntent instead of setOnClickListener. What is the reason for this ? What's the advantage of using PendingIntent in this case ?
iirc
A remote view is not running in your applications process, therefore it has to use IPC to tell your app something is clicked. This is asynchronous and so it is a "pending" click, not an instant click. The name reflects a subtle behaviour difference.
#setOnClickPendingIntent(int, android.app.PendingIntent)
vs
#setOnClickListener(android.view.View.OnClickListener)
I noticed that whenever I add new instances of the same AppWidget to the home screen, the appropriate AppWidgetProvider's onUpdate method is being called with the ids of all the instances in appWidgetIds.
The reference clearly states:
EXTRA_APPWIDGET_IDS The appWidgetIds to update. This may be all of the
AppWidgets created for this provider, or just a subset. The system
tries to send updates for as few AppWidget instances as possible.
Is this behavior intentional?
Is there a way to force onUpdate to be called only with the appropriate ids?
Is there are way to differentiate the affected instances from the others?
There is two options to create widgets:
First option is without using configuration activity and then all widgets behave similar and they all could be updated at once without differentiation.
The second one is by using configuration activity. And there you can get the widget id that was just created from the invoked configuration activity by:
Intent intent = getIntent();
Bundle extras = intent.getExtras();
int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
You can persist this widget id in the sqlite for example with other information on this widget that was defined while the configuration steps.
So for your questions:
Is this behavior intentional?
I think that the answer - 'Yes'. Because widgets that were created without configuration step should behave similar and onUpdate is called at intervals defined by the updatePeriodMillis attribute in the AppWidgetProviderInfo.
Is there a way to force onUpdate to be called only with the appropriate ids?
No. from doc I can learn that such option doesn't exist.
Is there are way to differentiate the affected instances from the others?
Yes, you can decide when and which exact widget will be updated. You can achieve this by using onRecieve in the widget provider class:
Use configuration activity and persist each widget id.
Or set pending intent to onClick event in the widget and pass the widget id within this intent to activity and then persist it. The idea - save the widget id, just to be able later to differ between them.
From your application (activity,service, alarm manager..) send broadcast with widget id you want to update.
onRecieve will catch the broadcast, will fetch the widget id and then you can work with concrete widget in the widget provider class.
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.
Can anyone give me an example of using a Stack Widget and being able to remove and add views dynamically.
Here is an example.
1) Widget loads and you add 4 views to the widget
2) User loads and activity within the same widget package and uses a button to delete one of the 4 views.
I need an example how to do that.
Thanks for the help!!
Your StackView widget should have an implemntation of the RemoteViewsService.RemoteViewsFactory interface which includes the onDataSetChanged() method. Within this method you need to update the widget from your data source.
Then in your application, any time your data set changes you can then tell any instances of your widget to refresh themselves by calling:
AppWidgetManager awm =
AppWidgetManager.getInstance(getActivity());
awm.notifyAppWidgetViewDataChanged(awm.getAppWidgetIds(new
ComponentName(getActivity(),
Your_App_Widget_Provider.class)),
R.id.your_stack_view);