Recevoir des notifications de position sur Android avec Kotlin

1. Avant de commencer

Android 10 et 11 offrent aux utilisateurs plus de contrôle sur leurs applications 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 seulement si l'appli est en cours d'utilisation (sous Android 10)
  • Une seule fois (sous Android 11)
  • Refuser

Android 10

6a1029175b467c77.png

Android 11

73d8cc88c5877c25.png

Dans cet atelier de programmation, vous allez apprendre à recevoir des notifications de position et à activer la localisation sur n'importe quelle version d'Android, en particulier Android 10 et 11. À la fin de l'atelier de programmation, vous disposerez d'une application qui respectera les bonnes pratiques actuelles pour récupérer les mises à jour de la position.

Prérequis

Objectifs de l'atelier

  • Suivez les bonnes pratiques concernant la localisation sur Android.
  • Gérez les autorisations d'accéder à la position au premier plan (lorsque l'utilisateur demande à votre application d'accéder à la position de l'appareil pendant son utilisation).
  • Modifier une application existante pour qu'elle prenne en charge la demande d'accès à la position en ajoutant du code pour s'abonner à la position ou se désabonner
  • Ajoutez la prise en charge de l'application à Android 10 et 11 en ajoutant une logique pour accéder aux données de localisation au premier plan ou lorsqu'elles sont en cours d'utilisation.

Prérequis

  • Android Studio 3.4 ou version ultérieure pour exécuter le code
  • Un appareil/émulateur exécutant une version Preview développeur d'Android 10 et 11

2. Premiers pas

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

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

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

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

Si vous n'avez pas Git, vous pouvez récupérer le projet sous forme de fichier ZIP:

Importer le projet

Ouvrez Android Studio, puis sélectionnez Open an existing Android Studio project (Ouvrir un projet Android Studio existant). à partir de l'écran d'accueil, puis ouvrez le répertoire du projet.

Une fois le projet chargé, une alerte peut s'afficher, indiquant que Git ne suit pas toutes les modifications locales. Vous pouvez cliquer sur Ignorer. (Vous ne transmettrez pas vos modifications au dépôt Git.)

Dans l'angle supérieur gauche de la fenêtre du projet, un message semblable à l'image ci-dessous devrait s'afficher si vous êtes dans la vue Android. Si vous êtes dans la vue Project (Projet), vous devez développer le projet pour afficher la même chose.

fa825dae96c5dc18.png

Il y a deux dossiers (base et complete). Chacun d'eux est appelé "module".

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

c2273e7835c0841a.png

Attendez qu'Android Studio ait fini d'indexer et de compiler le projet avant de modifier le code. Android Studio pourra ainsi récupérer tous les composants nécessaires.

Si vous recevez une invite du type Actualiser pour que les modifications de langue soient prises en compte ou similaire, sélectionnez Oui.

Comprendre le projet de démarrage

La configuration et la demande de localisation sont maintenant effectuées 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 devrait correspondre au contenu du module complete. Le module complete peut être utilisé pour vérifier votre travail ou pour vous y référer en cas de problème.

Les composants clés sont les suivants:

  • MainActivity : UI permettant à l'utilisateur d'autoriser l'application à accéder à la position de l'appareil
  • LocationService : service qui s'abonne ou se désabonne aux changements de position, et s'affiche en tant que service de premier plan (avec une notification) si l'utilisateur quitte l'activité de l'application. Vous ajoutez ici le code de la zone géographique.
  • 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 en savoir plus sur la configuration d'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. (Assurez-vous que l'appareil est équipé d'Android 10 ou version ultérieure.)
  2. Dans la barre d'outils, sélectionnez la configuration base dans le menu déroulant, puis cliquez sur Exécuter:

99600e9d44527ab.png

  1. Notez que l'application suivante s'affiche sur votre appareil:

99bf1dae46f99af3.png

Vous remarquerez peut-être qu'aucune information de localisation ne s'affiche sur l'écran de sortie. En effet, vous n'avez pas encore ajouté le code de la zone géographique.

3. Ajout du lieu...

Concepts

L'objectif de cet atelier de programmation est de vous montrer comment recevoir des notifications de position et, à terme, prendre en charge Android 10 et Android 11.

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

Types d'accès à la position

Vous vous souvenez peut-être des quatre options d'accès à la position proposées au début de l'atelier de programmation. Voici leur signification:

  • Autoriser seulement si l'appli est en cours d'utilisation
  • Cette option est recommandée pour la plupart des applications. Également appelé "pendant l'utilisation" ou "premier plan uniquement" , cette option a été ajoutée sur 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 suivantes est remplie:
  • Une activité est visible.
  • Un service de premier plan est en cours d'exécution avec une notification d'activité en cours.
  • Ponctuel uniquement
  • Ajoutée dans Android 11, cette option est la même que l'option Autoriser seulement si l'appli est utilisée, mais pour une durée limitée. Pour en savoir plus, consultez la section Autorisations ponctuelles.
  • Refuser
  • Cette option empêche l'accès aux informations de localisation.
  • Toujours autoriser
  • Cette option permet d'accéder à votre position en permanence, mais requiert une autorisation supplémentaire pour Android 10 ou version ultérieure. Vous devez également vous assurer que votre cas d'utilisation est valide et que vous respectez les règles relatives à la localisation. Cette option n'est pas abordée dans cet atelier de programmation, car il s'agit d'un cas d'utilisation plus rare. Toutefois, si vous disposez d'un cas d'utilisation valide et souhaitez savoir comment gérer correctement la localisation en permanence, y compris l'accès aux données de localisation en arrière-plan, consultez l'exemple LocationUpdatesBackgroundKotlin.

Services, services de premier plan et liaison

Pour assurer la compatibilité totale des mises à jour de position avec l'option Autoriser seulement si l'appli est utilisée, vous devez tenir compte du moment où l'utilisateur quitte votre application. Si vous souhaitez continuer à recevoir des mises à jour dans ce cas, vous devez créer un Service de premier plan et l'associer à un Notification.

De plus, si vous souhaitez utiliser le même Service pour demander des notifications de position lorsque votre application est visible et que l'utilisateur la quitte, vous devez associer/dissocier ce Service à l'élément d'interface utilisateur.

Comme cet atelier de programmation porte uniquement sur les notifications de position, vous trouverez 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 des notifications de position à partir d'un NETWORK_PROVIDER ou d'un GPS_PROVIDER, vous devez demander l'autorisation de l'utilisateur en déclarant l'autorisation ACCESS_COARSE_LOCATION ou ACCESS_FINE_LOCATION dans votre fichier manifeste Android, respectivement. Sans ces autorisations, votre application ne pourra pas demander l'accès à la position au moment de l'exécution.

Ces autorisations couvrent les cas Ponctuel uniquement et Autoriser seulement si l'appli est en cours d'utilisation lorsque votre application est utilisée 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 les classes du package com.google.android.gms.location.

Examinez les classes principales:

  • FusedLocationProviderClient
  • Il s'agit de l'élément central du framework de localisation. Une fois créée, elle vous permet de demander des notifications de position et d'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 de mise à jour, priorités et précision). Il est transmis à FusedLocationProviderClient lorsque vous demandez des mises à jour de la position.
  • LocationCallback
  • Permet de recevoir des notifications lorsque la position de l'appareil a changé ou ne peut plus être déterminée. Elle transmet un LocationResult, où vous pouvez obtenir le Location à enregistrer dans votre base de données.

Maintenant que vous avez une idée de base de ce que vous faites, commencez à utiliser le code.

4. Ajouter des fonctionnalités de localisation

Cet atelier de programmation porte sur l'option de localisation la plus courante: Autoriser seulement si l'appli est utilisée.

Pour recevoir des notifications de position, votre application doit avoir une activité visible ou un service en cours d'exécution au premier plan (avec une notification).

Autorisations

Le but de cet atelier de programmation est de vous montrer comment recevoir des notifications de position, et non comment demander l'autorisation d'accéder à la position. Le code basé sur les autorisations est donc déjà écrit pour vous. N'hésitez pas à les ignorer si vous les avez déjà comprises.

Voici les principales autorisations (aucune action n'est requise pour cette partie):

  1. Déclarez l'autorisation que vous utilisez 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 votre application n'a pas encore reçu l'autorisation, demandez l'accès.
  3. Gérez le choix des autorisations de l'utilisateur. (Vous pouvez consulter ce code dans MainActivity.kt.)

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

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

Maintenant, commencez à écrire un code de localisation.

Examiner les variables clés nécessaires aux mises à jour de la position

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

Fichier ForegroundOnlyLocationService.kt.

Aucune action n'est requise à cette étape. Il vous suffit d'examiner le bloc de code suivant, ainsi que les commentaires, afin de comprendre les classes clés et les variables que vous utilisez pour recevoir des notifications de position.

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

Examiner l'initialisation de FusedLocationProviderClient

Dans le module base, recherchez TODO: Step 1.2, Review the FusedLocationProviderClient dans le fichier ForegroundOnlyLocationService.kt. Votre 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 des notifications de position. La variable est déjà initialisée pour vous, mais il est important d'examiner le code pour comprendre comment elle est initialisée. Plus tard, vous ajouterez du code ici pour demander des notifications de 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 les paramètres de qualité de service supplémentaires dont vous avez besoin pour votre requête (intervalles, temps d'attente maximal et priorité).

// TODO: Step 1.3, Create a LocationRequest.
locationRequest = LocationRequest.create().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 les commentaires pour comprendre leur 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)

        // 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 the 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))
        }
    }
}

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

Dans votre rappel, vous obtenez d'abord la dernière position à l'aide d'un objet LocationResult. Ensuite, informez votre Activity du nouvel emplacement à l'aide d'une annonce locale (s'il est actif) ou mettez à jour Notification si ce service est exécuté en tant que Service de premier plan.

  1. Lisez les commentaires pour comprendre à quoi sert chaque partie.

S'abonner aux changements de lieu

Maintenant que vous avez tout 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.getMainLooper())

L'appel requestLocationUpdates() indique à FusedLocationProviderClient que vous souhaitez recevoir des notifications de position.

Vous reconnaissez probablement les éléments LocationRequest et LocationCallback que vous avez définis précédemment. Ils permettent à FusedLocationProviderClient de connaître les paramètres de qualité de service de votre requête et ce qu'il doit appeler en cas de mise à jour. Enfin, l'objet Looper spécifie le thread du rappel.

Vous remarquerez également que ce code se trouve dans une instruction try/catch. Cette méthode nécessite un tel blocage, car une erreur SecurityException se produit lorsque votre application n'est pas autorisée à accéder aux informations de localisation.

Se désabonner des modifications de lieu

Lorsque l'application n'a plus besoin d'accéder aux informations de localisation, il est important de se désabonner des notifications 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 notifications de position pour votre LocationCallback. addOnCompleteListener() donne le rappel pour l'achèvement et exécute Task.

Comme pour 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 une erreur SecurityException se produit lorsque votre application n'est pas autorisée à accéder aux informations de localisation

Vous vous demandez peut-être quand les méthodes qui contiennent le code d'abonnement/de désabonnement sont appelées. Ils sont déclenchés dans la classe principale lorsque l'utilisateur appuie sur le bouton. Si vous souhaitez l'afficher, examinez la classe MainActivity.kt.

Exécuter l'application

Exécutez votre application à partir d'Android Studio et cliquez sur le bouton de localisation.

Les informations d'emplacement doivent s'afficher sur l'écran de sortie. Il s'agit d'une application entièrement fonctionnelle pour Android 9.

2ae45c4e297e3681.png

d66089bfb532e993.png

5. Compatibilité avec Android 10

Dans cette section, vous allez prendre en charge Android 10.

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

Il vous suffit d'indiquer que votre service de premier plan doit être utilisé à des fins de localisation.

SDK cible 29

  1. Dans le module base, recherchez TODO: Step 2.1, Target Android 10 and then Android 11. dans le fichier build.gradle.
  2. Apportez les modifications suivantes:
  3. Définissez targetSdkVersion sur 29.

Votre code doit se présenter comme suit:

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

Vous serez ensuite invité à synchroniser votre projet. Cliquez sur Synch Now (Synchroniser).

153f70847e0ec320.png

Ensuite, votre application est presque prête pour Android 10.

Ajouter un type de service de premier plan

Dans Android 10, vous devez inclure le type de votre service de premier plan si vous avez besoin d'accéder à votre position pendant l'utilisation. Dans votre cas, il est utilisé pour obtenir des informations de localisation.

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"

Votre 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 appli est compatible avec la localisation Android 10 pendant l'utilisation en suivant les bonnes pratiques de localisation dans Android.

Exécuter l'application

Exécutez votre application à partir d'Android Studio et cliquez sur le bouton de localisation.

Tout devrait fonctionner comme avant, mais désormais sous Android 10. Si vous n'avez pas encore accepté les autorisations pour les établissements, l'écran d'autorisation devrait maintenant s'afficher.

6a1029175b467c77.png

c7c1d226e49a121.png

39a262b66a275f66.png

6. Compatibilité avec Android 11

Dans cette section, vous allez cibler Android 11.

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

SDK cible 11

  1. Dans le module base, recherchez TODO: Step 2.1, Target SDK dans le fichier build.gradle.
  2. Apportez les modifications suivantes:
  3. De compileSdkVersion à 30
  4. De targetSdkVersion à 30

Votre code doit se présenter comme suit:

android {
   TODO: Step 2.1, Target Android 10 and then Android 11.
   compileSdkVersion 30
   defaultConfig {
       applicationId "com.example.android.whileinuselocation"
       minSdkVersion 26
       targetSdkVersion 30
       versionCode 1
       versionName "1.0"
   }
...
}

Vous serez ensuite invité à synchroniser votre projet. Cliquez sur Synch Now (Synchroniser).

153f70847e0ec320.png

Ensuite, votre application est prête pour Android 11.

Exécuter l'application

Exécutez votre application à partir d'Android Studio et essayez de cliquer sur le bouton.

Tout devrait fonctionner comme avant, mais désormais sous Android 11. Si vous n'avez pas encore accepté les autorisations pour les établissements, l'écran d'autorisation devrait maintenant s'afficher.

73d8cc88c5877c25.png

cc98fac6e089bc4.png

7. Stratégies de localisation pour Android

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

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

Vous ne devez demander que 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'application, 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 un foregroundServiceType de "location" dans le fichier manifeste.
  • Ne demandez pas l'autorisation d'accéder à la position en arrière-plan, sauf si vous disposez d'un cas d'utilisation valide, comme décrit dans l'article Accès plus sûr et plus transparent à la position de l'utilisateur.

Permettre la dégradation progressive si l'autorisation n'est pas accordée

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

  • Votre application n'a pas accès aux informations de localisation.
  • Votre application n'a pas accès aux informations de localisation lorsqu'elle s'exécute en arrière-plan.

8. Félicitations

Vous avez appris à recevoir des notifications de position dans Android, en gardant à l'esprit les bonnes pratiques.

En savoir plus