I need help with Widget and PendingIntents
- by YaW
Hi,
I've asked here a question about Task Killers and widgets stop working (SO Question) but now, I have reports of user that they don't use any Task Killer and the widgets didn't work after a while. I have a Nexus One and I don't have this problem.
I don't know if this is a problem of memory or something. Based on the API:
A PendingIntent itself is simply a
reference to a token maintained by the
system describing the original data
used to retrieve it. This means that,
even if its owning application's
process is killed, the PendingIntent
itself will remain usable from other
processes that have been given it.
So, I don't know why widget stop working, if Android doesn't kill the PendingIntent by itself, what's the problem?
This is my manifest code:
<receiver android:name=".widget.InstantWidget" android:label="@string/app_name">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="@xml/widget_provider" />
</receiver>
And the widget code:
public class InstantWidget extends AppWidgetProvider {
public static ArrayList<Integer> alWidgetsId = new ArrayList<Integer>();
private static final String PREFS_NAME = "com.cremagames.instant.InstantWidget";
private static final String PREF_PREFIX_NOM = "nom_";
private static final String PREF_PREFIX_RAW = "raw_";
/**
* Esto se llama cuando se crea el widget. Metemos en las preferencias los valores de nombre y raw para tenerlos en proximos reboot.
* @param context
* @param appWidgetManager
* @param appWidgetId
* @param nombreSound
* @param rawSound
*/
static void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
int appWidgetId, String nombreSound, int rawSound){
//Guardamos en las prefs los valores
SharedPreferences.Editor prefs = context.getSharedPreferences(PREFS_NAME, 0).edit();
prefs.putString(PREF_PREFIX_NOM + appWidgetId, nombreSound);
prefs.putInt(PREF_PREFIX_RAW + appWidgetId, rawSound);
prefs.commit();
//Actualizamos la interfaz
updateWidgetGrafico(context, appWidgetManager, appWidgetId, nombreSound, rawSound);
}
/**
* Actualiza la interfaz gráfica del widget (pone el nombre y crea el intent con el raw)
* @param context
* @param appWidgetManager
* @param appWidgetId
* @param nombreSound
* @param rawSound
*/
private static void updateWidgetGrafico(Context context, AppWidgetManager appWidgetManager,
int appWidgetId, String nombreSound, int rawSound){
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget);
//Nombre del Button
remoteViews.setTextViewText(R.id.tvWidget, nombreSound);
//Creamos el PendingIntent para el onclik del boton
Intent active = new Intent(context, InstantWidget.class);
active.setAction(String.valueOf(appWidgetId));
active.putExtra("sonido", rawSound);
PendingIntent actionPendingIntent = PendingIntent.getBroadcast(context, 0, active, 0);
actionPendingIntent.cancel();
actionPendingIntent = PendingIntent.getBroadcast(context, 0, active, 0);
remoteViews.setOnClickPendingIntent(R.id.btWidget, actionPendingIntent);
appWidgetManager.updateAppWidget(appWidgetId, remoteViews);
}
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
//Esto se usa en la 1.5 para que se borre bien el widget
if (AppWidgetManager.ACTION_APPWIDGET_DELETED.equals(action)) {
final int appWidgetId = intent.getExtras().getInt(
AppWidgetManager.EXTRA_APPWIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID);
if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
this.onDeleted(context, new int[] { appWidgetId });
}
} else {
//Listener de los botones
for(int i=0; i<alWidgetsId.size(); i++){
if (intent.getAction().equals(String.valueOf(alWidgetsId.get(i)))) {
int sonidoRaw = 0;
try {
sonidoRaw = intent.getIntExtra("sonido", 0);
} catch (NullPointerException e) {
}
MediaPlayer mp = MediaPlayer.create(context, sonidoRaw);
mp.start();
mp.setOnCompletionListener(completionListener);
}
}
super.onReceive(context, intent);
}
}
/** Al borrar el widget, borramos también las preferencias **/
public void onDeleted(Context context, int[] appWidgetIds) {
for(int i=0; i<appWidgetIds.length; i++){
//Recogemos las preferencias
SharedPreferences.Editor prefs = context.getSharedPreferences(PREFS_NAME, 0).edit();
prefs.remove(PREF_PREFIX_NOM + appWidgetIds[i]);
prefs.remove(PREF_PREFIX_RAW + appWidgetIds[i]);
prefs.commit();
}
super.onDeleted(context, appWidgetIds);
}
/**Este método se llama cada vez que se refresca un widget. En nuestro caso, al crearse y al reboot del telefono.
Al crearse lo único que hace es guardar el id en el arrayList
Al reboot, vienen varios ID así que los recorremos y guardamos todos y también recuperamos de las preferencias el nombre y el sonido*/
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
for(int i=0; i<appWidgetIds.length; i++){
//Metemos en el array los IDs de los widgets
alWidgetsId.add(appWidgetIds[i]);
//Recogemos las preferencias
SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, 0);
String nomSound = prefs.getString(PREF_PREFIX_NOM + appWidgetIds[i], null);
int rawSound = prefs.getInt(PREF_PREFIX_RAW + appWidgetIds[i], 0);
//Si están creadas, actualizamos la interfaz
if(nomSound != null){
updateWidgetGrafico(context, appWidgetManager, appWidgetIds[i], nomSound, rawSound);
}
}
}
MediaPlayer.OnCompletionListener completionListener = new MediaPlayer.OnCompletionListener(){
public void onCompletion(MediaPlayer mp) {
if(mp != null){
mp.stop();
mp.release();
mp = null;
}
}
};
}
Sorry for the comments in Spanish.
I have the possibility to put differents widgets on the desktop, that's why I use the widgetId as the "unique id" for the PendingIntent.
Any ideas please? The 70% of the functionality of my app is the widgets, and it isn't working for some users :(
Thanks in advance and sorry for my English.