Android

Notifications push avec Android

La dernière version de l'OS pour appareils mobiles Android, FroYo (Android 2.2) a été annoncée lors du Google I/O le 20 mai dernier.

Cette nouvelle version compte de nombreuses nouveautés. Notamment le support de flash, la possibilité de transformer un téléphone en hotspot wifi. Et une autre fonctionnalité, Android Cloud Device Messaging. C2DM permet à une application web de transmettre des données à une application installée sur un appareil Android.

Ainsi, si votre application est un lecteur de flux RSS par exemple, votre serveur pourra transmettre de manière asynchrone à un appareil qui utilise celle-ci que de nouveaux éléments sont à lire. Alors, l'application n'aura plus qu'à aller automatiquement rafraichir la liste des entrées.

Voici en bref comment fonctionne C2DM :

  • Lorsque vous installez l'application sur un appareil Android, celle-ci s'enregistre auprès de Google, obtient un identifiant d'enregistrement et transmet cet identifiant à votre serveur en ligne.
  • Lorsque le serveur a besoin de pousser un message à l'appareil, il fait une requête POST chez Google.
  • Google transmet ce message à l'appareil, qui exécute alors l'évènement approprié.

A n'importe quel moment, l'application peut se désinscrire de C2DM afin d'arrêter de recevoir les notifications de push.

Android Cloud Device Messaging est encore en version alpha car supporté par très peu d'appareils mobiles Android.
En conséquent cet article est plus théorique que autre chose car je n'ai pas pu tester son utilisation sur une réelle application.

Enregistrement d'une application à C2DM

Afin de pouvoir recevoir des notifications, votre application doit s'enregistrer dans C2DM. Pour cela, vous devez tout d'abord avoir les permissions appropriées configurées dans votre fichier AndroidManifest.xml. Voici un exemple de contenu pour ce document permettant à votre application de recevoir des push :

<manifest package="com.example.myapp" ...>

    <!-- Seule cette application peut recevoir les messages et le résultat de l'enregistrement à C2DM -->
    <permission android:name="com.example.myapp.permission.C2D_MESSAGE" android:protectionLevel="signature" />
    <uses-permission android:name="com.example.myapp.permission.C2D_MESSAGE" />

    <!-- Cette application a l'autorisation de recevoir des messages -->
    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />

    <!-- Il est nécessaire d'avoir l'autorisation d'accéder à Internet pour pouvoir recevoir les messages. -->
    <uses-permission android:name="android.permission.INTERNET" />

    <!-- Seuls les serveurs C2DM peuvent transmettre des messages à l'application. -->
    <receiver android:name=".C2DMReceiver" android:permission="com.google.android.c2dm.permission.SEND">
        <!-- L'application peut recevoir des messages -->
        <intent-filter>
            <action android:name="com.google.android.c2dm.intent.RECEIVE" />
            <category android:name="com.example.myapp" />
        </intent-filter>

        <!-- L'application peut recevoir l'identifiant d'enregistrement au serveur C2DM -->
        <intent-filter>
            <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
            <category android:name="com.example.myapp" />
        </intent-filter>
</receiver>

Notre application est maintenant autorisée à s'enregistrer à C2DM. Mais cela n'est pas encore fait. Pour cela nous allons transmettre un Intent permettant d'identifier notre application.

Intent regIntent = new Intent("com.google.android.c2dm.intent.REGISTER");
regIntent.putExtra("app", PendingIntent.getBroadcast(this, 0, new Intent(), 0);
regIntent.putExtra("sender", "you@example.com");
startService(regIntent);

public void onReceive(Context context, Intent intent) {
    if (intent.getAction().equals("com.google.android.c2dm.intent.REGISTRATION")) {
        String registration = intent.getStringExtra("registration_id"); 

        if (intent.getStringExtra("error") != null) {
             // L'enregistrement a échoué. Réessayez plus tard.
        } else if (registration != null) {
            // L'enregistrement a correctement été effectué.
           // Transmettez ici l'identifiant d'enregistrement au serveur Web qui transmettra les messages.
        }
    }
}

Nous commençons ici par initialiser un Intent de type C2DM.intent.REGISTER. Puis nous transmettons deux valeurs à celui-ci :

  • Une identification de notre application, permettant de restreindre l'envoi de messages de push uniquement au serveur autorisé à cela.
  • Un email, identifiant du compte autorisé à transmettre des messages de push à l'application. C'est généralement un email défini par le développeur de l'application.

    startService(regIntent);

Puis nous initialisons le service. Ensuite nous écoutons la réception de tout message. Si il s'agit d'un message C2DM indiquant que nous avons réussi à nous enregistrer, nous validons cet enregistrement.

String registration = regIntent.getStringExtra("registration_id"); 

Nous récupérons l'identifiant d'enregistrement transmis à l'application.

if (regIntent.getStringExtra("error") != null) {
     // L'enregistrement a échoué. Réessayez plus tard.
} else if (registration != null) {
    // L'enregistrement a correctement été effectué.
    // Transmettez ici l'identifiant d'enregistrement au serveur Web qui transmettra les messages.
}

Enfin nous vérifions que l'enregistrement a été correctement été effectué et que l'identifiant a été correctement créé. Si c'est bien le cas, il ne vous restera plus qu'à transmettre cet identifiant à votre serveur web qui pourra alors commencer à envoyer des messages de push.

Les messages de push pourront maintenant être transmis à notre application. Voyons comment les recevoir et les traiter.

Transmission d'un message à l'application

Pour pouvoir transmettre des messages de push, il faut que votre serveur soit capable de transmettre des données en POST à Google. La requête doit être faite à l'URL https://android.apis.google.com/c2dm/send

Plusieurs paramètres sont requis pour transmettre un message à l'application :

  • registration_id - Il s'agit de l'identifiant d'enregistrement transmis à l'application lorsque celle-ci a activé C2DM.
  • collapse_key - Un identifiant permettant de regrouper divers messages similaires. Ainsi, si l'appareil est hors ligne lorsque vous envoyez ce message, il ne sera transmis que plus tard. Tous les messages ayant la même collapse_key seront transmis en un seul lors de la reconnexion de l'appareil, évitant ainsi trop de messages similaires en simultané.
  • data. - Une liste de clés/valeurs qui seront transmises à l'application. Vous pouvez en transmettre autant que vous le désirez.
  • delay_with_idle - Si l'appareil est en veille lorsque le message est envoyé, il ne sera alors pas reçu tout de suite mais uniquement lorsque l'utilisateur réactivera la machine.
  • Authorization: GoogleLogin auth=[AUTH_TOKEN] - Votre clé d'identification GoogleLogin.

La clé d'identification est transmise au serveur lorsque vous utilisez ClientLogin for Installed Applications. Elle permet de vous identifier. Vous devez être identifié avec l'email transmis et autorisé lorsque l'appareil s'est enregistré auprès de C2DM.

Google répondra alors à cette requête avec trois codes HTTP possibles :

  • 200 - Aucune erreur technique ne s'est produite. Cela ne signifie cependant pas que votre message a été envoyé. Le contenu de la page renvoyée peut contenir les choses suivantes :
    • id=<ID> - Identifiant du message si celui-ci a correctement été transmis.
    • Error=<code d'erreur> - Code d'erreur si le message n'a pas pu être transmis.
      • QuotaExceeded - Trop de messages ont été transmis. Réessayez plus tard.
      • DeviceQuotaExceeded - Trop de messages ont été transmis sur un appareil en particulier. Réessayez plus tard.
      • InvalidRegistration - identifiant d'enregistrement manquant ou invalide. Vous devez arrêter d'envoyer des messages à cet appareil.
      • NotRegistred - L'enregistrement n'est plus valide. Vous devez arrêter d'envoyer des messages à cet appareil.
      • MessageTooBig - Le message est trop gros. Réduisez le.
      • MissingCollapseKey - la collapse_key est manquante et doit être include.
  • 503 - Indique que le serveur est actuellement indisponible. Un entête Retry-After est alors transmis. Votre application ne doit pas retransmettre de requête avant la date transmise.
  • 401 - Indique que votre clé ClientLogin n'est pas valide.

Réception d'un message depuis l'application

Une fois que votre serveur a correctement transmis un message à Google, celui-ci retransmettra ce même message à votre appareil Android. A vous alors de l'intercepter. Pour cela, de la même manière que lorsque tout à l'heure, nous écoutions la réception de la validation de l'enregistrement de l'appareil, nous allons écouter la réception d'un nouveau message.

public void onReceive(Context context, Intent intent) {
    if (intent.getAction().equals("com.google.android.c2dm.intent.RECEIVE")) {
        String accountName = intent.getExtras().getString(Config.C2DM_ACCOUNT_EXTRA);
        String message = intent.getExtras().getString(Config.C2DM_MESSAGE_EXTRA);

        Log.d("PUSH", "Requête de push transmise pour le compte " + accountName);
     }
 }

Nous écoutons donc la réception d'un nouveau message.

if (intent.getAction().equals("com.google.android.c2dm.intent.RECEIVE")) {

Et nous vérifions qu'il s'agit bien de la réception d'un message de push.

String accountName = intent.getExtras().getString(Config.C2DM_ACCOUNT_EXTRA);

Ici, nous récupérons le nom du compte Google depuis lequel le message de push a été transmis. Toutes les clés/valeurs que vous aurez transmis dans la requête POST seront transmis et accessibles ici.

Log.d("PUSH", "Requête de push transmise pour le compte " + accountName);

Enfin ici, nous ne faisons que loguer la réception d'un nouveau message de push. A vous de faire ce que vou désirez.

Désinscription d'une application de C2DM

A tout moment, vous devez permettre aux utilisateurs de vos applications d'arrêter de recevoir des messages de push. Pour cela, il vous suffit de définir un Intent com.google.android.c2dm.intent.UNREGISTER, qui transmettra alors à Google votre demande de désinscription du push pour cette application.

Intent unregIntent = new Intent("com.google.android.c2dm.intent.UNREGISTER");
unregIntent.putExtra("app", PendingIntent.getBroadcast(this, 0, new Intent(), 0));
startService(unregIntent);

Vous pouvez aisément constater que nous définissons notre intent de la même manière que nous avons défini celui nous permettant de nous enregistrer au push, seul le nom de celui-ci change.

Conclusion

Avec C2DM, Google nous propose (enfin) une fonctionnalité permettant de transmettre des notifications diverses aux applications installées sur un appareil de manière intantanée (ou presque).

A vous de faire preuve d'imagination pour développer ces applications !

Déployer une application Android

Cet article est la suite de l'article Premiers pas en programmation Android. Dans cet article, nous allons reprendre l'application que nous avions développé précédemment et construire le package approprié afin de l'installer sur le SDK (afin de la tester) et sur un vrai téléphone (afin de l'utiliser).

Si vous n'avez pas suivi l'article précédent, vous pouvez toujours récupérer l'application directement construite sur github.
Cependant ce n'est pas la méthode la plus didactique d'y arriver ;)

Signer vos applications

Android requiert de signer numériquement chaque application que vous désirez installer sur une machine. Le système utilise cette signature afin de vérifier qui est l'auteur de l'application et ainsi d'établir des liens de confiance entre vos diverses application. La signature n'est pas utilisée pour vérifier quelles applications l'utilisateur peut installer. Il est inutile de certifier cette signature par une autorité qualifiée. Il est parfaitement autorisé et très typique pour des applications d'utiliser des certificats auto signés.

Certains aspects des signatures pourront influencer sur la stratégie que vous adopter à ce niveau. Principalement si vous prévoyez de développer de multiples applications. La stratégie recommandée par Android est d'avoir une signature pour toutes vos applications, pour les raisons suivantes :

  • Mises à jour - Toutes les versions d'une même application doivent être signées avec le même certificat. Si le certificat est le même, le système mettra à jour l'application. Sinon, l'utilisateur installera une nouvelle application lorsqu'il tentera en fait simplement de faire une mise à jour.
  • Modularité - Le système vous permet de partager le même processus entre diverses applications utilisant la même signature si vous le désirez, de manière à ce qu'elles soient considérées comme une seule application. De cette manière vous pouvez créer des applications modulaires ou chaque utilisateur pourra installer uniquement la ou les parties qu'il désire.

Une autre chose importante lorsque vous créerez votre signature est la période de validité de celle-ci. En effet, dès que la clé arrive à expiration, vos utilisateurs ne seront plus en mesure de mettre à jour leur application vers de nouvelles versions.

Par ailleurs si vous prévoyez de publier votre application sur Android Market, la clé que vous donnerez devra expirer après le 22 octobre 2033.

Construction en développement et exécution dans le SDK

Si vous avez construit votre projet de manière "normale", en utilisant la commande android create project alors, un fichier local.properties existe à la base de celui-ci. Si vous avez fait un git clone de mon projet sur GitHub alors ce fichier n'existe pas, vous devez le créer.

Ce fichier doit contenir le chemin vers le SDK android sur votre disque. Sur ma machine actuelle (Gentoo), le contenu de ce fichier est donc :

sdk.dir=/home/damien/android-sdk-linux
Si il n'existe pas, créez ce fichier avec le chemin approprié.

Puis construisons le package de l'application. Pour cela, nous allons utiliser ant. Ce projet, soutenu par la fondation Apache est un utilitaire permettant de construire des paquets java.

Construisons le paquet pour un environnement de dev. En console, tapez :

ant debug

Le rendu console devrait finir de la manière suivante.

(master) damien@pcdamien ~/projects/translator $ ant debug</p>

<p>BUILD SUCCESSFUL
Total time: 3 seconds
Si vous avez BUILD SUCCESSFUL à la fin, c'est que votre projet a correctement été construit.

Maintenant exécutons la machine virtuelle et installons l'application sur le téléphone. Dans l'article précédent, nous avons installé la SDK android. Créons un nouveau téléphone virtuel.

android create avd -n translator -t android-7
Nous créons une nouvelle machine virtuelle que nous nommons "translator" et qui va tourner sous le "target" android-7, ce qui équivant à Android 2.1.

Puis exécutons cette machine virtuelle.

emulator -avd translator &

Vous devez maintenant voir une fenêtre contenant un téléphone se lancer. Laissez le s'exécuter, cela peut prendre une à deux minutes. Une fois que cela est fait, débloquez le. La machine virtuelle est lancée !

Et elle est reliée virtuellement en USB à votre machine. Vous pouvez donc y accéder à distance. C'est ce que nous allons faire pour installer l'application.

adb install bin/translator-debug.apk

Cette commande se connectera au téléphone virtuel et installera votre application dessus. Le rendu console doit être le suivant :

2216 KB/s (34777 bytes in 0.015s)
	pkg: /data/local/tmp/translator-debug.apk
Success

Si, en revanche, vous avez le rendu suivant :

error: device offline
Essayez de manipuler un petit peu le téléphone (ouvrez le menu par exemple). Il n'a pas été détecté comme étant actif.

Votre application est maintenant installée. Rendez-vous dans le menu du téléphone, vous verrez son icone apparaitre.

Construction en production et installation sur un téléphone

Afin de construire notre application de manière à pouvoir la releaser sur un téléphone ou sur l'Android Market, nous devons créer la signature numérique donc nous avons parlé dans la première partie. Pour cela, java nous propose un outil en ligne de commande permettant de gérer ces signatures. Il s'agit de keytool.

Voici comment j'ai généré ma propre clé :

keytool -genkey -v -keystore dmathieu.keystore -alias dmathieu -keyalg RSA -validity 10000

Les paramètres passés sont les suivants :

  • -genkey - Indique qu'il faut générer une paire de clés (privées et publiques).
  • -v - Active le rendu console complet lors de la génération de la clé
  • -keystore dmathieu.keystore -
  • -storepass <password> - Le mot de passe du "keystore". Evitez de le transmettre de cette manière. Si vous ne renseignez rien ici, le mot de passe vous sera demandé lors de la génération de la clé.
  • -alias dmathieu - Un alias pour la clé. Seuls les 8 premiers caractères sont pris en compte.
  • -keyalg RSA - L'algorithme d'encryption de la clé. Les algorithmes disponibles sont RSA et DSA.
  • -dname <dname> - Un nom décrivant qui a créé la clé.
  • -validity 10000 - Le nombre de jours de validité de la clé.
  • -keypass <password> - Le mot de passe de votre clé. Evitez de le transmettre de cette manière. Si vous ne renseignez rien ici, le mot de passe vous sera demandé lors de la génération de la clé.
En exécutant cette commande, un fichier dmathieu.keystore est créé. Il contient votre clé. Sauvegardez la dans un endroit ou vous êtes sur qu'elle ne sera pas perdue mais ou elle restera en sécurité (personnelement, je l'ai stockée dans Google Docs).

Dans votre projet, ouvrez maintenant le fichier build.properties. Dans ce fichier, nous devons préciser le chemin vers votre keystore ainsi que l'alias de la clé.

key.store=/home/damien/projects/dmathieu.keystore
key.alias=dmathieu
Nous pouvons maintenant construire notre application pour une release avec la clé que nous venons de créer.

En ligne de commande, utilisons ant pour construire notre application en release.

ant release

Ceci fait plusieurs choses :

  • Votre application est construite, comme lors d'un ant debug.
  • Votre certificat est ajouté à l'application, de manière à l'authentifier.
  • Le package est optimisé, afin d'en optimiser les performances.

Votre projet a correctement été construit et est prêt à être installé sur votre téléphone. Il est disponible en bin/translator-release.apk. Vous n'avez plus qu'à brancher votre téléphone en USB et installer l'application dessus. Ou encore publier celle-ci sur l'Android Market !

Essayez par vous même vous rendant sur l'Android Market puis en cherchant l'application Translator et en l'installant. C'est l'application que nous avons développé lors de ces articles.

Conclusion

Nous avons, dans ces deux premiers articles sur Android, vu comment développer une application, la tester et l'installer sur un téléphone. Nous sommes cependant très loin d'avoir fait le tour du développement d'applications Android. Nous pourrions par exemple ajouter un widget à notre application. Et de multiples autres choses !

Si vous désirez aller plus loin, je ne peux que vous conseiller trois ressources :

  • l'Android Developer Guide - La documentation officielle en ligne.
  • Hello, Android chez PragProg - Un livre disponible au format PDF (la version finale papier devrait arriver en juillet). Uniquement en Anglais. Je l'ai acheté et vous le recommande vivement.
  • Programmation Android chez Eyrolles - sorti il y a une semaine.

A vous de jouer maintenant ! Quelles applications ces deux articles vous ont-ils poussé à développer ?

Premiers pas en programmation Android

Si vous me suivez sur GitHub, vous avez pu constater que depuis deux semaines, je m'amuse un peu en Java avec Android. Le développement d'applications Android plutôt intuitif si vous avez quelques bases en Java (pour ma part, mes bases remontent à plus de 3 ans en arrière. Comme quoi c'est vraiment intuitif). Dans cet article, nous allons voir les bases du développement d'applications Android en prenant comme exemple l'application qui m'a permis à moi, de me familiariser avec : translator.

Le fonctionnement de cette application est très simple. Vous entrez la langue d'entrée, la langue de sortie, la chaine à traduire. Et, en utilisant Google Translate, l'application va traduire cette chaine.

L'application que nous allons développer ici est disponible sur github. Ca s'appelle translator

Installation du SDK

Afin de pouvoir tester correctement votre application, il existe un émulateur Android permettant de simuler un téléphone et ainsi d'exécuter votre application. Bien évidemment ce n'est qu'un émulateur. Vous ne pourrez pas réellement passer des appels. En revanche cela vous permettra de tester efficacement votre application.

Vous pouvez le télécharger depuis la documentation développeurs d'Android en fonction de votre système d'exploitation.

Génération du projet

Parce que nous n'utilisons pas Eclipse, nous perdons de nombreux raccourcis apportés par l'IDE. Mais cette non utilisation est un choix. Ne nous restreignons pas à un IDE et développons directement avec notre éditeur favori !

Nous allons d'abord devoir générer un nouveau projet. Android propose une commande console pour cela : android create project.

android create project \
-t <target_ID> \
-n <your_project_name> \
-p /path/to/your/project \
-a <your_activity_name> \
-k <your_package_namespace>

Plusieurs options sont requises.

  • -t target : Il s'agit de la version de la plateforme Android que nous allons utiliser (ici Android 2.1). Pour obtenir la liste de toutes les plateformes disponibles, entrez androit list targets.
  • -n name : Le nom que vous donnez à votre projet
  • -p path : Le chemin dans lequel le nouveau projet sera créé
  • -a activity : Le nom de la fenêtre qui sera ouverte par défaut lorsque l'application sera lancée.
  • -k package : Le nom du package qui sera créé. Il doit suivre la convention de nommage des packages en java.

Créons notre nouveau projet que nous allons nommer "Translator". Ce projet nous permettra de traduire une chaine de caractères d'une langue à l'autre grâce à Google Translate.

android create project \
-t 1 \
-n Main \
-p ~/projects/translator \
-a Translator \
-k com.android.translator

Cela va générer de multiples fichiers dans votre nouveau projet Android

Le java pur : traduire une chaine avec Google Translate

Dans cet exemple, nous allons développer une application simple. Le principe est le suivant : nous choisissons deux langues. Une en entrée et une en sortie. Puis nous saisissons du texte. Et notre texte sera traduit d'une langue à l'autre.

Le langage de base pour toute application Android est le java. Dans cet article, je vais considérer que vous avez déjà des bases en Java. Si ce n'est pas le cas, vous trouverez de nombreux articles sur programmez.com.

Il y a encore 3 semaines je n'avais pas retouché à Java depuis près de 3 ans. Et même à l'époque, je n'avais vu que les bases. Une connaissance minimale, afin de comprendre les principes est donc suffisante.

Pour cette application nous allons utiliser une librairie open source et permettant de faire des appels à Google Translate : Google API Translate Java. Vous devez tout d'abord include cette librairie dans votre projet nouvellement créé. Téléchargez la sur Google Code (le fichier .jar) et placez la dans le dossier /libs de votre projet.

Nous allons pouvoir tout de suite coder une classe d'abstraction faisant appel à cette librairie

package com.android.translator;</p>

<p>import com.google.api.translate.Language;
import com.google.api.translate.Translate;</p>

<p>public class Translator {
	/*
	 * Takes the string to translate and does the work
	 */
	public static String translate(String text, Language lang_from, Language lang_to) throws Exception {
		Translate.setHttpReferrer("http//www.dmathieu.com");
		String translatedText = Translate.execute(text, lang_from, lang_to);
		return translatedText;
	}
}

Que faisons-nous ici ?

package com.android.translator;</p>

<p>import com.google.api.translate.Language;
import com.google.api.translate.Translate;
Nous définissons le package courant et nous incluons la librarie précédemment téléchargée qui fera la liaison avec Google Translate.

Translate.setHttpReferrer("http//www.dmathieu.com");
L'API Google Translate requiert de fournir un referer, pour des raisons statistiques internes. Vous pourrez utiliser la librairie quelque soit l'URL que vous mettez ici. Mais tentez tout de même d'en placer une indicative de votre application.

String translatedText = Translate.execute(text, lang_from, lang_to);
Nous récupérons la chaine traduite, qui sera ensuite retournée.

Ainsi nous pouvons faire appel à Translator.translate afin de traduire une chaine d'une langue vers l'autre.

Translator.translate("Hello World", Language.ENGLISH, Language.FRENCH);

Placez ce document dans un fichier que nous nommerons Translator.java dans le dossier src/com/android/translator. Et ... C'est tout pour le moment. Nous allons maintenant pouvoir nous attaquer au réel développement de l'application et de son interface.

Création de l'interface Android

Nous allons maintenant créer l'interface de notre application, contenant deux listes déroulantes, un champ de texte, un bouton et notre chaine traduite. L'interface de votre application se développe en XML.

Voici le XML permettant d'obtenir le rendu de la capture d'écran plus haut, situé dans res/layout/main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:layout_weight="1">
    	
    <Spinner 
        android:id="@+id/languages_from"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:prompt="@string/from_languages_prompt" />
				
    <Spinner 
        android:id="@+id/languages_to"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:prompt="@string/to_languages_prompt" />
				
    <EditText
        android:id="@+id/text_to_translate"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:background="@android:drawable/editbox_background"
        android:prompt="@string/text_to_translate" />
				
    <Button
        android:id="@+id/translate_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:text="@string/translate_button" />
			
    <TextView
        android:id="@+id/translated_text"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />
</LinearLayout>

Qu'est-ce qui se passe ?

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
		android:layout_weight="1">
</LineraLayout

Nous définissons le cadre de notre fenêtre. LinearLayout nous permet de définir une fenêtre contenant divers éléments qui seront positionnés les uns à la suite des autres, soit horizontalement soit verticalement.

Il existe, en plus des LinearLayout, divers types d'éléments permettant de créer des cadres dans lequels vous pourrez placer les éléments de votre interface :

  • AbsoluteLayout : Un layout permettant de placer ses éléments de manière absolue, en spécifiant leurs coordonnées x/y.
  • FrameLayout : Un layout permettant d'afficher un et un seul élément. Si plusieurs éléments sont présent, le dernier sera au dessus. La taille du layout sera celle du plus grand des enfants.
  • RelativeLayout : Un layout dans lequel chacun des éléments sont positionnés à la suite l'un de l'autre.
  • TableLayout : Un layout permettant de positionner ses enfants en lignes et colonnes.

Puis à l'intérieur de ce layout nous avons divers éléments.

<Spinner
    android:id="@+id/languages_from"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:prompt="@string/from_languages_prompt" />

L'élément Spinner permet de définir une liste déroulante. Nous en avons donc ici deux, un pour le langage en entrée et un pour le langage en sortie.

<EditText
    android:id="@+id/text_to_translate"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:background="@android:drawable/editbox_background"
    android:prompt="@string/text_to_translate" />

L'élément EditText est un champ textuel à remplir. L'utilisateur pourra y placer le contenu de son choix. Ici, nous y rentreront la phrase ou les mots à traduire.

<Button
    android:id="@+id/translate_button"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentRight="true"
    android:text="@string/translate_button" />

Afin de signifier à l'application que nous avons fini de taper la phrase et que nous sommes prêt à en obtenir la traduction, nous ajoutons ici un bouton.

<TextView
    android:id="@+id/translated_text"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content" />

Enfin nous ajoutons un champ de texte non éditable qui contiendra la chaine de caractères traduite.

Chaines de traduction

Lorsque, tout à l'heure, nous exécuterons l'application que nous sommes en train de développer, vous pourrez cliquer sur le spinner et choisir une langue.

Alors vous verrez la capture d'écran de gauche. Comme vous pouvez le constater, nous avons une petite phrase explicative disant "Choose a language to translate from".

 
Cette chaine est définie par l'attribut android:prompt du spinner.

android:prompt="@string/from_languages_prompt"
Il s'agit ici d'un appel à une chaine de traductions. Ouvrons res/values/strings.xml

Notre fichier ressemble à ceci :

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">Translator</string></p>

<p>    <string name="from_languages_prompt">Choose a language to translate from</string>
    <string name="to_languages_prompt">Choose a language to translate to</string>
    <string name="text_to_translate">The text you wish to translate</string>
    <string name="translate_button">Translate!</string>
</resources>
Nous y plaçons toutes les chaines de caractère de notre application et ceci sera leur traduction par défaut (l'Anglais généralement).

Vous pouvez constater que dans main.xml, nous faisons appel à @strings/xxx. Vous l'aurez compris, @strings correspond au nom du fichier. Vous pouvez donc sans aucun problème diviser les chaines de caractères de vos grosses applications en de multiples fichiers.

Si nous désirons ajouter une traduction en Français à notre application, il nous suffira alors de créer res/values-fr/strings.xml Qui contient, pour notre application :

Android récupèrera automatiquement la traduction appropriée en fonction des paramètres de langue définis par l'utilisateur sur son téléphone.

Remplissage des Spinner

Le contenu de nos listes déroulantes sera la liste de toutes les langues que Google Translate peut traduire. Nous allons remplir cela dynamiquement en fonction de toutes les langues disponibles dans la librairie Google Translate que nous utilisons. Cette liste est un enum Java : un objet contenant une liste d'éléments.

Rappelez vous, lorsque vous avez créé votre projet, vous avez défini le nom d'une classe "Activity", qui caractérise la fenêtre par défaut. Si vous avez suivi cet article à la lettre, votre Activity doit se nommer Main. Vous avez donc un fichier src/com/android/translator/Main.java. Ouvrons le.

Il contient une activity vide, permettant uniquement de charger la fenêtre.

package com.android.translator;</p>

<p>import android.app.Activity;
import android.os.Bundle;</p>

<p>public class HelloAndroid extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
}

C'est la méthode onCreate qui nous intéresse. Elle est appelée avec un paramètre savedInstanceState qui est le status précédent de l'application. Nous commençons par faire un appel à cette même méthode onCreate de la classe parente, permettant ainsi de charger l'environnement requis par Android pour l'application.

La ligne suivante est plus intéressante.

setContentView(R.layout.main);

R.layout.main est un entier (objet Integer). Chaque élément de votre interface (les éléments XML définis précédemment) est défini en Java par un entier. Vous pouvez donc retrouver chacun d'eux grâce à cet entier. Et vu qu'une constante contenant ce même entier est définie à chaque fois, vous pouvez le retrouver encore plus facilement ! Ici, R.layour.main caractérise le fichier layout/main.xml. Vous pouvez donc définir l'interface qui sera affichée par votre activity.

Vu que nous allons devoir ajouter du contenu à l'intérieur de nos deux Spinner, nous allons donc devoir récupérer leur contenu. Ils sont également identifiés par un entier. Dans notre spinner, nous avons défini un attribut android:id.

android:id="@+id/languages_from"

Nous allons donc pouvoir récupérer l'identifiant de notre spinner avec la constante R.id.languages_from

Spinner language_from = (Spinner) findViewById(R.id.languages_from)
Spinner language_to = (Spinner) findViewById(R.id.languages_to)
Jusque la, ça va ! Remplissons les maintenant.

Nous allons pour cela devoir créer un objet de type ArrayAdapter, qui sera un tableau de chaines de caractère, chaque élément représentant une entrée de la liste déroulante.

ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
Ici, nous ne faisons que créer cet adapter. Ajoutons-y nos données.

Pour ajouter un élément à un adapter, nous avons juste à utiliser la méthode add.

adapter.add("Ma Valeur");

Ici, toutes nos valeurs sont contenues dans un enum de la librairie Google Translate. Nous devons parcourir tous ces éléments et ajouter toutes les clés.

for (Language l : Language.values()) {
    adapter.add(l.name());
}

Super ! Une dernière petite chose pourtant. Notre adapter a été créé mais n'a été assigné nulle part. Il faut donc assigner les valeurs à nos deux spinners.

languages_from.setAdapter(adapter);
languages_to.setAdapter(adapter);

Ce qui nous donne, au final, pour définir le contenu de nous nos spinners :

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);</p>

<p>    Spinner language_from = (Spinner) findViewById(R.id.languages_from)
    Spinner language_to = (Spinner) findViewById(R.id.languages_to)</p>

<p>    ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_spinner_item);
    adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);</p>

<p>    for (Language l : Language.values()) {
        adapter.add(l.name());
    }</p>

<p>    languages_from.setAdapter(adapter);
    languages_to.setAdapter(adapter);
}

Pas trop perdu ? :)

Le projet entier de cette application est disponible de manière open source sur github.com/dmathieu/translator

Evènements : clic sur le bouton

Notre application commence à ressembler à quelque chose. Mais n'est pas encore utilisable. Pour cela, il nous faudrait en effet encore pouvoir traduire la phrase entrée dans le champs de texte. Allons y !

Nous désirons effectuer cette traduction lorsque l'utilisateur clique sur le bouton "Traduire !". Pour cela nous devons ajouter un listener de clic sur ce bouton. Une fonction qui sera exécutée à chaque clic.

A la suite du code précédent, plaçons notre listener :

findViewById(R.id.translate_button).setOnClickListener(new View.OnClickListener() {
    public void onClick(View v) {
        String text = ((EditText) findViewById(R.id.text_to_translate)).getText().toString();</p>

<p>        try {
            Language lang_from = Translator.getLanguageString((Spinner) findViewById(R.id.languages_from));
            Language lang_to = Translator.getLanguageString((Spinner) findViewById(R.id.languages_to));
            
            String translatedText = Translator.translate(text, lang_from, lang_to);
            ((TextView) findViewById(R.id.translated_text)).setText(translatedText);
        } catch(Exception e) {
            ((TextView) findViewById(R.id.translated_text)).setText(e.toString());
        }
    }
});

Que faisons nous ici ?

findViewById(R.id.translate_button)
Nous récupérons l'élément bouton. Puis nous appelons, sur cet élément, la méthode setOnClickListener. Cette méthode prends un élément de type View.OnClickListener. Cet élément, qui est une fonction, sera exécuté lors du clic sur le bouton.

String text = ((EditText) findViewById(R.id.text_to_translate)).getText().toString();
Ici, nous récupérons le texte entré et à traduire.

try {
    ****
} catch(Exception e) {
    ((TextView) findViewById(R.id.translated_text)).setText(e.toString());
}

Si, pour une raison quelconque, une erreur est survenue lors de la traduction de l'application, nous désirons pouvoir afficher cette erreur tout de même. Ainsi, si la connexion internet n'est pas disponible, il n'est pas possible de traduire la chaine. Une exception serait alors soulevée. Avec le try/catch, nous interceptons cette exception et affichons son contenu dans le champs de texte. Si nous n'avions pas ce try/catch, l'application se fermerait avec un message incompréhensible à chaque erreur.

Language lang_from = Translator.getLanguageString((Spinner) findViewById(R.id.languages_from));
Language lang_to = Translator.getLanguageString((Spinner) findViewById(R.id.languages_to));

Nous récupérons ici les deux langues sélectionnées en entrée et en sortie dans les deux spinners. La méthode Translator.getLanguageString doit être définie dans src/com/android/translator. Elle contient le code suivant :

public static Language getLanguageString(Spinner spinner) throws Exception {
    String lang = spinner.getSelectedItem().toString();</p>

<p>    for (Language l : Language.values()) {
        if (l.name() == lang) {
            return l;
        }
    }
    throw new Exception("Unknown language provided : " + lang);
}

Avec la méthode getSelectedItem(), nous récupérons la langue sélectionnée dans le spinner. Cependant la librairie Google Translate requiert que nous lui transmettions un objet de type Language. Pas une string comme retournée par le spinner. Nous parcourons donc toutes les langues disponibles jusqu'à trouver la bonne et la retourner. Si aucune langue n'est la bonne, nous soulevons une exception (qui sera alors prise en charge par le try/catch vu plus haut).

Continuons dans notre action sur le clic sur le bouton.

String translatedText = Translator.translate(text, lang_from, lang_to);
Ici, nous faisons appel à la librairie Google Translate afin de récupérer la chaine de caractères traduite.

((TextView) findViewById(R.id.translated_text)).setText(translatedText);
Et enfin ! Nous plaçons cette chaine de caracteres traduite dans l'élément de texte prévu à cet effet.

Conclusion

Désolé pour le (trop) long article ! Notre application est maintenant développée. Il ne nous reste maintenant plus qu'à l'installer sur le SDK afin de la tester, puis sur notre téléphone afin de l'utiliser. Pour tester et installer votre application sur le SDK et un vrai téléphone, vous pouvez référer à la suite de cet article : Déployer une application Android.