Recevoir des mises à jour de position sous Android avec Kotlin

Android 10 et 11 leur permettent de mieux contrôler l'accès à la position de leur appareil.

Lorsqu'une application exécutée sous Android 11 demande l'accès à la position, les utilisateurs disposent de quatre options:

  • Toujours autoriser
  • Autoriser si appli utilisée dans Android 10
  • À usage unique (Android 11)
  • Refuser

Android 10

Android 11

Dans cet atelier de programmation, vous découvrirez comment recevoir des mises à jour de la position et prendre en charge la localisation sur n'importe quelle version d'Android, en particulier Android 10 et 11. À la fin de l'atelier de programmation, vous vous attendreez à avoir une application qui respecte les bonnes pratiques actuelles en matière de récupération des mises à jour de position.

Conditions préalables

Objectifs de l'atelier

  • Suivez les bonnes pratiques concernant la localisation sur Android.
  • Gérer les autorisations d'accéder au premier plan (lorsque l'utilisateur demande que votre application accède à la position de l'appareil pendant que votre application est utilisée)
  • Modifiez une application existante pour permettre les demandes d'accès à la position en ajoutant du code pour les abonner et les désabonner.
  • Ajoutez la compatibilité avec l'application pour Android 10 et 11 en ajoutant une logique pour accéder à la position au premier plan ou en cours d'utilisation.

Prérequis

  • Android Studio version 3.4 ou ultérieure, pour exécuter le code
  • Un appareil/un émulateur exécutant un aperçu développeur d'Android 10 et 11

Cloner les éléments du projet de démarrage

Pour démarrer le plus rapidement possible, vous pouvez vous appuyer sur ce projet de démarrage. Si Git est installé, vous pouvez simplement exécuter la commande suivante:

 git clone https://github.com/googlecodelabs/while-in-use-location

N'hésitez pas à consulter directement la page GitHub.

Si vous n'avez pas Git, vous pouvez obtenir le projet au format ZIP:

Télécharger le fichier ZIP

Importer le projet

Ouvrez Android Studio, sélectionnez Open an existing Android Studio project (Ouvrir un projet Android Studio existant), puis ouvrez le répertoire du projet.

Une fois le projet chargé, une alerte peut apparaître, indiquant que Git ne suit pas toutes les modifications locales. Vous pouvez cliquer sur Ignorer. (Vous n'envoyez aucune modification dans le dépôt Git.)

En haut à gauche de la fenêtre du projet, vous devriez voir l'image ci-dessous si vous êtes dans la vue Android. Si vous êtes dans la vue Projet, vous devez développer le projet pour voir la même chose.

Il existe deux dossiers (base et complete). Ils sont tous appelés "module".

Notez qu'Android Studio peut mettre plusieurs secondes à compiler le projet en arrière-plan pour la première fois. Pendant ce temps, le message suivant s'affiche dans la barre d'état en bas d'Android Studio:

Attendez qu'Android Studio ait fini d'indexer et de créer le projet avant de modifier le code. Android Studio pourra alors extraire tous les composants nécessaires.

Si le message Actualiser pour changer la langue prend effet ? s'affiche, sélectionnez Oui.

Comprendre le projet de démarrage

Vous êtes prêt à demander des informations de localisation dans l'application. Utilisez le module base comme point de départ. À chaque étape, ajoutez du code au module base. À la fin de cet atelier de programmation, le code du module base doit correspondre au contenu du module complete. Vous pouvez utiliser le module complete pour vérifier votre travail ou vous y reporter en cas de problèmes.

Les principaux composants sont les suivants:

  • MainActivity : interface utilisateur permettant à l'application d'accéder à la position de l'appareil
  • LocationService : service qui s'abonne et se désabonne des modifications de lieu, et qui se présente comme un service de premier plan (avec notification) si l'utilisateur quitte l'activité de l'appli. Vous ajoutez le code de zone géographique ici.
  • Util : ajoute des fonctions d'extension pour la classe Location et enregistre l'emplacement dans SharedPreferences (couche de données simplifiée).

Configuration de l'émulateur

Pour savoir comment configurer un émulateur Android, consultez Exécuter sur un émulateur.

Exécuter le projet de démarrage

Exécutez votre application.

  1. Connectez votre appareil Android à votre ordinateur ou démarrez un émulateur. (Vérifiez que l'appareil utilise Android 10 ou une version ultérieure.)
  2. Dans la barre d'outils, sélectionnez la configuration base dans le menu déroulant, puis cliquez sur Run (Exécuter) :


  1. L'application suivante s'affiche sur votre appareil:


Notez que l'écran de sortie n'affiche aucune information de géolocalisation. C'est parce que vous n'avez pas encore ajouté le code de zone géographique.

concepts

Le but de cet atelier de programmation est de vous montrer comment recevoir des mises à jour de votre position. Cet outil est compatible avec Android 10 et Android 11.

Toutefois, avant de commencer à coder, il est judicieux de revoir les principes de base.

Types d'accès à la position

Vous vous souvenez peut-être des quatre options d'accès aux données de localisation présentées au début de l'atelier de programmation. Découvrez leur signification:

  • Autoriser seulement si l'appli est en cours d'utilisation
  • Cette option est recommandée pour la plupart des applications. Également appelée "pendant l'utilisation", cette option a été ajoutée dans Android 10 et permet aux développeurs de récupérer la position uniquement lorsque l'application est en cours d'utilisation. Une application est considérée comme active si l'une des conditions se vérifie:
  • Une activité est visible.
  • Un service de premier plan est en cours d'exécution avec une notification en cours.
  • Une seule fois
  • Dans Android 11, cette option est identique à la règle Autoriser uniquement si l'application est utilisée, mais pour une durée limitée. Pour plus d'informations, consultez Autorisations ponctuelles.
  • Refuser
  • Cette option empêche l'accès aux informations de géolocalisation.
  • Toujours autoriser
  • Cette option autorise l'accès à la position en permanence, mais nécessite une autorisation supplémentaire pour Android 10 ou version ultérieure. Vous devez également vous assurer que vous disposez d'un cas d'utilisation valide et que vous respectez les règles relatives à la localisation. Vous n'allez pas aborder cette option dans cet atelier de programmation, car il s'agit d'un cas d'utilisation plus rare. Cependant, si vous disposez d'un cas d'utilisation valide et que vous souhaitez comprendre comment gérer correctement la position permanente, y compris l'accès aux données de localisation en arrière-plan, consultez l'exemple LocationUpdatesBackgroundKotlin.

Services, services de premier plan et liaisons

Pour assurer une compatibilité totale avec les mises à jour de la position Autoriser uniquement si vous utilisez l'application, vous devez tenir compte des moments où l'utilisateur quitte votre application. Si vous souhaitez continuer à recevoir des mises à jour dans ce cas, vous devez créer un Service au premier plan et l'associer à un Notification.

En outre, si vous souhaitez utiliser le même Service pour demander la mise à jour de la position lorsque votre application est visible et que l'utilisateur quitte votre application, vous devez lier ou dissocier ce Service à l'élément d'UI.

Comme cet atelier de programmation se concentre uniquement sur l'obtention des mises à jour de position, vous pouvez trouver tout le code dont vous avez besoin dans la classe ForegroundOnlyLocationService.kt. Vous pouvez parcourir cette classe et la MainActivity.kt pour voir comment elles fonctionnent ensemble.

Pour en savoir plus, consultez Présentation des services et Présentation des services liés.

Autorisations

Pour recevoir les mises à jour de position depuis un NETWORK_PROVIDER ou un GPS_PROVIDER, vous devez demander l'autorisation de l'utilisateur en déclarant l'autorisation ACCESS_COARSE_LOCATION ou ACCESS_FINE_LOCATION, respectivement, dans votre fichier manifeste Android. Sans ces autorisations, votre appli ne pourra pas demander l'accès à la localisation au moment de l'exécution.

Ces autorisations concernent les cas Une seule fois et Autoriser uniquement si l'application est utilisée lorsque vous utilisez cette dernière sur un appareil équipé d'Android 10 ou version ultérieure.

Emplacement

Votre application peut accéder à l'ensemble des services de localisation compatibles via des classes dans le package com.google.android.gms.location.

Examinez les classes principales:

  • FusedLocationProviderClient
  • Il s'agit du composant central du framework de localisation. Une fois créé, vous pouvez l'utiliser pour demander des modifications de position et obtenir la dernière position connue.
  • LocationRequest
  • Il s'agit d'un objet de données qui contient des paramètres de qualité de service pour les requêtes (intervalles pour les mises à jour, les priorités et la justesse). Cet identifiant est transmis à FusedLocationProviderClient lorsque vous demandez la mise à jour de vos établissements.
  • LocationCallback
  • Elle est utilisée pour recevoir des notifications lorsque l'emplacement de l'appareil a changé ou ne peut plus être déterminé. Cette valeur est transmise à un LocationResult où vous pouvez obtenir l'Location à enregistrer dans votre base de données.

Maintenant que vous savez ce que vous faites, commencez avec le code !

Cet atelier de programmation porte sur l'option de localisation la plus courante Autoriser seulement si l'appli est en cours d'utilisation.

Pour recevoir des mises à jour de position, votre application doit comporter une activité visible ou un service exécuté au premier plan (avec une notification).

Autorisations

Le but de cet atelier de programmation est de vous montrer comment recevoir des mises à jour de la position, et non comment demander des autorisations d'accéder à la position. Le code basé sur les autorisations est donc déjà rédigé. N'hésitez pas à l'ignorer si vous le comprenez déjà.

Voici les principaux points forts de l'autorisation (aucune action n'est requise pour cette partie):

  1. Déclarez l'autorisation utilisée dans AndroidManifest.xml.
  2. Avant d'essayer d'accéder aux informations de localisation, vérifiez si l'utilisateur a autorisé votre application à le faire. Si ce n'est pas le cas, demandez l'accès.
  3. Gérez le choix de l'autorisation utilisateur. Ce code est disponible sur la page MainActivity.kt.

Si vous recherchez TODO: Step 1.0, Review Permissions dans AndroidManifest.xml ou MainActivity.kt, tout le code écrit pour les autorisations s'affiche.

Pour en savoir plus, consultez Présentation des autorisations.

À présent, commencez à écrire un code de localisation.

Consulter les variables clés nécessaires à la mise à jour des établissements

Dans le module base, recherchez TODO: Step 1.1, Review variables :

Fichier ForegroundOnlyLocationService.kt.

Aucune action n'est requise pour cette étape. Pour comprendre les classes et les variables clés que vous utilisez pour recevoir des notifications sur les établissements, il vous suffit de consulter le bloc de code suivant et les commentaires associés.

// TODO: Step 1.1, Review variables (no changes).
// FusedLocationProviderClient - Main class for receiving location updates.
private lateinit var fusedLocationProviderClient: FusedLocationProviderClient

// LocationRequest - Requirements for the location updates, i.e., how often you
// should receive updates, the priority, etc.
private lateinit var locationRequest: LocationRequest

// LocationCallback - Called when FusedLocationProviderClient has a new Location.
private lateinit var locationCallback: LocationCallback

// Used only for local storage of the last known location. Usually, this would be saved to your
// database, but because this is a simplified sample without a full database, we only need the
// last location to create a Notification if the user navigates away from the app.
private var currentLocation: Location? = null

Vérifier l'initialisation de FusedLocationProviderClient

Dans le module base, recherchez TODO: Step 1.2, Review the FusedLocationProviderClient dans le fichier ForegroundOnlyLocationService.kt. Le code doit se présenter comme suit:

// TODO: Step 1.2, Review the FusedLocationProviderClient.
fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this)

Comme indiqué dans les commentaires précédents, il s'agit de la classe principale pour recevoir les mises à jour de position. La variable est déjà initialisée, mais il est important de vous familiariser avec son code pour l'initialiser. Vous ajouterez un code ici plus tard pour demander des mises à jour de la position.

Initialiser la LocationRequest

  1. Dans le module base, recherchez TODO: Step 1.3, Create a LocationRequest dans le fichier ForegroundOnlyLocationService.kt.
  2. Ajoutez le code suivant après le commentaire.

Le code d'initialisation LocationRequest ajoute la qualité supplémentaire des paramètres de service dont vous avez besoin pour votre requête (intervalles, temps d'attente maximal et priorité).

// TODO: Step 1.3, Create a LocationRequest.
locationRequest = LocationRequest().apply {
   // Sets the desired interval for active location updates. This interval is inexact. You
   // may not receive updates at all if no location sources are available, or you may
   // receive them less frequently than requested. You may also receive updates more
   // frequently than requested if other applications are requesting location at a more
   // frequent interval.
   //
   // IMPORTANT NOTE: Apps running on Android 8.0 and higher devices (regardless of
   // targetSdkVersion) may receive updates less frequently than this interval when the app
   // is no longer in the foreground.
   interval = TimeUnit.SECONDS.toMillis(60)

   // Sets the fastest rate for active location updates. This interval is exact, and your
   // application will never receive updates more frequently than this value.
   fastestInterval = TimeUnit.SECONDS.toMillis(30)

   // Sets the maximum time when batched location updates are delivered. Updates may be
   // delivered sooner than this interval.
   maxWaitTime = TimeUnit.MINUTES.toMillis(2)

   priority = LocationRequest.PRIORITY_HIGH_ACCURACY
}
  1. Lisez attentivement les commentaires pour en comprendre le fonctionnement.

Initialiser le LocationCallback

  1. Dans le module base, recherchez TODO: Step 1.4, Initialize the LocationCallback dans le fichier ForegroundOnlyLocationService.kt.
  2. Ajoutez le code suivant après le commentaire.
// TODO: Step 1.4, Initialize the LocationCallback.
locationCallback = object : LocationCallback() {
   override fun onLocationResult(locationResult: LocationResult?) {
       super.onLocationResult(locationResult)

       if (locationResult?.lastLocation != null) {

           // Normally, you want to save a new location to a database. We are simplifying
           // things a bit and just saving it as a local variable, as we only need it again
           // if a Notification is created (when user navigates away from app).
           currentLocation = locationResult.lastLocation

           // Notify our Activity that a new location was added. Again, if this was a
           // production app, the Activity would be listening for changes to a database
           // with new locations, but we are simplifying things a bit to focus on just
           // learning the location side of things.
           val intent = Intent(ACTION_FOREGROUND_ONLY_LOCATION_BROADCAST)
           intent.putExtra(EXTRA_LOCATION, currentLocation)
           LocalBroadcastManager.getInstance(applicationContext).sendBroadcast(intent)

           // Updates notification content if this service is running as a foreground
           // service.
           if (serviceRunningInForeground) {
               notificationManager.notify(
                   NOTIFICATION_ID,
                   generateNotification(currentLocation))
           }
       } else {
           Log.d(TAG, "Location information isn't available.")
       }
   }
}

Le LocationCallback que vous créez ici est le rappel que FusedLocationProviderClient appellera lorsqu'une nouvelle mise à jour de la position sera disponible.

Dans votre rappel, vous obtenez d'abord la dernière position à l'aide d'un objet LocationResult. Ensuite, vous avertirez Activity de la nouvelle position à l'aide d'une diffusion locale (si elle est active) ou mettre à jour Notification si ce service est exécuté en tant que premier plan Service.

  1. Lisez attentivement les commentaires pour en comprendre le rôle.

S'abonner aux modifications de localisation

Maintenant que tout est initialisé, vous devez indiquer à FusedLocationProviderClient que vous souhaitez recevoir des mises à jour.

  1. Dans le module base, recherchez Step 1.5, Subscribe to location changes dans le fichier ForegroundOnlyLocationService.kt.
  2. Ajoutez le code suivant après le commentaire.
// TODO: Step 1.5, Subscribe to location changes.
fusedLocationProviderClient.requestLocationUpdates(locationRequest, locationCallback, Looper.myLooper())

L'appel requestLocationUpdates() informe FusedLocationProviderClient que vous souhaitez recevoir des mises à jour de position.

Vous reconnaissez probablement que les champs LocationRequest et LocationCallback que vous avez définis précédemment. Ces valeurs permettent au FusedLocationProviderClient de connaître les paramètres de qualité de service de votre requête et le nom à appeler lors de la mise à jour. Enfin, l'objet Looper spécifie le thread pour le rappel.

Notez également que ce code figure dans une instruction try/catch. Cette méthode nécessite un tel blocage, car un SecurityException se produit lorsque votre appli n'est pas autorisée à accéder aux informations de géolocalisation.

Se désabonner des modifications apportées aux établissements

Lorsque l'application n'a plus besoin d'accéder aux informations de localisation, il est important de se désabonner des mises à jour de position.

  1. Dans le module base, recherchez TODO: Step 1.6, Unsubscribe to location changes dans le fichier ForegroundOnlyLocationService.kt.
  2. Ajoutez le code suivant après le commentaire.
// TODO: Step 1.6, Unsubscribe to location changes.
val removeTask = fusedLocationProviderClient.removeLocationUpdates(locationCallback)
removeTask.addOnCompleteListener { task ->
   if (task.isSuccessful) {
       Log.d(TAG, "Location Callback removed.")
       stopSelf()
   } else {
       Log.d(TAG, "Failed to remove Location Callback.")
   }
}

La méthode removeLocationUpdates() configure une tâche pour indiquer à FusedLocationProviderClient que vous ne souhaitez plus recevoir de mises à jour de position pour votre LocationCallback. Le addOnCompleteListener() attribue le rappel à la fin et exécute le Task.

Comme à l'étape précédente, vous avez peut-être remarqué que ce code se trouve dans une instruction try/catch. Cette méthode nécessite un tel blocage, car un SecurityException se produit lorsque votre appli n'est pas autorisée à accéder aux informations de localisation

Vous vous demandez peut-être à quel moment les méthodes qui contiennent ce code sont appelées. Ils sont déclenchés dans la classe principale lorsque l'utilisateur appuie sur le bouton. Si vous voulez le voir, consultez la classe MainActivity.kt.

Exécuter l'application

Exécutez votre application depuis Android Studio et essayez le bouton de localisation.

Les informations de localisation doivent s'afficher dans l'écran de sortie. Cette application est totalement fonctionnelle pour Android 9.

Dans cette section, vous trouverez la compatibilité avec Android 10.

Votre application est déjà abonnée aux modifications de lieu. Vous n'avez donc pas beaucoup de travail à effectuer.

En fait, il vous suffit de spécifier que votre service de premier plan est utilisé à des fins de géolocalisation.

SDK 29 cible

  1. Dans le module base, recherchez TODO: Step 2.1, Target SDK 10 dans le fichier build.gradle.
  2. Apportez les modifications suivantes:
  1. Définissez compileSdkVersion sur 29.
  2. Définissez buildToolsVersion sur "29.0.3".
  3. Définissez targetSdkVersion sur 29.

Le code doit se présenter comme suit:

android {
   // TODO: Step 2.1, Target Android 10.
   compileSdkVersion 29
   buildToolsVersion "29.0.3"
   defaultConfig {
       applicationId "com.example.android.whileinuselocation"
       minSdkVersion 26
       targetSdkVersion 29
       versionCode 1
       versionName "1.0"
   }
...
}

Vous serez ensuite invité à synchroniser votre projet. Cliquez sur Synchroniser.

Après cela, votre application sera presque prête pour Android 10.

Ajouter un type de service au premier plan

Dans Android 10, vous devez inclure le type de service de votre premier plan si vous avez besoin d'un accès à la position pendant l'utilisation. Dans votre cas, elle est utilisée pour obtenir des informations sur les établissements.

Dans le module base, recherchez TODO: 2.2, Add foreground service type dans AndroidManifest.xml et ajoutez le code suivant à l'élément <service>:

android:foregroundServiceType="location"

Le code doit se présenter comme suit:

<application>
   ...

   <!-- Foreground services in Android 10+ require type. -->
   <!-- TODO: 2.2, Add foreground service type. -->
   <service
       android:name="com.example.android.whileinuselocation.ForegroundOnlyLocationService"
       android:enabled="true"
       android:exported="false"
       android:foregroundServiceType="location" />
</application>

Et voilà ! Votre application est compatible avec la localisation Android 10 pendant son utilisation en suivant les bonnes pratiques liées à la localisation sur Android.

Exécuter l'application

Exécutez votre application depuis Android Studio et essayez le bouton de localisation.

Tout devrait fonctionner comme avant, mais elle est désormais compatible avec Android 10. Si vous n'avez pas accepté les autorisations auparavant associées aux emplacements, l'écran dédié aux autorisations devrait désormais s'afficher.

Dans cette section, vous ciblez Android 11.

Bonne nouvelle, vous n'avez pas besoin de modifier les fichiers, sauf le fichier build.gradle !

SDK R cible

  1. Dans le module base, recherchez TODO: Step 2.1, Target SDK dans le fichier build.gradle.
  2. Apportez les modifications suivantes:
  1. De compileSdkVersion à "android-R"
  2. De targetSdkVersion à "R"

Le code doit se présenter comme suit:

android {
   // TODO: Step 2.1, Target Android 10.
   compileSdkVersion "android-R"
   buildToolsVersion "29.0.2"
   defaultConfig {
       applicationId "com.example.android.whileinuselocation"
       minSdkVersion 26
       targetSdkVersion "R"
       versionCode 1
       versionName "1.0"
   }
...
}

Vous serez ensuite invité à synchroniser votre projet. Cliquez sur Synchroniser.

Votre application sera ensuite prête pour Android 11 !

Exécuter l'application

Exécutez votre application à partir d'Android Studio, puis cliquez sur le bouton.

Tout devrait fonctionner comme avant, mais elle est désormais compatible avec Android 11. Si vous n'avez pas accepté les autorisations auparavant associées aux emplacements, l'écran dédié aux autorisations devrait désormais s'afficher.

En vérifiant et en demandant les autorisations d'accéder à la position comme indiqué dans cet atelier de programmation, votre application peut effectuer le suivi de son niveau d'accès à la position de l'appareil.

Cette page liste quelques bonnes pratiques clés liées aux autorisations d'accéder à la position. Pour savoir comment protéger les données de vos utilisateurs, consultez Bonnes pratiques concernant les autorisations des applications.

Demandez uniquement les autorisations dont vous avez besoin.

Ne demandez des autorisations que si nécessaire. Exemple :

  • Ne demandez pas l'autorisation d'accéder à la position au démarrage de l'appli, sauf en cas d'absolue nécessité.
  • Si votre application cible Android 10 ou une version ultérieure et que vous disposez d'un service de premier plan, déclarez une valeur foregroundServiceType de "location" dans le fichier manifeste.
  • Ne demandez pas d'autorisations d'accéder à la position en arrière-plan, sauf si vous disposez d'un cas d'utilisation valide tel que décrit dans la section Un accès plus sécurisé et plus transparent à la position de l'utilisateur.

Permettre une dégradation élégante si l'autorisation n'est pas accordée

Pour offrir une expérience utilisateur de qualité, concevez votre application de sorte qu'elle puisse gérer correctement les situations suivantes:

  • Votre appli n'a pas accès aux informations de localisation.
  • Votre appli n'a pas accès aux données de localisation en arrière-plan.

Vous avez appris à recevoir des mises à jour de votre position sur Android en tenant compte des bonnes pratiques.

En savoir plus