Skip to content

Instantly share code, notes, and snippets.

@rock3r
Last active Aug 29, 2015
Embed
What would you like to do?
How to update an homescreen widget (the right way)
package net.frakbot.gists.widgethelper;
/*
* Copyright 2014 Sebastiano Poggi
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import android.appwidget.AppWidgetManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
/**
* An helper class for handling homescreen widgets.
*
* <b>NOTE:</b> this class assumes that you have an {@link android.appwidget.AppWidgetProvider}
* named <code>WidgetProvider</code> (that handles widgets events).
* In <code>WidgetProvider</code>, you have to define:
* <code>public static final String EXTRA_WIDGET_IDS = "widget_ids";</code>
*
* It would also be good practice to have widget updates happening in an
* {@link android.app.IntentService} to avoid ANRs, as <code>AppWidgetProvider</code>
* is a <code>BroadcastReceiver<code> and can't block the main thread for long.
* You would then invoke this updater service from both the <code>WidgetProvider</code>
* and from the {@link #updateAllHomescreenWidgets}. For the latter, you
* simply have to change the <code>updateIntent</code> declaration to:
* <code>Intent updateIntent = new Intent(c, <b>UpdaterService.class</b>);</code>
*/
public class WidgetHelper {
/**
* Updates all the homescreen widgets for this application.
*
* @param c The Context used to update the widgets.
*/
public static void updateAllHomescreenWidgets(Context c) {
AppWidgetManager widgetMgr = AppWidgetManager.getInstance(c);
if (widgetMgr != null) {
int[] widgetIds = widgetMgr.getAppWidgetIds(new ComponentName(c, WidgetProvider.class));
Intent updateIntent = new Intent(c, WidgetProvider.class);
updateIntent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
updateIntent.putExtra(WidgetProvider.EXTRA_WIDGET_IDS, widgetIds);
c.sendBroadcast(updateIntent);
}
}
}
@rock3r
Copy link
Author

rock3r commented Mar 27, 2014

Please refer to FWeather for a complete widget implementation example.

@commonsguy
Copy link

Your updateIntent should add in the WidgetProvider as the component. As it stands, your broadcast will go to all AppWidgetProviders on the device, which is unnecessary at best and possibly a source of crashes at worst.

@commonsguy
Copy link

Also, is there a reason you are using your own non-standard means of passing around the app widget IDs, rather than using EXTRA_APPWIDGET_IDS, the way AppWidgetProvider expects?

@tiwiz
Copy link

tiwiz commented Mar 27, 2014

@commonsguy: yes, using EXTRA_APPWIDGET_IDS would mess up with Samsung TouchWiz launcher

@commonsguy
Copy link

@tiwiz: I do not understand. The launcher is not directly involved in the ACTION_APPWIDGET_UPDATE flow, particularly if the broadcast is targeted at your specific component. Hence, the launcher should neither know nor care whether the app widget IDs were passed around via EXTRA_APPWIDGET_IDS or any other extra name.

@rock3r
Copy link
Author

rock3r commented Mar 27, 2014

Thanks Mark, I'm adding the ComponentName -- that is also most likely the cause of a strange bug I've seen on Samsung devices. I'm writing an article on that, will update it because I think your comment just got me to an epiphany on what was going on there!

Preview, long story short: probably Samsung doesn't filter intents by process when it comes to updating widgets. Meaning, you can update other apps' widgets when you use the TouchWiz launcher. This doesn't happen on other devices though (and, security-wise, it shouldn't as I think it's a pretty dangerous security hole for phishing).

@commonsguy
Copy link

@rock3r: I seem to recall hearing about that security issue before, though I don't see it in my archived emails to the Android security team. It's certainly worth writing about. Ideally, the CTS would check this for stock home screens, though third-party home screens could still screw up. If you think of it, hit me up on Twitter or email or something when you post your articles -- thanks!

@ksperling
Copy link

Using the BroadcastReceiver / AppWidgetProvider when you're triggering the widget update yourself is a complete red herring. Just talk directly to the AppWidgetManager from a background thread (ideally wrapped via a Service).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment