Tutoriel Android : apprendre à investir l'écran d'accueil avec les AppWidgets

Android2ee

Mathias Seguy

Cet article vous explique comment mettre en place des AppWidgets sur Android. Ce sont des composants graphiques qui s'exécutent dans l'écran d'accueil.

Pour réagir à ce tutoriel, un espace de dialogue vous est proposé sur le forum : Commentez ?

- Titre : Tutoriel Android : apprendre à investir l'écran d'accueil avec les AppWidgets

- Auteur : Mathias Seguy

- Parution : 17 avril 2012

- Licence : Copyright ® 2012 Mathias Seguy. 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

- 1. Introduction

- 2. Vision globale et principes fondamentaux

- 3. Déclaration d'un AppWidget

- 3-1. Déclaration dans le manifeste

- 3-2. Définition dans un fichier xml

- 3-3. Description de l'interface graphique avec un fichier de layouts

- 3-4. Règles graphiques des AppWidgets

- 3-4-1. Taille

- 3-4-2. Marge

- 3-4-3. Background

- 4. Code Java de l'AppWidget

- 4-1. Les méthodes natives de l'AppWidgetProvider

- 4-1-1. onUpdate

- 4-1-2. onDeleted

- 4-1-3. onDisable et onEnable

- 4-1-4. onReceive

- 4-2. Manipulation du AppWidgetManager et des RemoteViews

- 4-2-1. RemoteViews

- 4-2-2. AppWidgetManager

- 4-2-3. Exemple d'utilisation

- 4-3. Ajouter des listeners de clics à vos composants graphiques

- 4-3-1. Déclaration de l'Intent

- 4-3-2. Mise en place de l'envoi de l'Intent

- 4-3-3. Récupération des Intents envoyés

- 4-3-4. Factorisation des noms des Intents dans le fichier string.xml

- 4-4. Utilisation de l'alarme pour la mise à jour du Widget

- 4-4-1. Digression sur le PendingIntent

- 4-5. Utilisation du SharedPereference

- 4-6. Lancement d'une activité à l'installation d'une instance du widget

- 5. Conclusion

- 6. Remerciements

1 - Introduction

L'objectif de cet article est de vous présenter comment concevoir et développer des AppWidgets, ces composants graphiques qui s'installent sur l'écran d'accueil de l'appareil.

Le développement de tels éléments graphiques est profondément différent du développement graphique au sein des activités. Le Design Pattern mis en place n'a rien à voir, il va falloir changer sa philosophie, ses méthodes et ses automatismes en commençant par le design de votre application.

Ce n'est pas les composants graphiques qui changent, mais la façon dont vous allez pouvoir interagir avec eux.

Cet article explique dans quel état d'esprit vous devez abordez votre conception, quels sont les Design Patterns à utiliser, comment les mettre en place et bien sûr comment développez tout ça pour fournir un code propre.

Le premier chapitre vous explique les principes fondamentaux, comment et où déclarer les différents éléments d'un AppWidget. À la fin de ce chapitre vous aurez compris comment mettre en place votre projet, comment décrire votre IHM et quels sont les règles graphiques à respecter.

Le second chapitre rentre dans les détails du code Java. Un premier paragraphe vous présente les méthodes du cycle de vie de l'AppWidget, vous expliquant son fonctionnement de manière globale. Le deuxième paragraphe vous montre comment mettre à jour les interfaces graphiques de vos AppWidgets, en particulier comment utiliser les RemoteViews et l'AppWidgetManager. Le troisième paragraphe va vous apprendre à interagir avec l'utilisateur au travers de vos AppWidgets. Toute la communication par Intent entre vos instances d'AppWidget et votre classe Java vous sera expliquée. Enfin, le dernier paragraphe vous montrera comment utiliser l'AlarmManager pour gérer finement la mise à jour de vos AppWidget.

Enfin, cet article est associé à un tutoriel de manière forte. Il est fortement préconisé de télécharger le tutoriel avant de lire l'article de manière à pouvoir le lire tout en allant regarder le tutoriel. Cela vous permet d'avoir une vision appliquée des différents concepts expliqués dans l'article.

Et celui-ci met en place le widget suivant :

2 - Vision globale et principes fondamentaux

Bien qu'étant une interface graphique, l'AppWidget doit être considéré comme une image ne pouvant interagir avec l'utilisateur de manière usuelle ; vous ne pouvez pas récupérer de pointeur vers un élément graphique du widget. C'est un type de programmation complètement différent qui doit être mis en place pour interagir avec votre widget, que ce soit une interaction utilisateur ou une mise à jour programmatique.

En fait un AppWidget est un IntentReceiver particulier, pas une activité ! Il n'a pas de cycle de vie, est créé à la réception d'un Intent et est détruit dès que l'Intent est consommé. Il ne peut donc pas stocker de valeurs ; les attributs sont inutiles ou doivent toujours être considérés comme null à la réception d'un nouvel Intent.

Un AppWidget contient un RemoteView qui n'est autre que l'ensemble des composants graphiques affichés par l'AppWidget. On peut placer sur chaque élément graphique de la RemoteView un écouteur de clic qui lance un Intent quand il est déclenché.

Un AppWidget peut posséder plusieurs instances, car il peut être installé à plusieurs endroits dans l'écran d'accueil. Chaque installation pointe vers une unique instance de l'AppWidget.

Le schéma suivant donne une bonne vision de ce qu'est un AppWidget et de son fonctionnement.

Tout d'abord, il hérite d'AppWidgetProvider, qui lui-même hérite de BroadCastReceiver. C'est donc un BroadCastReceiver que les équipes d'Android ont customisé pour nous faciliter leur utilisation dans le cadre des AppWidgets.

Ensuite, un AppWidget n'interagit qu'avec des Intents, ceux-ci peuvent être déclenchés par le système ou par les composants graphiques de l'AppWidget eux-mêmes.

Pour mettre à jour les composants graphiques de l'AppWidget, l'objet AppWidgetManager est utilisé. On ne met pas à jour les composants de l'AppWidget, on les recrée.

3 - Déclaration d'un AppWidget

L'AppWidget se déclare dans le manifeste de votre application, le fichier AndroidManifest.xml. Il se définit au moyen d'un fichier xml dans le dossier res\xml\. Son interface graphique initiale est décrite dans un fichier de layout classique (res\layout\widget_layout.xml).

3-1 - Déclaration dans le manifeste

La déclaration d'un AppWidget dans le AndroidManifest.xml est quasi identique à celle d'un BoradcastReceiver :

Déclaration de l'AppWidget dans le fichier AndroidManifest.xml

1. <!-- Broadcast Receiver that will process AppWidget updates -->

2. <receiver

3. android:label="@string/widget_name"

4. android:name=".SimpleWidget" >

5. <intent-filter >

6. <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />

7. </intent-filter>

9. <intent-filter >

10. <action android:name="com.android.tuto.widget.simple.sample1.click1" />

11. </intent-filter>

13. <meta-data

14. android:name="android.appwidget.provider"

15. android:resource="@xml/widget_properties_definition" />

16. </receiver>

Où :

- le label est le nom de votre widget qui sera affiché à l'utilisateur ;

- son name est la classe qui gère ce widget ;

- les intent-filter sont les intentions qui seront écoutées par votre widget et consommées par la classe définie par la balise name ;

- le meta-data, lui, définit deux choses : le name qui est toujours le même pour vos AppWidgets,  la ressource qui pointe vers le fichier xml décrivant votre AppWidget.

Maintenant, votre application déclare posséder un AppWidget pour l'écran d'accueil.

3-2 - Définition dans un fichier xml

Vous devez définir les attributs de votre AppWidget dans un fichier xml de la manière suivante :

Le fichier res/xml/widget_properties_definition.xml

1. <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"

2. android:minWidth="250dp"

3. android:minHeight="110dp"

4. android:updatePeriodMillis="86400000"

5. android:previewImage="@drawable/preview"

6. android:initialLayout="@layout/example_appwidget"

7. android:configure="com.example.android.ExampleAppWidgetConfigure"

8. android:resizeMode="horizontal|vertical">

9. </appwidget-provider>

Où :

- minWidth et minHeight sont les tailles minimales de votre widget, nous reviendrons sur cette notion de dimension ;

- updatePeriodMillis définit la période de rafraichissement de votre widget. Elle ne peut être inférieure à 30 minutes. Gardez en tête qu'une mise à jour trop fréquente épuisera la batterie, il est préconisé de le faire au plus une fois par heure. Un chapitre expliquera comment mettre en place des mises à jour plus efficaces ;

- previewImage permet de définir l'icône à afficher à l'utilisateur lorsque celui-ci ajoute un widget à l'écran d'accueil (introduit par Android 3.0) ;

- initialLayout décrit le layout qui sera utilisé pour construire le widget ;

- configure vous permet de définir une activité qui sera lancée à l'installation du Widget pour permettre à l'utilisateur d'initialiser des variables ou des préférences ;

- resizeMode permet de définir comment le widget peut être redimensionné (introduit par Android 3.0).

3-3 - Description de l'interface graphique avec un fichier de layouts

Avant de vous lancer dans une interface graphique ultracomplexe en pensant que tout fonctionne à l'identique, vous devez savoir que seuls les composants suivants sont utilisables dans une AppWidget :

Vous pouvez utiliser les layouts suivants :

- FrameLayout  ;

- LinearLayout  ;

- RelativeLayout .

Et les composants graphiques suivants:

- AnalogClock  ;

- Button  ;

- Chronometer  ;

- ImageButton  ;

- ImageView  ;

- ProgressBar  ;

- TextView  ;

- ViewFlipper  ;

- ListView  ;

- GridView  ;

- StackView  ;

- AdapterViewFlipper .

Attention : vous ne pouvez pas utiliser les classes filles de ces composants.

À cette restriction près, vous construisez votre composant graphique de manière usuelle, comme pour une activité (ne mettez pas de listeners dans le xml, cela ne sert à rien, ils ne seront pas appelés).

Un exemple de fichier de layout :

Définition de l'interface graphique de l'AppWidget dans le fichier res/layout/widget_layout.xml

1. <?xml version="1.0" encoding="utf-8"?>

2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

3. android:layout_width="fill_parent"

4. android:layout_height="fill_parent"

5. android:orientation="vertical"

6. android:background="#FF00FF00">

8. <TextView

9. android:id="@+id/textView"

10. android:layout_width="fill_parent"

11. android:layout_height="wrap_content"

12. android:text="@string/app_name" />

14. <ImageButton

15. android:id="@+id/imageButton"

16. android:layout_width="wrap_content"

17. android:layout_height="wrap_content"

18. android:background="@drawable/ic_android2ee_violet" >

19. </ImageButton>

21. <ImageView

22. android:id="@+id/imageView"

23. android:layout_width="wrap_content"

24. android:layout_height="wrap_content"

25. android:src="@drawable/ic_android2ee_blc" />

27. </LinearLayout>

3-4 - Règles graphiques des AppWidgets

Le document de référence concernant les règles graphiques à utiliser pour les AppWidgets se trouve ici : http://developer.android.com/guide/practices/ui_guidelines/widget_design.html .

3-4-1 - Taille

Tout d'abord, il faut savoir que l'écran d'accueil est divisé en une grille de cellules. Pour les smartphones, cette grille est majoritairement de quatre lignes par quatre colonnes et sur les tablettes de huit colonnes par sept lignes. Quand votre widget va être ajouté à l'écran d'accueil, il sera étiré pour occuper le nombre de cellules minimal (horizontalement et verticalement) de manière à respecter l'espace défini par les variables MinWidth et MinHeight que vous avez données dans sa définition.

Ainsi, il est extrêmement important de penser en nombre de cellules requises pour afficher votre widget, puis de convertir ce nombre en MinWidth et MinHeight en utilisant la règle suivante :

Si n est le nombre de cellules à afficher, alors la taille associée en dp est (70×n)-30. Autrement dit, le tableau suivant s'applique

Nombre de cellules

Taille associée (dp)

1

40

2

110

3

180

4

250

n

(70×n)-30

Donc un widget nécessitant au moins trois cellules horizontalement et quatre verticalement définira sa taille minimale ainsi :

Exemple de définition d'une taille pour trois cellules horizontales et quatre verticales

1. android:minWidth="180dp"

2. android:minHeight="250dp"

La documentation officielle donne aussi comme autre forme de calcul de donner la vraie taille minimale de votre widget. Pour cela, il faut désigner votre widget dans sa taille minimale et calculer cette taille. Il sera automatiquement étiré pour remplir le nombre de cellules. Qui dit étirement dit image de type Nine-Patch (soit dit en passant).

Pour GingerBread, il était couramment admis que les cellules avaient une dimension de 74*74 dp (density independant pixel) et 2 de marge. Mais ça, c'était avant.

Et puis une autre remarque, depuis HoneyComb, les Widgets sont redimensionnables (en largeur/hauteur). Du coup, MinHeight et MinWidth deviennent en fait les tailles par défaut. Pour définir des tailles minimales qui soient respectées, il faut utiliser MinResizeHeight et MinResizeWidth. Bien sûr, cela ne marche pas pour un SDK inférieur à Honey Comb.

3-4-2 - Marge

Il faut définir des marges pour votre widget et, mauvaise nouvelle, cela dépend de la version du SDK de l'appareil sur lequel va être ajouté votre widget. En effet depuis ICS, les marges sont automatiquement ajoutées.

Pour pallier ce problème, voilà comment il faut faire (toujours d'après le site Android) :

- Définissez votre balise sdk-target à 14 (ou plus) dans le AndroidManifest.xml. Attention, cela implique beaucoup de choses pour votre projet, réfléchissez-y bien ;

- Dans le fichier de layout de votre widget définissez comme marge pour votre layout parent android:padding=« @dimen/widget_margin » ;

- Ensuite, créer deux fichiers dans votre dossier de ressource values : res/values/dimens.xml qui contient la ligne suivante :

< dimen name = « widget_margin » > 8dp </ dimen >

- res/values-v14/dimens.xml qui contient la ligne suivante :

< dimen name = « widget_margin » > 0dp </ dimen >

Ce qui est important ici, c'est de comprendre qu'à partir du SDK 14, le fichier récupéré sera celui appartenant au dossier values-v14.

3-4-3 - Background

Comme je vous l'ai dit, votre widget sera étiré pour remplir l'espace qui lui est alloué. Le corollaire direct est que votre image de fond doit être une image de type nine-patch (possédant des zones définies comme étant à étirer).

Enfin sachez que Google met à votre disposition son templatePack graphique, qui vous fournit les briques graphiques élémentaires pour de designer votre widget (si vous êtes graphiste). Vous le trouverez ici : http://developer.android.com/shareables/app_widget_templates-v4.0.zip .

4 - Code Java de l'AppWidget

Passons aux choses sérieuses et voyons comment fonctionne ce composant.

Il faut toujours garder en tête que :

- votre AppWidget n'est pas une Activité, mais un BroadcastReceiver ;

- votre AppWidget étend AppWidgetProvider ;

- votre AppWidget peut être installée plusieurs fois dans l'écran d'accueil, on parle donc d'instance de votre AppWidget.

4-1 - Les méthodes natives de l'AppWidgetProvider

L'AppWidget possède les méthodes de réception d'Intents suivantes :

Méthodes natives de réception d'Intents

1. public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {}

2. public void onDeleted(Context context, int[] appWidgetIds) {}

3. public void onDisabled(Context context) {}

4. public void onEnabled(Context context) {}

5. public void onReceive(Context context, Intent intent) {}

Chacune de ces méthodes est appelée à la réception d'un Intent spécifique.

4-1-1 - onUpdate

La méthode onUpdate est appelée lors de la réception de l'Intent ACTION_APPWIDGET_UPDATE . Cet Intent est envoyé par le système dans les cas suivants :

- sortie de veille/redémarrage de l'appareil ;

- première installation d'une instance de votre AppWidget ;

- périodiquement en fonction du paramètre android:updatePeriodMillis que vous avez défini pour votre widget.

Une exception, toutefois, si vous avez associé à votre AppWidget une activité d'initialisation (AppWidget Configuration Activity), la méthode onUpdate ne sera pas appelée lors de l'installation de l'appareil, c'est l'activité qui sera exécutée.

Il faut donc dans cette méthode initialiser votre widget. Le mot update est mal choisi.

4-1-2 - onDeleted

Cette méthode est appelée lors de la réception de l'Intent ACTION_APPWIDGET_DELETED . Cet Intent est envoyé par le système lors de la suppression de l'une de vos instances de votre AppWidget de l'écran d'accueil.

4-1-3 - onDisable et onEnable

La méthode onEnable est appelée la première fois qu'une instance de votre AppWidget est installée. Si l'utilisateur ajoute de nouvelles instances, cette méthode ne sera pas rappelée. Si vous avez besoin d'installer une base de données (ou ce genre de choses), c'est dans cette méthode qu'il faut le faire. Elle est appelée lors de la réception de l'Intent ACTION_APPWIDGET_ENABLED .

La méthode onDisable est appelée lorsque la dernière instance de votre AppWidget est désinstallée. Si vous avez installé une base de données, c'est ici qu'il faut la détruire (ou demander à l'utilisateur s'il souhaite cette destruction). Elle est appelée lors de la réception de l'Intent ACTION_APPWIDGET_DISABLED .

4-1-4 - onReceive

La méthode onReceive est la méthode native de récupération des Intents. Les Intents précédents sont filtrés et reventilés vers les méthodes ci-dessus, vous ne les recevrez pas dans la méthode onReceive.

La méthode onReceive va vous être utile pour écouter les Intents que vous vous envoyez, typiquement ceux liés à l'écoute des clics utilisateurs.

4-2 - Manipulation du AppWidgetManager et des RemoteViews

Toute l'interaction avec votre widget s'effectuera au travers de l'AppWidgetManager et de la RemoteView qui affiche (s'il s'agit de « l'AppWidgetManager et de la RemoteView » ensemble alors « qui affichent », sinon il faut revoir la phrase) vos composants graphiques.

4-2-1 - RemoteViews

Les RemoteViews sont des objets qui permettent de décrire une vue qui sera hébergée par le processus d'une autre application. La description xml de la vue est utilisée par la RemoteViews pour reconstruire une View (via un LayoutInflater) et la RemoteViews sert d'interface pour manipuler ces composants graphiques. Remarquez que la RemoteViews n'hérite que de la classe Objet, en d'autres termes, ce n'est pas une Views. D'autre part, ne vous amusez pas à contourner la RemoteViews pour manipuler directement vos interfaces graphiques, à part si vous avez compris toutes les problématiques associées à faire tourner votre code dans un autre processus (notion de droit, de fuite mémoire, de programmation concurrents). Soit vous travaillez chez Google sur le code source du SDK, soit pas. Si vous n'y travaillez pas, utilisez la RemoteViews pour modifier le comportement des composants graphiques qui la composent.

La création d'une RemoteView s'effectue de la manière suivante :

RemoteViews views;

views = new RemoteViews(context.getPackageName(), R.layout. widget_layout_two );

Le constructeur prend en paramètre le nom du package qui contient votre classe AppWidgetProvider qui gère votre widget et le layout qui contient la description de l'IHM à construire.

Pour manipuler les composants graphiques au sein de votre RemoteViews vous ne pouvez pas utiliser la méthode findViewsById, en fait vous ne devez pas récupérer de pointeur vers ces composants. Il faut utiliser les méthodes de l'objet RemoteViews qui vous permettent d'effectuer ces opérations. Quelques exemples :

Exemple d'utilisation du RemoteViews pour mettre à jour les composants graphiques qu'il contient

1. //Affecte un texte à un TextView (équivalent de TextView.setText)

2. views.setTextViewText(R.id.textView, context.getString(R.string.app_name) + " : " + i);

3. //Appelle la méthode passée en paramètre (setClickable) sur le composant en lui passant le paramètre true

4. views.setBoolean(R.id.checkBox, "setClickable", true);

5. //Appelle la méthode passée en paramètre (setClickable) sur le composant en lui passant le paramètre true

6. views.setString(R.id.imageButton, "setText", "La string a afficher");

7. //Définit la couleur du Texte

8. views.setTextColor(R.id.textView, Color.GREEN);

9. //Affiche ou cache un composant

10. views.setViewVisibility(R.id.imageButton, true);

Comme on le voit dans ces exemples, on peut faire beaucoup de choses sur les composants, mais pas non plus tant que ça. Le pattern est toujours le même, on utilise les méthodes de la RemoteViews pour modifier le comportement des composants graphiques qui la composent.

4-2-2 - AppWidgetManager

L'AppWidgetManager, quant à lui, nous permet de récupérer la liste des identifiants des instances de notre AppWidget installées sur l'écran d'accueil et de les mettre à jour.

Récupération des instances de l'AppWidget en utilisant l'AppWidgetManager

1. AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);

2. ComponentName componentName = new ComponentName(context, SimpleWidget.class);

3. int[] appWidgetIds = appWidgetManager.getAppWidgetIds(componentName);

4. final int N = appWidgetIds.length;

6. // Mise à jour de la vue de l'AppWidget dont l'identifiant est appWidgetId

7. appWidgetManager.updateAppWidget(appWidgetId, views);

J'attire votre attention sur AppWidgetManager.getInstance(context) qui implique que si vous souhaitez utiliser l'AppWidgetManager dans une méthode, vous devez passer à cette méthode le contexte, sinon vous ne retrouverez jamais votre AppWidgetManager.

4-2-3 - Exemple d'utilisation

Ce paragraphe va vous permettre de comprendre l'utilisation de la RemoteViews et de l'AppWidgetManager dans le contexte d'un AppWidgetProvider.

Tout d'abord une implémentation de la méthode onUpdate. Cette implémentation est primordiale à comprendre pour mettre en place un AppWidget. Elle vous explique comment créer et mettre à jour vos AppWidgets. Elle est quasiment générique et se retrouve dans pratiquement tous les AppWidget créées. En effet, il faut dans tous les cas, retrouver le nombre d'instances de vos AppWidget installées, puis pour chaque instance vous devez la recréer puis la mettre à jour en utilisant l'AppWidgetManager. Donc, prenez un temps particulier pour bien la comprendre, car vous allez la réutiliser.

Voici le code :

Méthode onUpdate

1. public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {

2. //Nombre d'instances installées

3. final int N = appWidgetIds.length;

4. //L'objet RemoteViews

5. RemoteViews views;

6. // identifiant de l'instance courante

7. int appWidgetId

9. // Mise à jour de toutes les instances du widget installées (donc une boucle for)

10. for (int i = 0; i < N; i++) {

11. // identifiant de l'instance courante

12. appWidgetId = appWidgetIds[i];

13. // Création de la vue

14. views = new RemoteViews(context.getPackageName(), R.layout.widget_layout_two);

15. //Il n'est pas possible de récupérer un élément de la RemoteViews pour le modifier

16. //Il faut utiliser les méthodes de la RemoteViews

17. //Affecte une image à un composant (équivalent à ImageView.setImageResource)

18. views.setImageViewResource(R.id.imageView, R.drawable.ic_android2ee_bleu_inv);

19. //Affecte un texte à un TextView (équivalent de TextView.setText)

20. views.setTextViewText(R.id.textView, context.getString(R.string.app_name) + " : " + i);

21. // Et surtout demande de mettre à jour l'instance de l'AppWidget dont l'identifiant est appWidgetId avec cette vue

22. appWidgetManager.updateAppWidget(appWidgetId, views);

23. }

24. }

Le deuxième exemple est celui d'une méthode de notre vue qui est appelée par la méthode onReceive et qui effectue la mise à jour du widget dont l'identifiant est passé en paramètre. Cette méthode est très utile, car elle factorise la mise à jour / création de vos instances d'AppWidget. En effet, vos instances seront mises à jour soit dans la méthode onUpdate, soit à la réception d'un Intent. Il est ainsi très utile de factoriser cette mise à jour/ construction. J'attire votre attention sur le paramètre widgetId de cette méthode ; celui-ci peut être l'identifiant d'une instance d'un widget (dans le cas où l'on vient de recevoir un Intent de cette instance) qu'il faut mettre à jour, ou il peut-être une constante qui demande de reconstruire toutes les instances de l'AppWidget, ici ALL_WIDGET (dans le cas où la méthode est appelée par onUpdate qui a à charge la reconstruction de toutes les instances). Enfin cet exemple est très utile, car il vous montre comment récupérer une instance de l'AppWidgetManager et de la liste des identifiants de vos instances d'AppWidget à partir de l'objet Context.

Le code de la méthode est très similaire à celui de onUpdate ci-dessus, le voici :

Méthode upDateWidget qui met à jour une (les) instance(s) de l'AppWidget

1. /*** The constant to ask to rebuild all the instance of the widget     */

2. private static final Integer ALL_WIDGETS = -1;

4. private void updateWidget(Context context, int widgetId) {

5. //Retrouver l'instance courante du AppWidgetManager associé à notre context

6. AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);

7. //Instancier le ComponentName qui n'est autre que le nom de la classe relative à notre contexte

8. ComponentName componentName = new ComponentName(context, SimpleWidget.class);

9. //Retrouver les identifiants des instances de notre AppWidget installées

10. int[] appWidgetIds = appWidgetManager.getAppWidgetIds(componentName);

11. //Calculer le nombre de ces instances

12. final int N = appWidgetIds.length;

13. // Déclarer l'objet RemoteView

14. RemoteViews views;

15. //Trouver l'identifiant de l'image qui mettra à jour l'image dans le widget

16. int drawableId= R.drawable.ic_android2ee_blc;

17. //identifiant de l'instance courante

18. int appWidgetId&nbsp;;

19. // Parcourir toutes les instances et les mettre à jour si l'identifiant correspond

20. for (int i = 0; i < N; i++) {

21. //Mise à jour de l'identifiant de l'instance

22. appWidgetId = appWidgetIds[i];

23. //Si cet identifiant est celui passé en paramètre ou si l'identifiant passé en paramètre est la constante pour

24. // tous les widgets on met à jour

25. if ((widgetId == ALL_WIDGETS) || (widgetId == appWidgetId)) {

26. //Instanciation de la RemoteView

27. views = new RemoteViews(context.getPackageName(), R.layout.widget_layout_two);

28. // Reconstruction de cette RemoteViews

29. views.setImageViewResource(R.id.imageView, drawableId);

30. views.setTextViewText(R.id.textView, context.getString(R.string.app_name) + " : " + i);

31. // Et surtout demande de mettre à jour l'instance de l'instance  avec cette vue

32. appWidgetManager.updateAppWidget(appWidgetId, views);

33. }

34. }

35. }

4-3 - Ajouter des listeners de clics à vos composants graphiques

Pour interagir avec votre AppWidget vous ne pouvez utiliser que les listeners de clics.

4-3-1 - Déclaration de l'Intent

Comme toujours, pour que votre AppWidget puisse écouter un Intent, il faut le déclarer dans le fichier AndroidManifest.xml de votre application. Pour cela il suffit de rajouter un nouveau bloc intent-filter dans la déclaration de votre AppWidget :

Déclaration de l'Intent dans le fichier AndroidManifest.xml

1. <!-- Broadcast Receiver that will process AppWidget updates -->

2. <receiver

3. android:label="@string/widget_name"

4. android:name=".SimpleWidget" >

5. <intent-filter >

6. <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />

7. </intent-filter>

9. <intent-filter >

10. <action android:name="com.android.tuto.widget.simple.sample1.click1" />

11. </intent-filter>

13. <meta-data

14. android:name="android.appwidget.provider"

15. android:resource="@xml/widget_properties_definition" />

16. </receiver>

Ensuite, il faudra, dans votre classe AppWidget qui étend AppWidgetProvider, déclarer la constante qui correspond à cet Intent pour pouvoir l'utiliser :

private static final String INTENT_CLICK1 = « com.android.tuto.widget.simple.sample1.click1 » ;

4-3-2 - Mise en place de l'envoi de l'Intent

Cela s'effectue au moment où vous construisez votre RemoteViews, vous allez décider quels composants vous faites réagir aux clics utilisateurs et leur adjoindre une PendingIntent. L'exemple suivant montre comment le faire dans la méthode onUpdate. La partie de code qui nous intéresse est mise en avant ;

Mise en place de l'envoi de l'Intent dans la méthode onUpdate

1. public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {

2. //Nombre d'instances installées

3. final int N = appWidgetIds.length;

4. //L'objet RemoteViews

5. RemoteViews views;

6. // identifiant de l'instance courante

7. int appWidgetId

9. // Mise à jour de toutes les instances du widget installées (donc une boucle for)

10. for (int i = 0; i < N; i++) {

11. // identifiant de l'instance courante

12. appWidgetId = appWidgetIds[i];

13. // Création de la vue

14. views = new RemoteViews(context.getPackageName(), R.layout.widget_layout_two);

15. //Il n'est pas possible de récupérer un élément de la RemoteViews pour le modifier

16. //Il faut utiliser les méthodes de la RemoteViews

17. //Affecte une image à un composant (équivalent à ImageView.setImageResource)

18. views.setImageViewResource(R.id.imageView, R.drawable.ic_android2ee_bleu_inv);

19. //Affecte un texte à un TextView (équivalent de TextView.setText)

20. views.setTextViewText(R.id.textView, context.getString(R.string.app_name) + " : " + i);

23. // ->Début de la partie qui nous intéresse&nbsp;:

24. //----------------------------------------------------

25. // Ajout d'un listener de click

26. // Déclaration de l'Intent où INTENT_CLICK1&nbsp;est défini comme suit

27. //private static final String INTENT_CLICK1 = "com.android.tuto.widget.simple.sample1.click1";

28. Intent intentClick1 = new Intent(INTENT_CLICK1);

29. // Ajout de l'identifiant de l'appWidget pour savoir quelle instance envoie cet Intent

30. intentClick1.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);

31. // Création du PendingIntent qui sera envoyé. J'attire votre attention sur le deuxième paramètre

32. //qui permet d'associer un unique PendingIntent à votre composant, sinon ils seront identiques

33. // pour toutes les instances de vos widgets et vous ne retrouverez jamais votre paramètre extra de

34. // l'Intent initial

35. PendingIntent pendingIntent = PendingIntent.getBroadcast(context, appWidgetId, intentClick1, 0);

36. // Affectation de ce PendingIntent à l'action du clic sur le composant souhaité

37. views.setOnClickPendingIntent(R.id.imageView, pendingIntent);

39. // <-Fin de la partie qui nous intéresse.

40. //------------------------------------------------

42. // Et surtout demande de mettre à jour l'instance de l'AppWidget dont l'identifiant est appWidgetId avec

43. // cette vue

44. appWidgetManager.updateAppWidget(appWidgetId, views);

45. }

46. }

4-3-3 - Récupération des Intents envoyés

Cela s'effectue dans votre méthode onReceive qui va recevoir l'Intent que vous venez d'envoyer et qui ventilera l'action à effectuer sur une méthode spécifique. Dans l'exemple ci-dessous cela met à jour l'IHM, on reconstruit donc la RemoteViews, mais vous pouvez effectuer toute autre action :

Récupération et traitements des Intents envoyés/reçus

1. public void onReceive(Context context, Intent intent) {

2. super.onReceive(context, intent);

3. // ici on traite les intents:

4. if (INTENT_CLICK1.equals(intent.getAction())) {

5. //Récupération de l'identifiant de l'instance de l'AppWidget qui a envoyé l'Intent

6. int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,ALL_WIDGETS);

7. //Appel de la méthode de traitement en lui passant le contexte si elle doit mettre à jour l'IHM

8. updateWidget(context,appWidgetId);

9. }

10. }

12. private void updateWidget(Context context, int widgetId) {

13. //Retrouver l'instance courante du AppWidgetManager associé à notre context

14. AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);

15. //Instancier le ComponentName qui n'est autre que le nom de la classe relative à notre contexte

16. ComponentName componentName = new ComponentName(context, SimpleWidget.class);

17. //Retrouver les identifiants des instances de notre AppWidget installées

18. int[] appWidgetIds = appWidgetManager.getAppWidgetIds(componentName);

19. //Calculer le nombre de ces instances

20. final int N = appWidgetIds.length;

21. // Déclarer l'objet RemoteView

22. RemoteViews views;

23. //Trouver l'identifiant de l'image qui mettra à jour l'image dans le widget

24. int drawableId= R.drawable.ic_android2ee_blc;

25. //identifiant de l'instance courante

26. int appWidgetId&nbsp;;

27. // Parcourir toutes les instances et les mettre à jour si l'identifiant correspond

28. for (int i = 0; i < N; i++) {

29. //Mise à jour de l'identifiant de l'instance

30. appWidgetId = appWidgetIds[i];

31. //Si cet identifiant est celui passé en paramètre ou si l'identifiant passé en paramètre est la constante pour

32. // tous les widgets on met à jour

33. if ((widgetId == ALL_WIDGETS) || (widgetId == appWidgetId)) {

34. //Instanciation de la RemoteView

35. views = new RemoteViews(context.getPackageName(), R.layout.widget_layout_two);

36. // Reconstruction de cette RemoteViews

37. views.setImageViewResource(R.id.imageView, drawableId);

38. views.setTextViewText(R.id.textView, context.getString(R.string.app_name) + " : " + i);

39. // Ajout d'un listener de click

40. // Déclaration de l'Intent où INTENT_CLICK1&nbsp;est défini comme suit

41. //private static final String INTENT_CLICK1 = "com.android.tuto.widget.simple.sample1.click1";

42. Intent intentClick1 = new Intent(INTENT_CLICK1);

43. // Ajout de l'identifiant de l'appWidget pour savoir quelle instance envoie cet Intent

44. intentClick1.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);

45. // Création du PendingIntent qui sera envoyé. J'attire votre attention sur le deuxième paramètre

46. //qui permet d'associer un unique PendingIntent à votre composant, sinon ils seront identiques

47. // pour toutes les instances de vos widgets et vous ne retrouverez jamais votre paramètre extra de

48. // l'Intent initial

49. PendingIntent pendingIntent = PendingIntent.

50. getBroadcast(context, appWidgetId, intentClick1, 0);

51. // Affectation de ce PendingIntent à l'action du clic sur le composant souhaité

52. views.setOnClickPendingIntent(R.id.imageView, pendingIntent);

54. // Et surtout demande de mettre à jour l'instance de l'instance  avec cette vue

55. appWidgetManager.updateAppWidget(appWidgetId, views);

56. }

57. }

58. }

4-3-4 - Factorisation des noms des Intents dans le fichier string.xml

Dans l'exemple décrit dans ce paragraphe, la chaîne « com.android.tuto.widget.simple.sample1.click1 »  est dupliquée et écrite en dur dans le code. D'un point de vue pédagogique c'est plus clair. Par contre dans un vrai projet, il faut toujours factoriser vos chaînes de caractères. Pour cela, vous allez les déclarer dans votre fichier values/string.xml puis en faire référence dans votre Manifest et votre classe Java.

Ce qui donne dans votre fichier string.xml:

Déclaration des noms des Intent dans le fichier xml des strings

1. <resources>

3. ...

4. <string name="INTENT_CLICK1">com.android2ee.tuto.appwidget.sample1.click1</string>

5. <string name="INTENT_UPDATE">com.android2ee.tuto.appwidget.sample1.update</string>

6. ...

8. </resources>

Dans votre AndroidManifest.xml :

Déclaration des Intents dans le fichier AndroidManifest.xml

1. <receiver

2. android:label="@string/widget_name"

3. android:name=".AppWidgetTuto" >

4. <intent-filter >

5. <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />

6. </intent-filter>

8. <intent-filter >

9. <action android:name="@string/INTENT_CLICK1" />

10. </intent-filter>

12. <intent-filter >

13. <action android:name="@string/INTENT_UPDATE" />

14. </intent-filter>

16. <meta-data

17. android:name="android.appwidget.provider"

18. android:resource="@xml/widget_properties_definition" />

19. </receiver>

Et enfin dans votre code Java :

Déclaration des Intents dans le code Java

1. // Define its Intent

2. Intent intent = new Intent(context.getResources().getString(R.string.INTENT_UPDATE));

3. .

4. // Manage the Intent

5. if (context.getResources().getString(R.string.INTENT_CLICK1).equals(intent.getAction())) {

6. ...

4-4 - Utilisation de l'alarme pour la mise à jour du Widget

Vous pouvez lancer une Thread dans la méthode onUpdate ou dans vos autres méthodes pour mettre à jour votre AppWidget, mais c'est vraiment une très mauvaise idée. Je dirai même que c'est la porte ouverte aux fuites mémoires. Donc si vous pensez utiliser cette méthode, oubliez ça de suite.

Pour gérer la mise à jour de votre AppWidget vous pouvez utiliser le paramètre updatePeriodMillis  déclaré dans le fichier de définition de votre AppWidget, mais ce n'est pas très souple et de nombreuses restrictions s'imposeront à vous.

La meilleure façon d'effectuer ces mises à jour automatiques est d'utiliser l'alarme, l'AlarmManager. Cet objet permet d'envoyer périodiquement un Intent dans le système et ainsi de déclencher dans votre code une mise à jour. Le principe est identique à celui de la mise en place d'écouteurs de clics ; je définis un Intent et je le récupère dans la méthode onReceive pour le traiter.

Ainsi, il faut définir dans votre fichier manifeste que votre AppWidget écoute ce nouvel Intent

Déclaration de l'écoute de l'alarme dans le fichier AndroidManifest.xml

1. <!-- Broadcast Receiver that will process AppWidget updates -->

2. <receiver

3. android:label="@string/widget_name"

4. android:name=".SimpleWidget" >

5. <intent-filter >

6. <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />

7. </intent-filter>

9. <intent-filter >

10. <action android:name="com.android.tuto.widget.simple.sample1.click1" />

11. </intent-filter>

13. <intent-filter >

14. <action android:name="com.android.tuto.widget.simple.sample1.update" />

15. </intent-filter>

16. <meta-data

17. android:name="android.appwidget.provider"

18. android:resource="@xml/widget_properties_definition" />

19. </receiver>

Définir dans votre classe AppWidget, cette nouvelle variable

private static final String INTENT_UPDATE = « com.android.tuto.widget.simple.sample1.update » ;

Puis définir dans votre méthode onReceive l'action associée à la récupération de cet Intent :

Traitement de l'écoute de l'Intent d'alarme

1. public void onReceive(Context context, Intent intent) {

2. super.onReceive(context, intent);

3. // ici on traite les intents:

4. if (INTENT_CLICK1.equals(intent.getAction())) {

5. //Récupération de l'identifiant de l'instance de l'AppWidget qui a envoyé l'Intent

6. int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,ALL_WIDGETS);

7. //Appel de la méthode de traitement en lui passant le contexte si elle doit mettre à jour l'IHM

8. updateWidget(context,appWidgetId);

9. } else if(INTENT_UPDATE.equals(intent.getAction())) {

10. //On vient de recevoir l'Intent envoyé par l'alarme, on fait quelque chose

11. forceUpdate(context);

12. }

13. }

Ensuite, on déclare l'alarme et on la lance dans la méthode onEnable (celle qui est appelée à la première installation de la première instance de votre widget) :

Déclaration et initialisation de l'alarme

1. public void onEnabled(Context context) {

2. //récupération de l'AlarmManager

3. AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);

4. //Définition de l'intent

5. Intent intent = new Intent(INTENT_UPDATE);

6. //définition du pendingIntent envoyé

7. PendingIntent pendingIntentUpdate = PendingIntent.getBroadcast(context,0,intent,0);

8. //Mise en place de l'alarme&nbsp;: mode où le temps de veille ne compte pas

9. //dont la première alarme est déclenchée dans un demi-jour (12h)

10. //se répétera toutes les douze heures

11. //et enverra le pendingIntent

12. alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME,

13. AlarmManager.INTERVAL_HALF_DAY,

14. AlarmManager.INTERVAL_HALF_DAY,

15. pendingIntentUpdate);

16. }

Et enfin, on n'oublie pas de la désactiver quand l'utilisateur désinstalle la dernière instance de votre widget :

Nettoyage et désinstallation de l'alarme

1. public void onDisabled(Context context) {

2. super.onDisabled(context);

3. // récupération de l'AlarmManager

4. AlarmManager alarmManager = (AlarmManager)

5. context.getSystemService(Context.ALARM_SERVICE);

6. // Définition de l'intent

7. Intent intent = new Intent(INTENT_UPDATE);

8. // définition du pendingIntent envoyé

9. PendingIntent pendingIntentUpdate = PendingIntent.getBroadcast(context, 0, intent, 0);

10. // Suppression de l'alarme

11. alarmManager.cancel(pendingIntentUpdate);

12. }

Vous avez remarqué que nous avons déclaré deux fois le même PendingIntent, une méthode factorisant ce code serait la bienvenue :

Factorisation du code

1. /** * The constant action to set the alarm */

2. private final int SET_ALARM = 1;

3. /** * The constant action to cancel the alarm */

4. private final int CANCEL_ALARM = 2;

6. /** * La méthode pour mettre en place ou désactiver l'alarme des mises à jour */

7. private void manageAlarm (Context context, int action) {

8. // récupération de l'AlarmManager

9. AlarmManager alarmManager = (AlarmManager)

10. context.getSystemService(Context.ALARM_SERVICE);

11. // Définition de l'intent

12. Intent intent = new Intent(INTENT_UPDATE);

13. // définition du pendingIntent envoyé

14. PendingIntent pendingIntentUpdate = PendingIntent.getBroadcast(context, 0, intent, 0);

15. // Mise en place de l'action

16. if (CANCEL_ALARM == action) {

17. // Annulation de l'alarme associée au PendingIntent

18. alarmManager.cancel(pendingIntentUpdate);

19. } else if (SET_ALARM == action) {

20. //Mise en place de l'alarme: mode où le temps de veille ne compte pas

21. //dont la première alarme est déclenchée dans un demi-jour (12h)

22. //se répétera toutes les douze heures

23. //et enverra le pendingIntent

24. alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME,

25. AlarmManager.INTERVAL_HALF_DAY,

26. AlarmManager.INTERVAL_HALF_DAY, pendingIntentUpdate);

27. }

28. }

30. public void onEnable(Context context) {

31. // mise en place de l'alarme de l'AlarmManager

32. manageAlarm(context, SET_ALARM);

34. }

35. public void onDisabled(Context context) {

36. super.onDisabled(context);

37. // Suppression de l'alarme

38. manageAlarm(context, CANCEL_ALARM);

39. }

4-4-1 - Digression sur le PendingIntent

Un PendingIntent décrit un Intent et une action à effectuer avec lui. Il est remis à d'autres applications afin que celles-ci puissent effectuer l'action demandée de votre part dans le futur.

En donnant un PendingIntent à une autre application vous lui accordez « vos droits » le tant d'effectuer cette action. Elle agit en tant que représentant de votre application. C'est pour cela que le nom de l'Intent passé au PendingIntent est le nom de votre classe (path inclus).

Ainsi, dans notre exemple, l'AlarmManager enverra l'Intent comme s'il était vous dans le système.

4-5 - Utilisation du SharedPereference

Il est parfois utile voir nécessaire de faire persister des paramètres associés aux différentes instances des AppWidgets installées.

Pour cela l'utilisation de l'objet SharedPreference est préconisée. Deux manières d'effectuer ce traitement sont possibles :

- soit vous créez autant de fichiers SharedPreference que d'instances de vos AppWidget ;

- soit vous créez au sein de votre SharedPreference autant de clefs suffixées par l'identifiant de ces instances.

Personnellement, je trouve la première solution plus propre, car les clefs restent identiques, et le fichier qui stocke ces données est associé à l'instance de votre AppWidget. La seconde solution est moins structurée, moins propre, enfin de mon point de vue. D'un autre côté, la première solution oblige à la destruction de fichiers sur le terminal, la seconde non. Il faut donc peser le pour et le contre de ces deux solutions. Moi, j'opte pour la première.

Seule la première solution sera présentée.

Important : il ne faudra pas oublier de supprimer les données quand le widget sera détruit, sinon vous polluez la mémoire de l'appareil pour rien et c'est le mal.

Nettoyage des fichiers de préférences à la désinstallation d'une instance de l'AppWidget

1. /***  */

2. private static final String SIMPLE_WIDGET_NAME = "SimpleWidget_";

3. /** * */

4. private static final String ON_UPDATE_CALLED_COUNTER = "onUpdateCalledCounter";

6. @Override

7. public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {

8. // mise en place de l'alarme de l'AlarmManager

9. manageAlarm(context, SET_ALARM);

10. //Le nom du fichier de préférence

11. String myPreferences;

12. //L'objet SharedPreference

13. SharedPreferences preferences;

14. //Son éditeur

15. SharedPreferences.Editor editor;

16. //une variable pipo

17. int onUpdateCalledCounter;

18. //Le nombre de widgets

19. final int N = appWidgetIds.length;

20. //La remote views

21. RemoteViews views;

22. // Perform this loop procedure for each App Widget that belongs to this provider

23. for (int i = 0; i < N; i++) {

24. int appWidgetId = appWidgetIds[i];

25. //Gestion des preferences

26. // Le nom du fichier de préférence

27. myPreferences = SIMPLE_WIDGET_NAME+appWidgetId;

28. //Récupération de l'objet SharedPreference associé

29. preferences = context.getSharedPreferences(myPreferences, Activity.MODE_PRIVATE);

30. //Récupération de la valeur associée à la clef ON_UPDATE_CALLED_COUNTER

31. onUpdateCalledCounter=preferences.getInt(ON_UPDATE_CALLED_COUNTER, 0);

32. //on l'incrémente

33. onUpdateCalledCounter++;

34. //On ouvre l'éditeur

35. editor=preferences.edit();

36. //On met à jour la variable

37. editor.putInt(ON_UPDATE_CALLED_COUNTER, onUpdateCalledCounter);

38. //Et on n’oublie pas le commit

39. editor.commit();

40. //Et on continue la méthode

41. }

42. }

44. @Override

45. public void onDeleted(Context context, int[] appWidgetIds) {

46. super.onDeleted(context, appWidgetIds);

47. //Le nom du fichier de préférence

48. String myPreferences;

49. //Le fichier SharedPreference en tant que File et son fichier backup

50. File sharedPreferencefile, sharedPreferenceBackupfile;

51. //Le nombre de widgets

52. final int N = appWidgetIds.length;

54. //Destruction des fichiers SharedPreference associés à ces instances

55. for (int i = 0; i < N; i++) {

56. int appWidgetId = appWidgetIds[i];

57. //Tout d'abord il faut vider les valeurs de l'objet

58. // Le nom du fichier de préférence

59. myPreferences = SIMPLE_WIDGET_NAME+appWidgetId;

60. //Vidage du fichier

61. context.getSharedPreferences(myPreferences, Activity.MODE_PRIVATE).edit().clear().commit();

62. //ensuite il faut détruire le fichier lui-même&nbsp;:

63. //on les récupère

64. sharedPreferencefile= new File("/data/data/.../shared_prefs/"+myPreferences+".xml");

65. sharedPreferencefile= new File("/data/data/.../shared_prefs/"+myPreferences+".bak");

66. //on les détruit

67. sharedPreferencefile.delete();

68. sharedPreferenceBackupfile.delete();

69. }

70. }

4-6 - Lancement d'une activité à l'installation d'une instance du widget

Enfin un dernier mot concernant le lancement d'une activité à l'installation d'une instance du Widget.

Parfois, il est utile de lancer une activité quand une instance de votre AppWidget est installée. Cela permet d'interagir avec l'utilisateur pour lui demander par exemple de mettre en place les préférences associées à cet AppWidget (taux de rafraichissement).

C'est assez simple à mettre en place. Cela s'effectue en trois étapes.

Tout d'abord, il faut déclarer l'activité dans le Manifest et définir qu'elle se lance par réception de l'action APPWIDGET_CONFIGURE :

Déclaration de l'activité d'installation dans le fichier AndroidManifest.xml

1. <activity

2. android:name=".ConfigureAppWidget"

3. android:label="@string/app_name" >

4. <intent-filter>

5. <action android:name="android.apwidget.action.APPWIDGET_CONFIGURE" />

6. </intent-filter>

7. </activity>

Ensuite dans votre fichier de définition de votre AppWidget, il suffit de rajouter la balise de lancement de l'activité de configuration :

Déclaration de la liaison entre l'AppWidget et l'activité d'installation

1. <?xml version="1.0" encoding="utf-8"?>

2. <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"

3. android:label="@string/hello"

4. android:minWidth="180dip"

5. android:minHeight="40dip"

6. android:initialLayout="@layout/widget_layout"

7. android:updatePeriodMillis="1800000"

8. android:configure="com.android2ee.tuto.appwidget.sample1.ConfigureAppWidget"

9. >

Enfin, votre activité doit respecter certaines règles :

- récupérer l'identifiant de l'instance de l'AppWidget qui a appelé l'activité, en analysant l'Intent reçu ;

- renvoyer un Intent portant les données de votre configuration à l'AppWidget (si nécessaire).

Si vous souhaitez mettre à jour l'instance de votre AppWidget au sein de votre activité, il vous suffit de faire comme d'habitude, en utilisant l'AppWidgetManager que vous récupérez par le Context de votre activité.

La récupération de l'identifiant de l'instance se retrouve ainsi :

Récupération de l'instance de l'AppWidget suite à l'activité d'installation

1. Intent intent = getIntent();

2. Bundle extras = intent.getExtras();

3. if (extras != null) {

4. mAppWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID,

5. AppWidgetManager.INVALID_APPWIDGET_ID);

6. }

Ensuite, si vous souhaitez renvoyer des informations à votre AppWidget et terminer votre activité :

Mise en place de la fin de l'activité d'installation

1. Intent resultValue = new Intent();

2. resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);

3. setResult(RESULT_OK, resultValue);

4. finish();

Remarquez que si vous ne lancez pas cet Intent à la fin de votre activité, votre AppWidget le recevra de manière automatique, mais son résultat sera à RESULT_CANCEL. Ce mécanisme permet de notifier à l'APPWidget que l'utilisateur est sorti de l'activité via le bouton « back ». Ainsi, si vous souhaitez savoir si l'utilisateur a bien terminé son activité, utilisez ce mécanisme.

5 - Conclusion

J'espère que cet article vous a plu, vous êtes maintenant capable de mettre en place des AppWidgets sur la page d'accueil des téléphones Android permettant une meilleure expérience utilisateur, félicitations.

À bientôt pour d'autres articles sur le développement Android.

6 - Remerciements

J'adresse ici tous mes remerciements à Feanorin pour son implication, son aide et sa sympathie et à jacques_jean pour l'excellence de ses corrections orthographiques.

Je remercie les correcteurs techniques Feanorin et MrDuChnok pour la pertinence de leurs remarques.

Je remercie spécialement Monsieur Adam Daniel, DRH de développez.com, pour ses mails nocturnes, ses encouragements et son aide qui m'a été précieuse.