• Aucun résultat trouvé

Développement sous Android

N/A
N/A
Protected

Academic year: 2022

Partager "Développement sous Android"

Copied!
98
0
0

Texte intégral

(1)

Développement sous Android

Chapitres traités

Les fournisseurs de contenu (Content Provider)

Pour cette dernière étude de l'environnement Android, nous allons en profiter pour découvrir plein de fonctionnalités intéressantes comme le partage des données, les services, la communication réseau, la prise en compte des capteurs intégrés, la géolocalisation, le graphisme, le multimédia, etc.

Les fournisseurs de contenu (Content Provider)

Vous pouvez décider d'étendre votre application en proposant des extensions utilisant les mécanismes de partage de données mis à disposition par un service déjà installé sur le téléphone de l'utilisateur. Le partage des données Android via les fournisseurs de contenu est un excellent moyen de diffuser de l'information selon une interface standard d'échange.

L'utilisation des bases de données vous permet de stocker des données complexes et structurées. L'accès à une base de données n'est possible que depuis l'application à partir de laquelle elle a été créée. Si vous souhaitez exposer les données d'une application à d'autres applications, par exemple des photos prises par votre application, Android prévoit un mécanisme plutôt élégant pour parvenir à cette fin : la notion de fournisseur de contenu, sous la forme d'une interface générique permettant d'exposer les données d'une application en lecture et/ou en écriture.

Ce mécanisme permet de faire une scission claire entre votre application et les données exposées. Ainsi, en rendant vos données disponibles au travers d'une telle interface, vous rendez votre application accessible et extensible à d'autres applications en tant que fournisseur de contenu, que ces applications soient créées par vous-même ou des tiers.

Les URIs

Avec Android, toute URI de schéma content:// représente une ressource servie par un fournisseur de contenu. Les fournisseurs de contenu encapsulent les données en utilisant des instances d'URI comme descripteur.

Nous ne savons jamais d'où viennent les données représentées par l'URI et nous n'avons pas besoin de le savoir : la seule chose qui compte est qu'elles soient disponibles lorsque nous en avons besoin.

Ces données pourraient être stockées dans une base de données SQLite ou dans des fichiers plats, voire récupérées à partir d'un terminal ou stockées sur un serveur situé loin d'ici, sur Internet.

A partir d'une URI, vous pouvez réaliser les opérations CRUD de base (Create, Read, Update, Delete) en utilisant un fournisseur de contenu. Les instances d'URI peuvent représenter des collections ou des éléments individuels. Grâce aux premières, vous pouvez créer de nouveaux contenus via des opérations d'insertion. Avec les secondes, vous pouvez lire les données qu'elles représentent, les modifier ou les supprimer.

Composantes d'une URI

Un fournisseur de contenu fonctionne un peu à la manière d'un service web accessible en REST (que nous verrons lors de cette étude) : vous exposez un ensemble d'URI capable de vous retourner différents ensembles d'éléments (tous les éléments, un seul ou un sous-ensemble) en fonction de l'URI et des paramètres.

Quand une requête cible un fournisseur de contenu, c'est le système qui gère l'instanciation de celui-ci. Un développeur n'aura jamais à instancier les objets d'un fournisseur de contenu lui-même.

Le modèle simplifié de construction d'une URI est constitué du schéma, de l'espace de noms des données et, éventuellement, de l'identifiant de l'instance. Ces différents composants sont séparés par des barres de fraction, comme dans une URL.

content://constants/5. Cette URI représente une instance constants d'identifiant 5.

La combinaison du schéma et de l'espace de noms est appelé URI de base d'un fournisseur de contenu ou d'un ensemble de données supporté par un fournisseur de contenu.

Dans l'exemple précédent, content://contants est l'URI de base d'un fournisseur de contenu qui sert des informations sur "contants" (en l'occurence, des constantes physiques).

L'URI de base peut être plus compliquée. Celle des contacts est, par exemple, content://com.android.contacts/contacts, car le fournisseur de contenu des contacts peut fournir d'autres données en utilisant d'autres valeurs pour l'URI de base. L'URI de base représente une collection d'instances. Combinée avec un identifiant d'instance (5, par exemple), elle représente une instance unique.

La plupart des API d'Android s'attendent à ce que les URI soient des objets de type Uri, bien qu'il soit plus naturel de les considérer comme des chaînes. La méthode statique Uri.parse() permet ainsi de créer une instance d'URI à partir de sa représentation textuelle.

D'où viennent ces instances d'URI ?

Le point de départ le plus courant, lorsque nous connaissons le type de données avec lequel nous souhaitons travailler, consiste à obtenir l'URI de base du fournisseur de contenu lui-même. CONTENT_URI, par exemple, est l'URI de base des contacts représentés par des personnes, elle correspond à content://com.android.contacts/contacts.

Si vous avez simplement besoin de la collection, cette URI fonctionne telle quelle ; si vous avez besoin d'une instance dont vous connaissez l'identifiant, vous pouvez l'ajouter à la fin de cette dernière, afin d'obtenir une URI pour cette instance précise.

(2)

connaissez l'identifiant, vous pouvez l'ajouter à la fin de cette dernière, afin d'obtenir une URI pour cette instance précise.

Accéder à un fournisseur de contenu

Pour accéder à un fournisseur de contenu, vous devrez utiliser la classe android.content.ContentResolver. Cette classe est un véritable utilitaire qui sera votre principal point d'accès vers les fournisseurs de contenu Android.

Portez également une attention particulière à l'espace de noms android.provider dans lequel vous trouverez un ensemble de classes facilitant l'accès aux fournisseurs de contenu natifs de la plate-forme Android (appels, contacts, etc.).

Vous pouvez récupérer une instance - unique pour l'application - de la classe ContentResolver eu utilisant la méthode getContentResolver().

ContentResolver fournisseur = getContentResolver();

Chaque fournisseur de contenu expose publiquement une propriété CONTENT_URI, qui stocke l'URI de requête du contenu, comme le montre les fournisseurs de contenu natifs d'Android suivants :

android.provider.CallLog.Calls.CONTENT_URI android.provider.Calendar.CONTENT_URI

android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI

Effectuer une requête

Tout comme les bases de données abordées dans l'étude précédente, les fournisseurs de contenu retournent leurs résultats sous la forme d'un Cursor. Du coup, vous pouvez effectuer les mêmes opérations que lorsque vous manipuliez des bases de données, l'utilisation dans le cadre d'un fournisseur de contenu ne limite en rien ses fonctionnalités.

Une requête s'effectuera en utilisant la méthode query() du ContentResolver en passant les paramètres listés dans le tableau suivant :

1. uri : L'URI du fournisseur de contenu.

2. projection : La projection, soit les colonnes que vous souhaitez voir retournées. Cette valeur peut aussi être null.

3. where : Une clause pour filtrer les éléments retournés. Dans cette chaîne de caractères, l'utilisation du '?' est possible ; chacun de ces caractères sera remplacé par les valeurs du paramètre de sélection (le paramètre suivant). Cette valeur peut aussi être égale à null.

4. selection : Un tableau de sélections qui remplaceront les caractères '?' placés dans la clause where. Ceci permet de rendre dynamiques les requêtes de façon à ne pas avoir à manipuler une chaîne de caractères pour construire la requête mais de le faire via l'ensemble de valeurs d'un tableau rempli dynamiquement à l'exécution. Cette valeur peut aussi être null.

5. order : le nom de la colonne associée à une chaîne de tri ('DESC' ou 'ASC'). Cette valeur peut aussi être égale à null.

// Requêter toutes les données du fournisseur de contenu ContentResolver fournisseur = getContentResolver();

Cursor données = fournisseur.query(MonFournisseur.CONTENT_URI, null, null, null, null);

// Filtrer les données retournées et trier par ordre croissant sur le nom ContentResolver fournisseur = getContentResolver();

String[] projection = new String[] {"nom", "prénom", "âge"};

String filtre = "prénom = Julien";

String ordre = "nom ASC";

Cursor données = fournisseur.query(MonFournisseur.CONTENT_URI, projection, filtre, null, ordre);

1. Une fois le curseur récupéré, vous opérez de la même façon qu'avec une base de données.

2. La partie concernant la création d'un fournisseur de contenu expliquera plus en détail comment fonctionne la paramètre URI d'une requête.

3. Pour restreindre la requête à un élément en particulier, vous pouvez ajouter la valeur de son identifiant (id) correspondant. par exemple, si celui-ci est 10, l'URI sera content://fr.btsiris.db/10.

Il existe plusieurs méthodes d'aide, telles que ContentUris.withAppendedId() et Uri.withAppendPath(), qui vous faciliteront la vie. pour continuer notre exemple, le code correspondant pour construire la requête adéquate permettant de ne cibler qu'un élément en particulier s'écrit ainsi :

Uri requête = Uri.parse("content://fr.btsiris.bd");

Uri requêteParticulière = ContentUris.withAppendedId(requête, 10);

Uri requêteParticulière = Uri.withAppendPath(requête, "10");

Cursor données = managedQuery(requêteParticulière, null, null, null, null);

Les fournisseurs de contenu natifs

Android possède quelques fournisseurs de contenu disponibles nativement permettant d'exposer certaines informations contenues dans les bases de données du système.

1. Contacts : Retrouvez, ajoutez ou modifiez les informations des contacts.

2. Magasin multimédia : Le magasin multimédia stocke l'ensemble des fichiers multimédias de votre appareil. Ajouter ou supprimez les fichiers multimédias depuis ce fournisseur de contenu.

(3)

3. Navigateur : Accédez à l'historique, aux marque-pages ou aux recherches.

4. Appels : Accédez à l'historique des appels entrants, sortants et manqués.

5. Paramètres : Accédez aux paramètres système - sonnerie, etc. - pour lire ou modifier certaines préférences.

Les concepteurs de la plate-forme Android l'ont bien compris, pouvoir accéder librement aux contacts d'un appareil de téléphonie est une aubaine pour bon nombre d'applications. En accordant les permissions adéquates, un développeur peut effectuer une requête, modifier, supprimer ou encore ajouter un contact.

L'exemple suivant permet de lire l'ensemble des contacts stockés dans l'appareil :

fr.btsiris.fournisseur.FournisseurContenu.java

package fr.btsiris.fournisseur;

import android.app.*;

import android.database.Cursor;

import android.net.Uri;

import android.os.Bundle;

import android.provider.ContactsContract;

import android.widget.*;

public class FournisseurContenu extends ListActivity { @Override

public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);

setContentView(R.layout.main);

Uri requête = ContactsContract.Contacts.CONTENT_URI;

String ordre = ContactsContract.Contacts.DISPLAY_NAME +" COLLATE LOCALIZED ASC";

Cursor lignes = getContentResolver().query(requête, null, null, null, ordre);

startManagingCursor(lignes);

ListAdapter liste = new SimpleCursorAdapter(this, android.R.layout.two_line_list_item, lignes, new String[] {ContactsContract.Data.DISPLAY_NAME}, new int[] {android.R.id.text1});

setListAdapter(liste);

} }

res/layout/main.xml

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

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

android:id="@android:id/list"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

/>

La plupart des fournisseurs de contenu ne donnent accès à leurs contenus qu'avec des permissions. Par conséquent, n'oubliez pas d'ajouter la permission adéquate à l'application lors de la lecture de ce fournisseur de contenu, comme dans l'exemple ci-dessous :

AndroidManifest.xml

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

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

package="fr.btsiris.fournisseur"

android:versionCode="1"

android:versionName="1.0">

<application android:label="@string/app_name" >

<activity android:name="FournisseurContenu" android:label="@string/app_name">

(4)

<intent-filter>

<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />

</intent-filter>

</activity>

</application>

<uses-permission android:name="android.permission.READ_CONTACTS" />

</manifest>

Ajouter, supprimer ou mettre à jour des données via le fournisseur de contenu

Les fournisseurs ne sont pas seulement des points d'exposition pour permettre aux applications d'effectuer des requêtes sur les bases de données d'autres applications. Ils permettent aussi de manipuler les données exposées en termes d'ajout, de modification et de suppression.

Toutes ces actions s'effectuent toujours à l'aide de la classe ContentResolver.

1. Ajouter des données : Pour insérer des données, la classe ContentResolver propose deux méthodes : insert() et bulkInsert(). La première permet d'insérer une seule donnée et la seconde d'insérer un jeu de données en un seul appel.

// Créer un objet contenant les valeurs de la donnée à ajouter ContentResolver fournisseur = getContentResolver();

ContentValues valeur = new ContentValues();

valeur.put("nom", "REMY");

valeur.put("prénom", "Emmanuel");

Uri nouvelleValeur = fournisseur.insert(MonFournisseur.CONTENT_URI, valeur);

// Insérer des données en masse dans un fournisseur de contenu ContentResolver fournisseur = getContentResolver();

ContentValues[ ] valeurs = new ContentValues[5];

valeurs[0] = new ContentValues();

valeurs[0].put("nom", "REMY");

valeurs[0].put("prénom", "Emmanuel");

valeurs[1] = new ContentValues();

valeurs[1].put("nom", "ALBIN");

valeurs[1].put("prénom", "Michel");

...

int nombreValeurs = fournisseur.bulkInsert(MonFournisseur.CONTENT_URI, valeurs);

2. Mettre à jour les données : Vous avez trouvé les éléments qui vous intéressent depuis le fournisseur de contenu et vous souhaitez les mettre à jour ? Pour cela, la classe ContentResolver met à votre disposition la méthode update(). Celle-ci prend en paramètres une valeur ContentValues spécifiant les valeurs de chaque colonne de l'entrée ainsi qu'un paramètre de filtre pour spécifier quel ensemble de données est concernée par cette mise à jour. Tous les éléments répondant à la condition du filtre sont mis à jour et la valeur que retourne la méthode update() est le nombre d'éléments ayant été mis à jour avec succès.

// Mise à jour des éléments du fournisseur de contenu ContentResolver fournisseur = getContentResolver();

ContentValues valeur = new ContentValues();

valeur.put("nom", "REMY");

valeur.put("prénom", "Emmanuel");

String filtre = "nom = REMY" ;

int nombreValeurs = fournisseur.update(MonFournisseur.CONTENT_URI, valeur, filtre, null);

3. Supprimer des données : Tout comme le curseur de base de données, vous avez la possibilité de supprimer des éléments directement depuis le fournisseur de contenu avec la méthode delete(). Vous pouvez supprimer une seule valeur, celle ciblée par l'URI, ou plusieurs valeurs en spécifiant un paramètre de filtre.

// Suppression de l'élément possédant un id de 10 ContentResolver fournisseur = getContentResolver();

Uri requête = Uri.parse("content://fr.btsiris.bd");

Uri recherche = ContentUris.withAppendedId(requête, 10);

fournisseur.delete(recherche, null, null);

// Suppression de tous les éléments du fournisseurs de contenu dont le prénom est 'Julien' String filtre = "prénom = Julien" ;

fournisseur.delete(requête, filtre, null);

Créer un fournisseur de contenu

La création d'un fournisseur de contenu est d'une importance capitale lorsqu'une application souhaite mettre ses données à disposition d'autres applications. Si elle ne les garde que pour elle-même, vous pouvez éviter la création d'un fournisseur de contenu en vous contentant d'accéder directement aux données depuis vos activités.

Un fournisseur de contenu est une classe Java, tout comme une activité est une intention, la principale étape de la création d'un fournisseur consiste à produire sa classe Java, qui doit hériter de ContentProvider.

Cette sous-classe doit implémenter six méthodes qui, ensemble, assurent le service qu'un fournisseur de contenu est censé offrir aux activités qui veulent créer, lire, modifier ou supprimer du contenu.

La création d'un fournisseur de contenu s'effectue en dérivant une classe de ContentProvider et en redéfinissant les méthodes du tableau ci- dessous en fonction de vos besoin :

(5)

1. onCreate() : Obligatoire. Appelée lors de la création du fournisseur de contenu. Renvoie un booléen pour spécifier que le fournisseur de contenu a été chargé avec succès.

2. query() : Requête sur le fournisseur de contenu. Retourne un curseur permettant au programme local l'ayant appelé de naviguer au sein des résultats retournés.

3. delete() : Suppression d'une ou plusieurs lignes de données. L'entier retourné représente le nombre de lignes supprimées.

4. insert() : Appelée lorsqu'une insertion de données doit être réalisée sur le fournisseur de contenu. L'URI renvoyée correspond à la ligne nouvellement insérée.

5. update() : Mise à jour d'une URI. Toutes les lignes correspondant à la sélection seront mises à jour avec les valeurs fournies dans l'objet de type ContentValues des paramètres.

6. getType() : Retourne le type de contenu MIME à partir de l'URI spécifiée en paramètre.

La méthode query() retourne un objet Cursor vous permettant de manipuler et de naviguer dans les données de la même façon qu'avec un curseur de base de données SQLite (Cursor n'est qu'une interface). Par conséquent, vous pouvez utiliser n'importe quel curseur disponible dans le SDK Android : SQLiteCursor (base de données SQLite), MatrixCursor (pour les données non stockées dans une base de données), MergeCursor, etc.

1. De façon à identifier sans ambiguïté et pouvoir manipuler un fournisseur de conetnu, vous devez spécifier une URI unique, en général basé sur l'espace de nom de la classe d'implémentation du fournisseur. De façon à être accessible par tous les développeurs et applications, l'URI doit être exposé sous la variable publique statique nommée CONTENT_URI : "content://fr.btsiris.bd/personnes".

2. Avec cette URI, vous demanderez au fournisseur de contenu que nous allons élaborer de renvoyer toutes les personnes enregistrées actuellement dans la base de données. Si vous complétez la requête en la prefixant avec un numéro, vous demanderez uniquement la personne souhaitée à l'aide de son identifiant : "content://fr.btsiris.bd/personnes/10".

3. Le SDK d'Android possède une classe UriMatcher permettant de faciliter la gestion des URI. En utilisant et configurant celle-ci, vous n'aurez pas à scinder les URI vous-même pour essayer d'en extraire le contenu, la classe UriMatcher le fera pour vous.

Ce chapitre n'est pas erminé et sera traité dans un avenir proche.

.

Création d'un service

Les services d'Android sont destinés aux processus de longue haleine, qui peuvent devoir continuer de s'exécuter même lorsqu'ils sont séparés de toute activité. A titre d'exemple, citons la musique, qui continue d'être jouée même lorsque l'activité de "lecture" a été supprimée, la récupération des mises à jour des flux RSS sur Internet et la persistance d'une session de messagerie instantanée, même lorsque le client a perdu le focus à cause de la réception d'un appel téléphonique.

Un service est créé lorsqu'il est lancé manuellement (via un appel à l'API) ou quand une activité tente de s'y connecter via une communication interprocessus (IPC). Il perdure jusqu'à ce qu'il ne soit plus nécessaire et que le système ait besoin de récupérer de la mémoire. Cette longue exécution ayant un coût, les services doivent prendre garde à ne pas consommer trop de CPU sous peine d'user prématurément la batterie du terminal.

A la différence d'autres plates-formes du marché, Android offre un environnement pour ces applications sans interface utilisateur. Ainsi, les services sont des applications dérivant de la classe android.app.Service, qui s'exécutent en arrière-plan et qui sont capables de signaler à l'utilisateur qu'un événement ou une information nouvelle requiert son attention.

1. Contrairement aux activités qui offrent une interface utilisateur, les services en sont complètement dépourvus. Ils peuvent cependant réagir à des événements, mettre à jour des fournisseurs de contenu, effectuer n'importe quel autre traitement..., et enfin notifier leur état à l'utilisateur via des toasts et des notifications.

2. Ces services, complètement invisibles pour l'utilisateur, possèdent un cycle de vie similaire à une activité et peuvent être contrôlés depuis d'autres applications (activité, service et Broadcast Receiver). Il est ainsi commun de réaliser une application composée d'activités et d'un service ; un lecteur de musique utilisera une interface de sélection des musiques alors que le service jouera celles-ci en arrière plan.

3. Les applications qui s'exécutent fréquemment ou continuellement sans nécessiter constamment une interaction avec l'utilisateur peuvent être implémentées sous la forme d'un service. Les applications qui récupèrent des informations (informations, météo, résultats sportifs, etc.) via des flux RSS/Atom représentent de bons exemples de services.

Création d'un service

Les services sont conçus pour s'exécuter en arrière plan et nécessitent de pouvoir être démarrés, contrôlés et stoppés par d'autres applications. Les services partagent un certain nombre de comportements avec les activités. C'est pourquoi vous retrouverez des méthodes similaires à celles abordées précédemment pour les activités. Pour créer un service, dérivez votre classe de android.app.Service puis, à l'instar d'une activité, redéfinissez les méthodes du cycle de vie avant de déclarer le service dans le fichier de configuration de l'application.

Les services peuvent redéfinir trois méthodes : onCreate(), onStart() et onDestroy(). Les méthodes onCreate() et onDestroy() sont respectivement appelées lors de la création et de la destruction du service, à l'intar d'une activité.

La méthode onBind() est la seule méthode que vous devez obligatoirement implémenter dans un service. Cette méthode retourne un objet IBender qui permet au mécanisme IPC - communication interprocessus permettant d'appeler des méthodes distantes - de fonctionner.

Une fois votre service créé, il vous faudra, comme pour les activités d'une application, déclarer celui-ci dans le fichier de configuration de l'application au moyen de la balise <service android:name=".MonService" />. Voici les paramètres supplémentaires que vous pouvez spécifier sous forme d'attributs dans l'élément <service> :

(6)

1. process : Spécifie un nom de processus dans lequel le code sera exécuté.

2. enabled : Indique si le service est actif ou non (peut-être instancié par le système).

3. exported : Booléen indiquant si le service est utilisable par d'autres applications.

4. permission : Spécifie une permission nécessaire pour exécuter ce service.

En plus des attributions dans l'élément <service>, vous pouvez utiliser l'élément <meta-data> pour spécifier des données supplémentaires et une section <intent-filter> à l'instar des activités.

Démarrer et arrêter un service

Les services peuvent être démarrés manuellement (via l'appel de la méthode startService() ou via un objet Intent) ou quand une activité essaie de se connecter au service via une communication interprocessus (IPC).

1. Pour démarrer un service manuellement, utilisez la méthode startService(), laquelle accepte en paramètre un objet Intent. Ce dernier permet de démarrer un service en spécifiant son type de classe ou une action spécifiée par le filtre d'intention du service.

// Démarre le service explicitement

startService(new Intent(this, MonService.class));

// Démarre le service en utilisant une action (enregistré dans Intent Filter).

startService(new Intent(MonService.MON_ACTION_SPECIALE));

Le moyen le plus simple de démarrer un service est de spécifier le nom de sa classe en utilisant le paramètre class. Cette option ne fonctionne que si le service est un composant de votre application et non un service tiers. Si le service à démarrer n'a pas été créé par vous, la seule alternative sera d'utiliser l'Intent associé à ce service.

2. La méthode startService() peut être appelée plusieurs fois, ce qui aura pour conséquence d'appeler plusieurs fois la méthode onStart() du service. Sachez donc gérer cette situation, de façon à ne pas allouer de ressources inutiles ou réaliser des traitements, qui rendraient caduque la bonne exécution de votre service.

3. Pour arrêter un service, utilisez la méthode stopService() avec l'action que vous avez utilisée lors de l'appel à la méthode startService() :

stopService(new Intent(this, monService.getClass()));

Contrôler un service depuis une activité

L'exécution d'un service requiert le plus souvent d'avoir pu configuré ce dernier, souvent à l'aide d'une interface que l'utilisateur pourra utiliser pour spécifier ou changer les paramètres. Afin de pouvoir communiquer avec un service, vous avez plusieurs possibilités à votre disposition.

1. La première consiste à exploiter le plus possible le mécanisme des intentions au sein du service.

2. La seconde (dans le cas de deux applications distinctes) consiste à effectuer un couplage fort entre les processus de l'application appelante et le processus du service en utilisant le langage AIDL (Android Interface Definition Language), qui permet la définition de l'interface du service au niveau du système - ce thème ne sera pas abordé dans cette étude.

3. Il existe aussi une autre alternative qui combine ces deux possibilités en contrôlant un service à l'aide d'une activité et en les liant, à condition que le service et l'activité fassent partie de la même application. C'est cette dernière façon que nous allons détailler ci-après.

Lorsqu'une activité est liée à un service, celle-ci possède une référence permanente vers le service, qui vous permet de communiquer avec le service comme si ce dernier n'était qu'une simple référence à un objet de votre activité.

Pour permettre à une activité de s'associer à un service, la méthode onBind() doit être implémentée au niveau du service. Cette méthode retourne un objet qui sera utilisé plus tard par l'activité pour récupérer la référence vers le service. La méthode onBind() doit renvoyer un type IBinder propre à votre service, ce qui signifie que vous devez créer votre propre implémentation de l'interface IBinder :

public class ServiceConversion extends Service { ...

@Override

public IBinder onBind(Intent intention) { return new ConversionBinder(); }

public class ConversionBinder extends Binder {

ServiceConversion getService() { return ServiceConversion.this; } }

}

Une fois la méthode onBind() implémentée au niveau de votre service, vous devez maintenant connecter l'activité au service. Pour ce faire, vous devez utiliser une connexion de type ServiceConnection : ce sont les méthodes de cette dernière qui seront appelées par l'activité pour se connecter ou se déconnecter du service.

Créez la connexion au niveau de l'activité, en redéfinissant les méthodes onServiceConnected() et onServiceDisconnected() de la classe ServiceConnection. La première méthode sera appelée lors de la connexion de l'activité au service et la seconde lors de la déconnexion.

public class Conversion extends Activity { private ServiceConversion service;

private ServiceConnection connexion = new ServiceConnection() { public void onServiceConnected(ComponentName nom, IBinder binder) { service = ((ServiceConversion.ConversionBinder)binder).getService();

(7)

service = ((ServiceConversion.ConversionBinder)binder).getService();

}

public void onServiceDisconnected(ComponentName nom) { service = null;

} };

}

Une fois que la méthode onBind() du service est implémentée et qu'un objet ServiceConnection est prêt à faire office de médiateur, l'activité doit encore demander de façon explicite l'association au service, à l'aide de la méthode bindService(). Cette méthode prend trois paramètres :

1. L'intention du service que vous souhaitez invoquer, souvent de façon explicite directement en utilisant la classe du service ; 2. Une instance de la connexion créée vers le service ;

3. Un drapeau spécifiant les options d'association, souvent 0 ou la constante BIND_AUTO_CREATE. Une autre valeur BIND_DEBUG_UNBIND permet d'afficher des informations de débogage. Vous pouvez combiner les deux valeurs à l'aide de l'opérateur binaire &.

public class Conversion extends Activity { @Override

public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);

Intent soumettre = new Intent(this, ServiceConversion.class);

bindService(soumettre, connexion, Context.BIND_AUTO_CREATE);

} }

Une fois l'association effectuée à l'aide de la méthode bindService(), la méthode onServiceConnected() sera appelée et la référence vers le service associé sera récupérée. Vous pourrez ainsi appeler toutes les méthodes et propriétés publiques du service au travers de cette référence comme s'il s'agissait d'un simple objet de l'activité, et de cette façon contrôler le service au travers de l'interface utilisateur proposée par l'activité. Pour vous déconnecter, utilisez la méthode unbindService() de l'instance de l'activité.

A titre d'exemple, nous allons modifier un projet que nous avons déjà mis en oeuvre lors de l'étude précédente. Il s'agit de la conversion monétaire. Bien que cela ne soit pas très utile, je vous propose, comme un cas d'école, d'intégrer à ce projet un service qui s'occupera de la conversion elle-même.

Au niveau du visuel, nous ne verrons aucune modification apparente si ce n'est la présence d'affichage de messages qui nous montrent le démarrage et l'arrêt du service.

AndroidManifest.xml

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

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

package="fr.btsiris.conversion"

android:versionCode="1"

android:versionName="1.0">

<application android:label="Service en action" >

<activity android:name="Conversion" android:label="Conversion">

<intent-filter>

<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />

</intent-filter>

</activity>

<service android:name="ServiceConversion" />

</application>

</manifest>

res/layout/main.xml

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

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

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:stretchColumns="1">

<TableRow>

<fr.btsiris.conversion.Monnaie android:id="@+id/euro"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:layout_span="3"/>

<Button

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:text="Franc"

android:onClick="calculFranc" />

</TableRow>

<TableRow>

<fr.btsiris.conversion.Monnaie android:id="@+id/franc"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

(8)

android:layout_span="3"/>

<Button

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:text="€uro"

android:onClick="calculEuro" />

</TableRow>

</TableLayout>

fr.btsiris.conversion.ServiceConversion.java

package fr.btsiris.conversion;

import android.app.*;

import android.content.Intent;

import android.os.*;

import android.widget.Toast;

public class ServiceConversion extends Service { private final double TAUX = 6.55957;

@Override

public void onCreate() { super.onCreate();

Toast.makeText(this, "Service en action", Toast.LENGTH_LONG).show();

}

@Override

public void onDestroy() { super.onDestroy();

Toast.makeText(this, "Service désactivé", Toast.LENGTH_LONG).show();

}

public double euroFranc(double euro) { return euro * TAUX;

}

public double francEuro(double franc) { return franc / TAUX;

}

@Override

public IBinder onBind(Intent intention) { return new ConversionBinder(); }

public class ConversionBinder extends Binder {

ServiceConversion getService() { return ServiceConversion.this; } }

}

Dans ce cas de figure, il n'est absolument pas nécessaire de redéfinir les méthodes de rappel onStart() et onDestroy(). Je les ai placés juste pour vérifier le moment de l'activation et de la désactivation du service, comme un système de débogage.

fr.btsiris.conversion.Monnaie.java

package fr.btsiris.conversion;

import android.content.Context;

import android.text.InputType;

import android.util.AttributeSet;

import android.view.Gravity;

import android.widget.EditText;

import java.text.*;

public class Monnaie extends EditText { private NumberFormat formatMonnaie;

public Monnaie(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle);

init();

}

public Monnaie(Context context, AttributeSet attrs) { super(context, attrs);

init();

}

public Monnaie(Context context) { super(context);

init();

}

private void init() { setSymbole('€');

setValeur(0.0);

setGravity(Gravity.RIGHT);

(9)

setInputType(InputType.TYPE_CLASS_NUMBER);

}

public void setSymbole(char symbole) {

formatMonnaie = new DecimalFormat("#,##0.00 "+symbole);

}

public double getValeur() { try {

Number valeur = (Number) formatMonnaie.parse(getText().toString());

setText(formatMonnaie.format(valeur));

return valeur.doubleValue();

}

catch (ParseException ex) { return 0.0; } }

public void setValeur(double valeur) { setText(formatMonnaie.format(valeur));

} }

fr.btsiris.conversion.Conversion.java

package fr.btsiris.conversion;

import android.app.Activity;

import android.content.*;

import android.os.*;

import android.view.View;

public class Conversion extends Activity { private Monnaie euro, franc;

private ServiceConversion service;

private ServiceConnection connexion = new ServiceConnection() { public void onServiceConnected(ComponentName nom, IBinder binder) { service = ((ServiceConversion.ConversionBinder)binder).getService();

}

public void onServiceDisconnected(ComponentName nom) { service = null;

} };

@Override

public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);

setContentView(R.layout.main);

euro = (Monnaie) findViewById(R.id.euro);

franc = (Monnaie) findViewById(R.id.franc);

franc.setSymbole('F');

franc.setValeur(0.0);

Intent soumettre = new Intent(this, ServiceConversion.class);

bindService(soumettre, connexion, Context.BIND_AUTO_CREATE);

}

@Override

protected void onDestroy() { super.onDestroy();

unbindService(connexion);

}

public void calculFranc(View vue) {

franc.setValeur(service.euroFranc(euro.getValeur()));

}

public void calculEuro(View vue) {

euro.setValeur(service.francEuro(franc.getValeur()));

} }

Géolocalisation, Google Maps et GPS

L'une des caractéristiques essentielles des téléphones mobiles est leur portabilité, et il n'est donc pas surprenant que quelques-unes des fonctionnalités les plus séduisantes d'Android soient les services permettant de déterminer, de contextualiser et de cartographier des positions géographiques.

Vous pouvez créer des activités fondées sur des cartes utilisant Google Maps comme un élément d'interface utilisateur. Vous aurez un accès complet aux cartes, ce qui vous permettra de contrôler les paramètres d'affichage, de changer le niveau de zoom et de faire un panoramique.

Vous pourrez annoter les cartes et gérer la saisie de l'utilisateur pour produire des informations et des fonctionnalités contextuelles.

(10)

Nous couvrirons également dans ce chapitre les services de géolocalisation, qui permettent de déterminer la position courante de l'appareil. Ils incluent le GPS et la technologie Google de localisation cellulaire. Vous pourrez spécifier quelle technologie utiliser en la nommant explicitement ou implicitement, en définissant un ensemble de critères en termes de précision, coût ou autres.

Les cartes et les services de géolocalisation utilisent la latitude et la longitude pour identifier les positions géographiques, mais la plupart des utilisateurs raisonnent plutôt en terme d'adresses. Android fournit un Geocoder qui supporte les géocodages avant et inverse. Grâce à lui, vous pourrez convertir latitudes et longitudes en adresses et inversement.

Utilisés conjointement, la cartographie, le géocodage et les services de géolocalisation fournissent une puissante boîte à outils pour incorporer la mobilité native de votre téléphone à vos applications. Les services de géolocalisation d'Android sont donc divisés en deux grandes parties :

1. Les API qui gèrent les plans (dans l'espace de noms com.google.android.maps) ;

2. Les API qui gèrent la localisation à proprement parler (dans l'espace de noms android.location).

Nous allons d'abord voir comment déterminer la position d'un appareil sur Android. Ensuite, nous verrons en deux temps comment utiliser l'émulateur pour simuler des positions en étudiant d'abord la génération des fichiers de points et ensuite leur emploi. La suite du chapitre sera consacrée aux capacités de la plate-forme et notamment à l'affichage de cartes Google Maps ainsi qu'aux différents dessins ou affichage réalisables grâce à ces mêmes cartes.

Utiliser les services de géolocalisation

Les services de géolocalisation sont un terme générique désignant les différentes technologies utilisées pour déterminer la position courante d'un appareil. Les deux principaux éléments sont les suivants :

1. Location Manager : Fournit des points d'entrée vers les services de géolocalisation.

2. Location Providers : (fournisseurs de position) : Chacun d'eux représente une technologie de localisation de la position de l'appareil.

A l'aide du Location Manager vous pouvez :

1. Obtenir une position courante.

2. Suivre des déplacements.

3. Déclencher des alertes de proximité en détectant les mouvements dans une zone spécifique.

4. Trouver les Location Providers disponibles.

Lorsqu'il s'agit de déterminer la position courante de l'appareil, plusieurs problèmes peuvent être rencontrés :

1. Tous les appareils ne disposent pas tous du même matériel de géolocalisation (certains n'ont pas de récepteur GPS) ;

2. Les conditions d'utilisation du téléphone peuvent rendre inutilisable une méthode de localisation (cas de fonctionnalités GPS dans un tunnel, par exemple).

Sélectionner un fournisseur de position - Location Provider En fonction de l'appareil, Android peut utiliser plusieurs technologies pour déterminer la position courante. Chacune d'elles, appelée Location Providers, offrira diverses caractéristiques en matière de consommation d'énergie, de coût, de précision et de capacité à déterminer l'altitude, la vitesse ou le cap.

C'est pourquoi Android offre plusieurs moyens de localisation au travers d'une liste de fournisseurs de positions : selon les conditions du moment ou vos propres critères, Android se chargera de sélectionner le plus apte à donner la position de l'appareil.

Il faudra donc récupérer cette liste de fournisseurs et déterminer celui qui est le plus adapté à l'utilisation souhaitée. De manière générale, nous distinguons deux types de fournisseurs naturels :

1. Le fournisseur basé sur la technologie GPS, de type LocationManager.GPS_PROVIDER. C'est le plus précis des deux, mais c'est également le plus consommateur en terme de batterie.

2. Le fournisseur qui se repère grâce aux antennes des opérateurs mobiles et aux points d'accès WI-FI, de type LocationManager.NETWORK_PROVIDER.

// Ainsi, pour obtenir une instance d'un provider spécifique, appelez la méthode getProvider() en lui passant son nom : String fournisseur = LocationManager.GPS_PROVIDER;

LocationProvider gpsProvider = sysLocalisation.getProvider(fournisseur);

Ceci n'est en général utile que pour déterminer les capacités d'un provider particulier. La plupart des méthodes d'un Location Manager ne requièrent que le nom du provider pour exécuter les services de géolocalisation.

Obtenir la liste des fournisseurs de position Le but des services de géolocalisation est de déterminer la position de l'appareil. L'accès à ces services est géré par le système au travers de la méthode getSystemService(). La classe des fournisseurs de position, LocationProvider, est une classe abstraite : chacune des différentes implémentations (correspondant aux différents moyens de localisation à notre disposition) fournit l'accès aux informations de localisation d'une manière qui lui est propre.

// Le code suivant permet d'obtenir la liste de tous les fournisseurs :

LocationManager sysLocalisation = (LocationManager) getSystemService(Context.LOCATION_SERVICE);

(11)

List<String> fournisseurs = sysLocalisation.getAllProviders();

A un instant donné, tous les outils de géolocalisation de l'appareil ne sont peut-être pas disponibles : l'utilisateur peut en effet décider de désactiver certains moyens (par exemple, le GPS peut être désactivé pour économiser les batteries).

Une autre alternative permet ainsi d'obtenir la liste de tous les providers disponibles sur l'appareil en appelant cette fois-ci la méthode getProviders() en spécifiant un booléen pour indiquer si vous désirez toute la liste des fournisseurs ou uniquement ceux qui sont activés :

// Le code suivant permet d'obtenir la liste de tous les fournisseurs activés :

LocationManager sysLocalisation = (LocationManager) getSystemService(Context.LOCATION_SERVICE);

boolean actifsSeulement = true;

List<String> fournisseurs = sysLocalisation.getProviders(actifsSeulement);

Comme nous l'avons vu plus haut, si vous souhaitez obtenir un fournisseur particulier, appelez plutôt la méthode getProvider() en spécifiant le type de fournisseur à l'aide des constantes prédéfinies LocationManager.GPS_PROVIDER ou LocationManager.NETWORK_PROVIDER :

LocationManager sysLocalisation = (LocationManager) getSystemService(Context.LOCATION_SERVICE);

String fournisseur = LocationManager.GPS_PROVIDER;

LocationProvider gpsProvider = sysLocalisation.getProvider(fournisseur);

Comme la plupart des fonctionnalités sous Android, pour utiliser ces quelques lignes de code, il ne faut pas oublier de déclarer les permissions appropriées dans la section <manifest> du fichier de configuration de l'application.

<manifest xmlns:android="http://schemas.android.com/apk/res/android" ...>

<application android:label="@string/app_name" >

....

</application>

// L'utilisation du fournisseur de type GPS est lié à la déclaration de permission suivante : <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

// L'utilisation du fournisseur réseau dépend quant à lui de la permission :

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

</manifest>

Nous retrouvons ici la notion de localisation grossière ou fine suivant la méthode utilisée par l'implémentation.

.

Je rappelle encore une fois que le but des services de géolocalisation est de déterminer la position de l'appareil. Une seule ligne de code suffit pour cela. Il faut cependant choisir auparavant un fournisseur de position. Une fois cette étape réalisée, nous utilisons la méthode getLastKnownLocation() issue de la classe LocationManager, avec comme paramètre le fournisseur de position choisi. Le retour de cette méthode est une instance d'un objet Location si une position a été calculée, sinon son résultat vaut null :

LocationManager sysLocalisation = (LocationManager) getSystemService(Context.LOCATION_SERVICE);

String fournisseur = LocationManager.GPS_PROVIDER;

Location localisation = sysLocalisation.getLastKnownLocation(fournisseur);

L'objet de type Location renvoyé par la méthode getLastKnownLocation() inclut toutes les informations de position disponibles données par le fournisseur : latitude, longitude, cap, altitude, vitesse et heure à laquelle la position a été déterminée. Toutes ces propriétés sont accessibles via les méthodes getXxx() de l'objet Location. Dans certaines instances, des détails additionnels sont fournis dans le Bundle des compléments.

Choix d'un fournisseur de position en fonctions de critères Il est peu probable dans la plupart des scenarii que vous souhaitiez explicitement choisir le fournisseur de position à utiliser. Plus communément, vous spécifierez les exigences qu'un fournisseur devra respecter et laisserez Android déterminer la meilleure technologie à utiliser.

Une méthode existe dans Android pour choisir un fournisseur à partir de besoins que vous formulez, ce qui permet de déterminer le fournisseur le plus adapté à vos attentes.

La classe centrale pour la définition des critères se nomme Criteria. Grâce à elle vous pouvez spécifier vos exigences en terme de précision (fine ou approximative), de consommation d'énergie (faible, moyenne, élevée), de coût et de capacité à renvoyer l'altitude, la vittesse et le cap.

// Spécifie des critères de précision approximative, de faible consommation d'énergie et pas d'altitude, cap et vitesse.

Criteria critères = new Criteria();

critères.setAccuracy(Criteria.ACCURACY_COARSE);

critères.setPowerRequirement(Criteria.POWER_LOW);

critères.setAltitudeRequired(false);

critères.setBearingRequired(false);

critères.setSpeedRequired(false);

critères.setCostAllowed(true);

// Spécifie des critères de localisation la plus fine possible avec l'altitude fournie obligatoirement.

Criteria critères = new Criteria();

critères.setAccuracy(Criteria.ACCURACY_FINE);

critères.setAltitudeRequired(true);

Une fois les critères définis, nous utilisons la méthode getBestProvider() de l'instance de la classe LocationManager récupérée un peu plus tôt.

Le booléen en paramètre donne la possibilité de se restreindre aux fournisseurs activés uniquement.

(12)

LocationManager sysLocalisation = (LocationManager) getSystemService(Context.LOCATION_SERVICE);

Criteria critères = new Criteria();

critères.setAccuracy(Criteria.ACCURACY_FINE);

critères.setAltitudeRequired(true);

String fournisseur = sysLocalisation.getBestProvider(critères, true);

Si plus d'un fournisseur de position correspond à vos critères, celui possédant la précision la plus élevée est renvoyé. Si aucun fournisseur ne répond à vos exigences, les critères sont assouplis dans l'ordre suivant jusqu'à ce qu'un fournisseur soit trouvé :

1. Consommation d'énergie.

2. Précision.

3. Capacité à déterminer le cap, la vitesse et l'altitude.

Le critère de coût n'est jamais modifié. Si aucun fournisseur n'est trouvé, la valeur null est renvoyée.

.

Avant d'aller plus loin, je vous propose de prendre tout de suite un premier exemple de géolocalisation qui permet de récupérer tout simplement la latitude et la longitude de votre mobile juste au démarrage de votre mobile.

AndroidManifest.xml

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

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

package="fr.btsiris.localisation"

android:versionCode="1"

android:versionName="1.0">

<application android:label="GPS" >

<activity android:name="Geolocalisation" android:label="Où suis-je ?">

<intent-filter>

<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />

</intent-filter>

</activity>

</application>

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

</manifest>

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">

<TextView

android:id="@+id/latitude"

android:layout_width="fill_parent"

android:layout_height="wrap_content" />

<TextView

android:id="@+id/longitude"

android:layout_width="fill_parent"

android:layout_height="wrap_content" />

</LinearLayout>

fr.btsiris.localisation.Geolocalisation.java

package fr.btsiris.localisation;

import android.app.Activity;

import android.content.Context;

import android.location.*;

import android.os.Bundle;

import android.widget.TextView;

public class Geolocalisation extends Activity { private TextView latitude, longitude;

@Override

public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);

setContentView(R.layout.main);

latitude = (TextView) findViewById(R.id.latitude);

longitude = (TextView) findViewById(R.id.longitude);

LocationManager localisations = (LocationManager) getSystemService(Context.LOCATION_SERVICE);

Location position = localisations.getLastKnownLocation(LocationManager.GPS_PROVIDER);

if (position!=null) {

latitude.setText("Latitude : "+position.getLatitude());

longitude.setText("Longitude : "+position.getLongitude());

}

else latitude.setText("Position non déterminée ");

} }

(13)

Si vous ne possédez pas de capteur GPS dans votre mobile, prévoyez le type de fournisseur LocationManager.NETWORK_PROVIDER.

Je vous propose une autre alternative, qui aboutit au même résultat, qui prend en compte le choix du fournisseur en fonctions de différents critères.

fr.btsiris.localisation.Geolocalisation.java

package fr.btsiris.localisation;

import android.app.Activity;

import android.content.Context;

import android.location.*;

import android.os.Bundle;

import android.widget.TextView;

public class Geolocalisation extends Activity { private TextView latitude, longitude;

@Override

public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);

setContentView(R.layout.main);

latitude = (TextView) findViewById(R.id.latitude);

longitude = (TextView) findViewById(R.id.longitude);

LocationManager localisations = (LocationManager) getSystemService(Context.LOCATION_SERVICE);

Criteria critères = new Criteria();

critères.setAccuracy(Criteria.ACCURACY_COARSE);

critères.setPowerRequirement(Criteria.POWER_LOW);

critères.setAltitudeRequired(false);

critères.setBearingRequired(false);

critères.setSpeedRequired(false);

critères.setCostAllowed(true);

String fournisseur = localisations.getBestProvider(critères, true);

Location position = localisations.getLastKnownLocation(fournisseur);

if (position!=null) {

latitude.setText("Latitude : "+position.getLatitude());

longitude.setText("Longitude : "+position.getLongitude());

}

else latitude.setText("Position non déterminée ");

} }

Suivre les déplacements La plupart des applications de localisation doivent réagir aux déplacements de l'utilisateur. Se contenter de sonder le LocationManager ne suffira pas à forcer l'obtention des nouvelles mises à jour des Location Providers. Détecter le changement de position relève de deux problématiques : recevoir des mises à jour de sa position et détecter le mouvement.

Ces deux problématiques de changement se résolvent par l'utilisation de la même méthode du LocationManager. Il s'agit de la méthode requestLocationUpdates() dont voici les paramètres utiles :

LocationManager sysLocalisation = (LocationManager) getSystemService(Context.LOCATION_SERVICE);

String fournisseur = LocationManager.GPS_PROVIDER;

sysLocalisation.requestLocationUpdates(fournisseur, temps, distance, écouteurLocalisation);

1. Le premier paramètre est le fournisseur de position souhaité.

2. Ensuite, le deuxième paramètre indique le temps entre deux mises à jour exprimé en millisecondes. Attention à ne pas mettre de valeur trop faibles, qui accaparerait les ressources du téléphone et viderait votre batterie. La documentation du SDK recommande une valeur minimale de 60 000 ms.

3. Le troisième paramètre précise la distance correspond au nombre de mètres qui doivent être parcourus avant de recevoir une nouvelle position. Si elle est supérieure à zéro, la mise à jour s'effectuera uniquement une fois la distance parcourue.

4. Enfin, le dernier paramètre est l'écouteur (une implémentation de l'interface LocationListener) qui recevra les diverses notifications. Cet écouteur propose quatre méthodes que vous devez redéfinir suivant les circonstances :

class ChangementPosition implements LocationListener {

public void onLocationChanged(Location position) {

// Met à jour l'application en fonction de la nouvelle position.

}

public void onStatusChanged(String fournisseur, int statut, Bundle extras) { // Met à jour l'application si le status du matériel a changé.

}

public void onProviderEnabled(String fournisseur) { // Met à jour l'application lorsque le fournisseur est activé.

}

public void onProviderDisabled(String fournisseur) {

// Met à jour l'application lorsque le fournisseur est désactivé.

(14)

// Met à jour l'application lorsque le fournisseur est désactivé.

} }

5. Pour arrêter les mises à jour, il faut fournir au gestionnaire de localisation l'instance de l'écouteur à retirer au travers de la méthode removeUpdates().

Les GPS ont pour la plupart une consommation électrique significative. Pour la minimiser, vous devez désactiver les mises à jour à chaque fois que cela est possible, particulièrement lorsqu'elles servent à mettre à jour l'interface utilisateur mais que votre application n'est pas visible. Vous pouvez également améliorer les performances en augmentant le temps minimal entre les mises à jour.

Dans ce deuxième exemple nous prévoyons une mise à jour de la position de votre mobile qui permet de suivre votre déplacement :

fr.btsiris.localisation.Geolocalisation.java

package fr.btsiris.localisation;

import android.app.Activity;

import android.content.Context;

import android.location.*;

import android.os.Bundle;

import android.widget.*;

public class Geolocalisation extends Activity { private TextView latitude, longitude;

private LocationManager localisations; private String fournisseur;

@Override

public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);

setContentView(R.layout.main);

latitude = (TextView) findViewById(R.id.latitude);

longitude = (TextView) findViewById(R.id.longitude);

Criteria critères = new Criteria();

critères.setAccuracy(Criteria.ACCURACY_COARSE);

critères.setPowerRequirement(Criteria.POWER_LOW);

critères.setAltitudeRequired(false);

critères.setBearingRequired(false);

critères.setSpeedRequired(false);

critères.setCostAllowed(true);

localisations = (LocationManager) getSystemService(Context.LOCATION_SERVICE);

fournisseur = localisations.getBestProvider(critères, true);

localisations.requestLocationUpdates(fournisseur, 5000, 5, new ChangementPosition());

}

class ChangementPosition implements LocationListener { public void onLocationChanged(Location position) { if (position!=null) {

latitude.setText("Latitude : "+position.getLatitude());

longitude.setText("Longitude : "+position.getLongitude());

}

else latitude.setText("Position non déterminée ");

Toast.makeText(Geolocalisation.this, "Nouvelle position", Toast.LENGTH_SHORT).show();

}

public void onStatusChanged(String fournisseur, int statut, Bundle extras) { } public void onProviderEnabled(String fournisseur) { }

public void onProviderDisabled(String fournisseur) { } }

}

Voici une autre alternative qui prend en compte soit le GPS intégré soit le réseau téléphonique. Par ailleurs, je fais en sorte d'arrêter le service de géolocalisation lorsque l'activité n'est plus en action. Le service se remettra automatiquement en place lorsque nous réactiverons l'activité.

fr.btsiris.localisation.Geolocalisation.java

package fr.btsiris.localisation;

import android.app.Activity;

import android.content.Context;

import android.location.*;

import android.os.Bundle;

import android.widget.*;

public class Geolocalisation extends Activity { private TextView latitude, longitude;

private LocationManager localisations; private String fournisseur;

private ChangementPosition changements = new ChangementPosition();

(15)

@Override

public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);

setContentView(R.layout.main);

latitude = (TextView) findViewById(R.id.latitude);

longitude = (TextView) findViewById(R.id.longitude);

localisations = (LocationManager) getSystemService(Context.LOCATION_SERVICE);

miseAJourDeLaPosition(localisations.getLastKnownLocation(LocationManager.GPS_PROVIDER));

}

@Override

protected void onStart() { super.onStart();

localisations.requestLocationUpdates(LocationManager.GPS_PROVIDER, 2000, 2, changements);

localisations.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 5000, 5, changements);

}

@Override

protected void onStop() { super.onStop();

localisations.removeUpdates(changements);

}

private void miseAJourDeLaPosition(Location position) { if (position!=null) {

latitude.setText("Latitude : "+position.getLatitude());

longitude.setText("Longitude : "+position.getLongitude());

}

else latitude.setText("Position non déterminée ");

Toast.makeText(Geolocalisation.this, "Nouvelle position", Toast.LENGTH_SHORT).show();

}

class ChangementPosition implements LocationListener {

public void onLocationChanged(Location position) { miseAJourDeLaPosition(position); } public void onStatusChanged(String fournisseur, int statut, Bundle extras) { }

public void onProviderEnabled(String fournisseur) { } public void onProviderDisabled(String fournisseur) { } }

}

Alertes de proximité Il est souvent utile de voir vos applications réagir lorsque l'utilisateur se déplace en direction d'un lieu particulier ou s'en éloigne. Les alertes de proximité permettent de mettre en place des déclencheurs exécutés dans ces cas-là.

Pour mettre en place une alerte de proximité pour une zone de donnée, sélectionnez le centre de celle-ci (en l'exprimant en longitude et latitude), le rayon et un délai d'expiration de l'alerte. Elle sera déclenchée si l'appareil entre ou sort du rayon défini.

Lorsqu'elles sont déclenchées, les alertes de proximité exécutent des intentions, le plus souvent des Broadcast Intents. Pour spécifier l'intention, utilisez un PendingIntent, une classe qui wrappe l'intention en une sorte de pointeur de méthode au moyen de la méthode getBroadcast() :

Intent intention = new Intent(MON_ACTION);

PendingIntent proximité = PendingIntent.getBroadcast(this, -1, intention, 0);

La méthode addProximityAlert() de la classe LocationManager permet, quant à elle, d'enregistrer un PendingIntent qui sera déclenché quand l'appareil se trouvera à une certaine distance du point fixé. La méthode possède la signature suivante :

public void addProximityAlert(double latitude, double longitude, float rayon, long expiration, PendingIntent intention);

1. latitude : correspond à la latitude du point d'alerte ; 2. longitude : correspond à la longitude du point d'alerte ;

3. rayon : correspond au rayon autour du point d'alerte qui déterminera la sensibilité de la détection ;

4. expiration : définit une période en millisecondes à la fin de laquelle l'élément LocationManager retirera le point d'alerte des alertes surveillées. La valeur -1 signifie que l'enregistrement est valide jusqu'à qu'il soit retiré manuellement par la méthode removeProximityAlert().

5. intention : est utilisé pour générer l'intention déclenchée quand l'appareil rentre ou sort de la zone désignée par le point et le rayon.

L'intention PendingIntent est étendue avec un extra dont la clé se nomme KEY_PROXIMITY_ENTERING. Nous obtenons alors un booléen dont la valeur est true si nous sommes dans la zone définie et false si nous la quittons.

La gestion de l'alerte se réalise au travers d'un BroadcastReceiver, dont vous devez redéfinir la méthode de rappel onReceiver() qui permet de spécifier quelles actions vous souhaitez réaliser par rapport à la proximité du lieu, qu'il est ensuite nécessaire d'enregistrer au travers de la méthode registerReceiver(). Si l'activité n'est plus active, il est souhaitable d'enlever l'alerte au moyen de la méthode removeProximityAlert().

Afin de factoriser toutes ces petites connaissances que nous venons d'acquérir, je vous propose de reprendre l'exemple précédent, et de l'étoffer avec une alerte qui se produit lorsque nous sortons d'une zone de 7 mètres de rayon. Quand l'activité démarre, elle mesure la postion actuelle qui sert alors de référence pour l'alerte.

Références

Documents relatifs

Appy pie pour android gratuits sur les applications rend difficile sur comment créer une application gratuite où vous aurez besoin pour toutes les sites.. Lxtream application

Architecture du système d’exploitation Android - Application et Framework pour les applications...  La seule couche visible et accessible par l’utilisateur

Le but de ce cours est de découvrir la programmation sous Android, sa plate-forme de développement et les spécificités du développement embarqué sur

For more information on using Google Maps in Android application

Dans ce chapitre on voit comment sauvegarder des données d’une application android : une préférence (donnée courte), un fichier (donnée plus volumineuse), un fichier JSON, une base

Il s’agit d’afficher sur un smartphone (ou une tablette) les flux vidéo de télémesure et de commander le robot en maintenant une latence minimale au travers d’une

  

Dans le cadre du projet de fin d'année en E3, nous avons opté pour le développement d'une application multimédia fonctionnant sous le système Android, que ce