Créer un client XMPP Android

| Florent Garin | Ricky81 |

L'objectif de ce tutoriel est de réaliser un petit client Jabber tournant sous Android. Par simplicité, nous allons nous servir des serveurs de Google au travers d'un compte Gmail, néanmoins n'importe quel autre serveur compatible avec le protocole XMPP pourrait être employé.

Commentez ♪

- Titre : Créer un client XMPP Android

- Auteurs : Florent Garin, Ricky81

- Parution : 20 novembre 2009

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

Sommaire

- I. Installation d'Android

- II. Installation du plugin Eclipse

- III. Création du projet

- IV. XMPP, heu c'est quoi ?

- IV-A. Android et XMPP

- IV-B. La bibliothèque tierce Smack

- V. Passons au développement

- VI. Exécution de l'application

- VII. Conclusion

- VIII. Téléchargements

- IX. Liens

- X. Remerciements

I - Installation d'Android

La première étape est bien sûr d'installer notre environnement de développement, un JDK en version 1.5 ou 1.6 est un prérequis de base. Le SDK du système d'exploitation pour mobile Android est disponible à l'adresse suivante : http://developer.android.com/index.html

En novembre 2009, la dernière version était la 2.0 ; les versions se succédant à une vitesse soutenue. Le SDK se présente sous forme d'un simple fichier zip, ou tgz pour les Linuxiens, qu'il suffit de décompresser. Cependant, depuis la version 2.0, des changements sont intervenus : désormais le SDK a été modularisé et ses composants sont téléchargeables individuellement grâce à un gestionnaire de packages qui se lance en exécutant « SDK Setup.exe » (présent à la racine du SDK Android).

Attention cependant, il se peut qu'une erreur se produise lors de la récupération du fichier repository.xml qui référence les composants de la plateforme. Si tel est le cas il faudra modifier son URL pour basculer de l'https à l'http :

Une fois ce problème potentiel réglé, il ne reste plus qu'à sélectionner les éléments à installer :

Cet outil de gestion des composants est en outre accessible directement depuis le plugin Eclipse Android (Android Development Tools). Pour les adeptes d'Eclipse, il ne sera donc pas nécessaire d'exécuter « SDK Setup.exe » pour gérer les éléments du SDK.

II - Installation du plugin Eclipse

Même s'il n'est pas indispensable et que n'importe quel autre éditeur ferait l'affaire, il peut être plus simple dans un premier temps tout au moins de développer avec l'aide du plugin Eclipse.

Pour l'installer, il suffit d'ajouter le site https://dl-ssl.google.com/android/eclipse/ dans la liste des dépôts de l'IDE.

III - Création du projet

Nous voilà prêts à commencer le développement de notre programme Android. La création du projet se fait par l'assistant du plugin Eclipse ADT :

Nous cochons bien « Android 2.0 » comme plateforme cible. Il n'est pas nécessaire de sélectionner « Google APIs ». Ce terme désigne les API développées par Google, par exemple Google Maps, qui ne font pas partie officiellement d'Android ; il n'y a donc aucune garantie que ces bibliothèques soient présentes sur un téléphone même s'il est motorisé par Android.

IV - XMPP, heu c'est quoi ?

XMPP signifie eXtensible Messaging and Presence Protocol. Il s'agit d'un protocole standard ouvert de messagerie instantanée anciennement appelé Jabber. XMPP est au cœur de nombreux serveurs de chat, dont GTalk, le service de Google également accessible depuis Gmail.

Si le protocole est avant tout connu pour ses fonctionnalités d'échange de messages textuels et de notification de présence, les possibilités de XMPP sont bien plus larges que ça et peuvent servir de fondations à un véritable MOM (Message-Oriented Middleware).

IV-A - Android et XMPP

Le SDK Android n'intègre pas d'API XMPP. Il y a pourtant eu quelques tentatives d'inclusion d'une telle API dans les premières versions du kit de développement. Initialement certaines classes mentionnaient explicitement le nom XMPP, puis à l'occasion des mises à jour successives du SDK, l'API a été renommée « GTalk API » pour être aujourd'hui complètement supprimée.

IV-B - La bibliothèque tierce Smack

Android ne nous offrant pas en standard ce qu'il nous faut, nous devons nous tourner vers l'open source, plus précisément vers Smack.

Smack est un projet open source visant à fournir une implémentation Java d'un client XMPP. Ce projet est porté par la société Jive Software qui est très impliquée dans l'évolution de XMPP.

Utiliser Smack dans Android n'est pas si simple que cela ; télécharger le fichier smack.jar et l'intégrer au projet Eclipse ne marche pas ! Eh oui, nous ne devons pas oublier que la machine virtuelle d'Android (Dalvik) n'est pas une JVM standard et seul un sous-ensemble des classes du JDK est implémenté. Quelques petites modifications sont donc nécessaires pour rendre smack compatible avec Dalvik. Un lien vers le jar modifié est disponible à la fin de ce tutoriel.

V - Passons au développement

L'interface graphique de notre activité est relativement basique, elle est décrite dans le fichier de définition de 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">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content" android:text="Destinataire :" />
<EditText android:id="@+id/recipient" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:singleLine="true"
android:autoText="false" android:capitalize="none"
android:scrollHorizontally="true" />
<ListView android:id="@+id/thread" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_weight="1"
android:scrollbars="horizontal" />
<EditText android:id="@+id/message" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:singleLine="true"
android:autoText="false" android:capitalize="none"
android:scrollHorizontally="true" />
<Button android:id="@+id/send" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:text="Envoyer">
</Button>
</LinearLayout>

Le fichier manifeste de notre application veillera bien à réclamer les droits pour accéder à Internet :

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.developpez.florentgarin.android"
android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".ClientJabberActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<uses-sdk android:minSdkVersion="4" />
<uses-permission android:name="android.permission.INTERNET" />
</manifest>

Ensuite, la classe de l'activité ClientJabberActivity se présente comme suit :

package com.developpez.florentgarin.android;

import java.util.ArrayList;
import java.util.List;

import org.jivesoftware.smack.*;
import org.jivesoftware.smack.filter.*;
import org.jivesoftware.smack.packet.*;
import org.jivesoftware.smack.util.StringUtils;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.*;

public class ClientJabberActivity extends Activity {

private final static String SERVER_HOST = "talk.google.com";
private final static int SERVER_PORT = 5222;
private final static String SERVICE_NAME = "gmail.com";
private final static String LOGIN = "xxxxx.xxxxx@gmail.com";
private final static String PASSWORD = "xxxxx";

private List<String> m_discussionThread;
private ArrayAdapter<String> m_discussionThreadAdapter;
private XMPPConnection m_connection;
private Handler m_handler;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
m_handler = new Handler();
try {
initConnection();
} catch (XMPPException e) {
e.printStackTrace();
}

final EditText recipient = (EditText) this.findViewById(R.id.recipient);
final EditText message = (EditText) this.findViewById(R.id.message);
ListView list = (ListView) this.findViewById(R.id.thread);

m_discussionThread = new ArrayList<String>();
m_discussionThreadAdapter = new ArrayAdapter<String>(this,
R.layout.multi_line_list_item, m_discussionThread);
list.setAdapter(m_discussionThreadAdapter);

Button send = (Button) this.findViewById(R.id.send);
send.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
String to = recipient.getText().toString();
String text = message.getText().toString();

Message msg = new Message(to, Message.Type.chat);
msg.setBody(text);
m_connection.sendPacket(msg);
m_discussionThread.add("moi :");
m_discussionThread.add(text);
m_discussionThreadAdapter.notifyDataSetChanged();
}
});
}

private void initConnection() throws XMPPException {
//Initialisation de la connexion
ConnectionConfiguration config =
new ConnectionConfiguration(SERVER_HOST, SERVER_PORT, SERVICE_NAME);
m_connection = new XMPPConnection(config);
m_connection.connect();
m_connection.login(LOGIN, PASSWORD);
Presence presence = new Presence(Presence.Type.available);
m_connection.sendPacket(presence);

//enregistrement de l'écouteur de messages
PacketFilter filter = new MessageTypeFilter(Message.Type.chat);
m_connection.addPacketListener(new PacketListener() {
public void processPacket(Packet packet) {
Message message = (Message) packet;
if (message.getBody() != null) {
String fromName = StringUtils.parseBareAddress(message
.getFrom());
m_discussionThread.add(fromName + ":");
m_discussionThread.add(message.getBody());

m_handler.post(new Runnable() {
public void run() {
m_discussionThreadAdapter.notifyDataSetChanged();
}
});
}
}
}, filter);
}

}

Le fichier « multi_line_list_item.xml » sert à effectuer le rendu des lignes de messages :

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:textStyle="bold"
android:singleLine="false"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>

On peut noter le recours à un objet de type Handler pour s'assurer que l'affichage des messages reçus se fait bien par le thread responsable des modifications d'IHM.

VI - Exécution de l'application

Au premier lancement, nous devons créer l'image AVD (Android Virtual Device). Ce premier lancement est d'ailleurs très long, il faut s'armer de patience. La meilleure stratégie est de ne pas fermer la fenêtre de l'émulateur pour accélérer les exécutions suivantes.

Enfin, la récompense est au bout ! Nous pouvons dialoguer depuis Android avec un utilisateur qui lui converse par exemple depuis Gmail (celui-ci doit déjà avoir été invité) :

VII - Conclusion

Notre application montre que quelques lignes de code suffisent pour envoyer et recevoir des messages XMPP depuis Android. Ce qui est particulièrement intéressant ici c'est la prise en charge intégrale de la tuyauterie d'échanges de messages par le serveur Jabber. On pourrait imaginer développer ainsi un jeu d'échecs ou de bataille navale assez facilement en s'appuyant sur l'infrastructure XMPP décentralisée constituée de la multitude de serveurs privés ou publics implémentant le fameux protocole.

VIII - Téléchargements

- Bibliothèque smack modifiée pour Android : FTP (secours HTTP)

- Projet Eclipse complet : FTP (secours HTTP)

IX - Liens

- Projet smack

- SDK Android

X - Remerciements

Merci à tomlev et ZedroS pour leur relecture orthographique de l'article.