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.
<<?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 :
<?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.
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 »).
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 :
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 :
<?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 :
@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.