Créer un AppWidget sous Android

Sylvain Berfini

Le but de cet article est de vous apprendre à créer vos propres AppWidget afin de permettre à vos applications d'envahir le bureau de leurs utilisateurs.

Commentez ♪

- Titre : Créer un AppWidget sous Android

- Auteur : Sylvain Berfini

- Parution : 20 mai 2011

- Licence : Copyright ® 2011 Sylvain Berfini. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Qu'est-ce qu'un AppWidget ?

- II. Configuration de notre AppWidget

- III. Noyau de l'AppWidget

- IV. Aller plus loin avec son AppWidget

- V. Remerciements

I - Qu'est-ce qu'un AppWidget ?

Un AppWidget est une application qui a pour vocation d'être affichée sur votre bureau.

Appelés couramment widgets par le commun des utilisateurs, nous parlerons ici d'AppWidgets pour faire la distinction avec les widgets de l'API Android que tous les développeurs utilisent quotidiennement, sans pour autant savoir comment ça s'appelle.

Tous ceux d'entre vous qui consultent la météo, tuent les services en cours d'exécution, regardent leur niveau de batterie depuis leur bureau (voir ci-dessous), utilisent des AppWidget.

Voyons maintenant comment créer nos propres AppWidget, afin de contenter nos utilisateurs les plus exigeants.

II - Configuration de notre AppWidget

Commençons par déclarer notre AppWidet dans le Manifest.xml de notre application. Rien de bien sorcier ici, si ce n'est qu'on déclare notre AppWidget non pas comme une Activity, mais comme un Receiver. Notez tout de même la présence de l'intent filter APPWIDGET_UPDATE, permettant de lui demander de se mettre à jour. Il est ainsi également possible d'utiliser : ACTION_APPWIDGET_DELETED, ACTION_APPWIDGET_ENABLED et ACTION_APPWIDGET_DISABLED.

Manifest.xml

<<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.developpez.viish.tutos"
android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon" android:label="@string/app_name">
<!-- Notre AppWidget -->
<receiver android:name="MonWidgetDeveloppez">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider" android:resource="@xml/widget_developpez_meta" />
</receiver>

<activity android:name="ConfigurationWidgetActivity"></activity>
... Vos activity habituelles
</application>
<uses-sdk android:minSdkVersion="10" />
</manifest>

Remarquez ici que je passe un paramètre à resource qui se trouve être un fichier xml contenu dans /res/xml. Il va contenir toutes les informations vitales de l'AppWidget, ses métadonnées. Mais quelles sont les caractéristiques qui permettent de définir notre AppWidget ? Sa taille (de 1 à 4 par 1 à 4), le temps entre chaque mise à jour et son apparence. Il va falloir également un moyen de permettre à l'utilisateur de le configurer. Voyons tout cela :

Configuration des métadatas de l'AppWidget

<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="72dp"
android:minHeight="146dp"
android:initialLayout="@layout/widget_layout"
android:updatePeriodMillis="0"
android:configure="com.developpez.viish.tutos.ConfigurationWidgetActivity" >
</appwidget-provider>

Il faut savoir que sur un smartphone Android, la zone disponible pour les AppWidgets est constituée de quatre cases. Pour calculer le nombre de pixels necessaires en fonction du nombre de cases que l'on souhaite voir notre AppWidget occuper, il suffit de faire : nombre_de_cases * 74 - 2. Ici mon AppWidget prendra une case en largeur et deux en hauteur.

Ensuite vient un layout. C'est le fichier qui va contenir l'interface de votre AppWidget. Rien de bien sorcier là-dedans, c'est comme pour les applications. Après ça, j'ai déclaré à 0 le temps de rafraichissement, tout simplement pour qu'il ne soit pas mis à jour automatiquement. Ce sera donc à mon programme de prendre sa mise à jour en compte, par exemple lors d'un clic de l'utilisateur dessus.

Pour finir, je donne en paramètre un nom de classe (avec l'arborescence des packages). Ce sera l'Activity qui servira à configurer mon AppWidget lors de son ajout par l'utilisateur sur son bureau.

Maintenant que tout est configuré, codons le noyau de notre AppWidget.

III - Noyau de l'AppWidget

En plus de l'Activity déclarée précédemment pour la configuration de l'AppWidget, il va nous falloir une classe héritant de AppWidgetProvider. Ici nous nous contenterons pour l'instant de OnUpdate, mais il est possible de surcharger les méthodes OnEnabled, OnDisabled, OnUpdate, OnDeleted et OnReceive.

Noyau de l'AppWidget

public class MonWidgetDeveloppez extends AppWidgetProvider
{
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
{
final int N = appWidgetIds.length;

// Pour chaque AppWidget MonWidgetDeveloppez (n'oubliez pas qu'on peut en ajouter tant qu'on veut), on les met à jour :
for (int i = 0; i < N; i++)
{
int appWidgetId = appWidgetIds[i];
updateAppWidget(context, appWidgetManager, appWidgetId);
}
}

// Cette méthode est entièrement libre, à vous de la modifier comme bon vous semble. Voici toutefois une base minimaliste
static void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId)
{
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.napply_widget_layout); // On récupère les Views de notre layout
views.setTextViewText(R.id.hello_world, "Hello Developpez !"); // On peut agir sur ces vues
appWidgetManager.updateAppWidget(appWidgetId, views); // On met ensuite à jour l'affichage du widget
}
}

Et non, ce n'est pas plus compliqué que ça. À vous de coder les fonctions de votre widget ici (le mien affiche « Hello Developpez ! »). Ceci n'est que le squelette de base nécessaire.

Voyons maintenant comment l'utilisateur peut paramétrer son AppWidget (bien qu'inutile dans mon exemple « Hello World »).

Configuration de l'AppWidget

public class ConfigurationWidgetActivity extends Activity
{
private int mAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;

@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);

// On essaye de récupérer l'id de l'AppWidget
Intent intent = getIntent();
Bundle extras = intent.getExtras();
if (extras != null)
{
mAppWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
}

// Si l'intent ne contient pas son ID, ça ne sert à rien de continuer.
if (mAppWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID)
{
setResult(RESULT_CANCELED);
finish();
}

// Sinon on le configure comme prévu
configureWidget(getApplicationContext());

// IMPORTANT : penser à renvoyer l'ID du widget
Intent resultValue = new Intent();
resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
setResult(RESULT_OK, resultValue);
finish();
}

// Une fois encore, libre à vous de faire ce que vous voulez.
// Ici j'appelle la fonction update de mon Widget pour l'initialisation, mais il ne tient qu'à vous d'en faire autre chose.
public void configureWidget(Context context)
{
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
MonWidgetDeveloppez.updateAppWidget(context, appWidgetManager, mAppWidgetId);
}
}

IV - Aller plus loin avec son AppWidget

Je vous l'accorde, jusqu'à présent rien de bien palpitant, si ce n'est d'afficher du texte sur le bureau. Voyons maintenant comment initier un début d'interaction avec l'utilisateur. Nous allons faire en sorte que lors de l'appui sur l'AppWidget, celui-ci lance l'Activity principale de notre application.

Commençons par modifier quelque peu notre fonction update :

Noyau de l'AppWidget

static void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId)
{
// Cela ne change pas
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.napply_widget_layout); // On récupère les Views de notre layout
views.setTextViewText(R.id.hello_world, "Hello Developpez !"); // On peut agir sur ces vues
appWidgetManager.updateAppWidget(appWidgetId, views); // On met ensuite à jour l'affichage du widget

// On prépare un intent à lancer lors d'un clic
Intent intent = new Intent(context, MonWidgetDeveloppez.class);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
intent.setAction(ACTION_LANCER_APPLICATION); // Je crée ici ma propre action

// On lie l'intent à l'action
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
views.setOnClickPendingIntent(R.id.vue_a_cliquer, pendingIntent); // L'id de la view qui réagira au clic sur le widget.
appWidgetManager.updateAppWidget(appWidgetId, views);
}

N'oublions pas de définir la nouvelle action dans la classe du Widget : public static final String ACTION_LANCER_APPLICATION = « com.developpez.viish.tutos.LANCER_APPLICATION ».

Modifions aussi le Manifest.xml :

Manifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.developpez.viish.tutos"
android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon" android:label="@string/app_name">
<!-- Notre AppWidget -->
<receiver android:name="MonWidgetDeveloppez">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action android:name="com.developpez.viish.tutos.LANCER_APPLICATION" />
</intent-filter>
<meta-data android:name="android.appwidget.provider" android:resource="@xml/widget_developpez_meta" />
</receiver>

<activity android:name="ConfigurationWidgetActivity"></activity>
<activity android:name="MainActivity"></activity> <!-- Mon Activity principale, lancée par le clic sur le Widget -->
...
</application>
<uses-sdk android:minSdkVersion="10" />
</manifest>

Il ne reste plus qu'à faire réagir notre Widget à cette nouvelle action :

A rajouter dans le noyau

@Override
public void onReceive(Context context, Intent intent)
{
super.onReceive(context, intent);
if (intent.getAction().equals(ACTION_LANCER_APPLICATION))
{
lancerActivityPrincipale(context);
}
}

protected void lancerActivityPrincipale(Context context)
{
Intent i = new Intent(context, MainActivity.class);
context.startActivity(i);
}

Et voilà ! Il ne tient plus qu'à vous de rajouter quelques AppWidget bien sympathiques dans vos applications, pour le bonheur de tous. Comme d'habitude je me tiens à votre disposition pour tout complément d'information sur ce tutoriel et je vous remercie de m'avoir lu jusqu'ici.

V - Remerciements

Je voudrais remercier jacques_jean pour sa relecture attentive.