Hi,
No, this isn't another tutorial on how to create Android Widgets. For this, I recommend you the Android SDK or Google. This post is on how to create a simple app that lets the user add and remove widgets, like the Android Home Screen does.
I decided to write this one because I couldn't find anything on the web saying how to do this. I found how to create this example looking at the Android Home Screen Source Code (AHSSC). So, if you already did this, you may find some variable names similar. You can use this as trails to look yourself on the AHSSC ツ
Initialization
You start by creating two objects. The first is an AppWidgetManager, which will give you the data you need about installed widgets. The second one is an AppWidgetHost, which will keep in memory your widget instances. Latter, your app will handle only the view that will draw the widget.
mAppWidgetManager = AppWidgetManager.getInstance(this); mAppWidgetHost = new AppWidgetHost(this, R.id.APPWIDGET_HOST_ID);
Selecting the Widget
You start by asking to the AppWidgetHost to allocate resources for a widget instance. It will return an ID for that. Then, you need to start an activity to let the user select which widget he wants to add to your app. You need to give this ID to the activity.
void selectWidget() { int appWidgetId = this.mAppWidgetHost.allocateAppWidgetId(); Intent pickIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_PICK); pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); addEmptyData(pickIntent); startActivityForResult(pickIntent, R.id.REQUEST_PICK_APPWIDGET); } void addEmptyData(Intent pickIntent) { ArrayList<AppWidgetProviderInfo> customInfo = new ArrayList<AppWidgetProviderInfo>(); pickIntent.putParcelableArrayListExtra( AppWidgetManager.EXTRA_CUSTOM_INFO, customInfo); ArrayList<Bundle> customExtras = new ArrayList<Bundle>(); pickIntent.putParcelableArrayListExtra( AppWidgetManager.EXTRA_CUSTOM_EXTRAS, customExtras); };
Unfortunately, any kind of software has bugs, and here is one of the Android SDK. The Widget API supports that you merge custom widgets of your application with the installed ones. But if you don't add anything, the Activity that shows the list of widgets to the user crashes with a NullPointerException. The
addEmptyData()
method above adds some dummy data to avoid this bug. More on this bug here. If you want to add a custom widget, start looking at this point of the AHSSC.Configuring the Widget
If the user successfully selects a widget from the list (he didn't pressed "back"), it will return an OK to you as an activity result. The data for this result contains the widget ID. Use it to retrieve the AppWidgetProviderInfo to check if the widget requires any configuration (some widgets does need). If it requires, you need to launch the activity to configure the widget. If not, jump to the next step.
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode == RESULT_OK ) { if (requestCode == REQUEST_PICK_APPWIDGET) { configureWidget(data); } else if (requestCode == REQUEST_CREATE_APPWIDGET) { createWidget(data); } } else if (resultCode == RESULT_CANCELED && data != null) { int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1); if (appWidgetId != -1) { mAppWidgetHost.deleteAppWidgetId(appWidgetId); } } } private void configureWidget(Intent data) { Bundle extras = data.getExtras(); int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, -1); AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId); if (appWidgetInfo.configure != null) { Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE); intent.setComponent(appWidgetInfo.configure); intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); startActivityForResult(intent, REQUEST_CREATE_APPWIDGET); } else { createWidget(data); } }
Creating and Adding it to Your Views
Now is time to create the widget itself. You will use the Widget ID and the AppWidgetProviderInfo to ask to the AppWidgetHost "could you please create a view of this widget for me?". It will return an AppWidgetHostView which is a derived class from View. This one you can handle as any other view from the Framework. But don't forget to set the Widget ID and Widget Info on the view (I don't know why the AppWidgetHost didn't when creating the view).
public void createWidget(Intent data) { Bundle extras = data.getExtras(); int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, -1); AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId); AppWidgetHostView hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo); hostView.setAppWidget(appWidgetId, appWidgetInfo); layout.addView(hostView); }
Updating
The widget is now working, but is not being updated by your app. If the widget is a clock, it will be stuck at the time you added it. To register the widget to receive the events it needs, call startListening() on the AppWidgetHost. To avoid wasting battery with unnecessary updates while your app is not visible, call it during the onStart() method of your activity, and call stopListening() during the onStop() method.
@Override protected void onStart() { super.onStart(); mAppWidgetHost.startListening(); } @Override protected void onStop() { super.onStop(); mAppWidgetHost.stopListening(); }
Releasing the Widget
The widget should be working now. But if you want to remove the widget, you need to ask to the AppWidgetHost to release it. If you do not release it, you'll get a memory leak (your app will consume unnecessary memory). Finally, remove it from your LayoutView.
public void removeWidget(AppWidgetHostView hostView) { mAppWidgetHost.deleteAppWidgetId(hostView.getAppWidgetId()); layout.removeView(hostView); }
Note that the widget ID is also deleted during the
onActivityResult()
method if the user gave up selecting the widget. I hope this can help you develop widget based apps. You can download the full source code for this post here or on GitHub. There is also an APK to install on your phone (just make sure you can install it).
UPDATE: I've moved my whole blog to a new domain. That's why the comments section is closed here. The new URL for this post is http://www.leonardofischer.com/hosting-android-widgets-my-appwidgethost-tutorial/. If you have any question, post it there.
Excellent article, there are plenty of articles written about how to make Android Widgets but not nearly enough about how to host them in your application.
ReplyDeletethank you thank you thank you. This code and information has helped me so much in turning an idea into a project. I couldn't find this info anywhere else.
ReplyDeleteHi! can you please tell me how to add a certain widget (like google search) without using the widget picker activity? Thank you!
ReplyDeleteIf I add it by just specifying an ID I always get errors.
ReplyDeleteI also tried
ComponentName cn = new ComponentName(getBaseContext(), "com.android.quicksearchbox.SearchWidgetProvider");
int[] ids = AppWidgetManager.getInstance(getApplicationContext()).getAppWidgetIds (cn);
...
That was indeed really very helpful!
ReplyDeleteKeep blogging!
:D