Créer et surveiller des zones de géorepérage

Le géorepérage combine la localisation actuelle de l'utilisateur et la conscience de celui-ci d'être à proximité de lieux susceptibles de l'intéresser. Pour indiquer un lieu d'intérêt, indiquez sa latitude et sa longitude. Pour ajuster la proximité avec la localisation, ajoutez un rayon. La latitude, la longitude et le rayon définissent une zone de géorepérage en créant une zone circulaire autour du lieu d'intérêt.

Vous pouvez avoir plusieurs zones de géorepérage actives, avec une limite de 100 par application et par utilisateur d'appareil. Pour chaque zone de géorepérage, vous pouvez demander aux services de localisation de vous envoyer des événements d'entrée et de sortie ou définir une durée d'attente ou de temps d'arrêt dans la zone de géorepérage avant le déclenchement d'un événement. Vous pouvez limiter la durée de n'importe quelle zone de géorepérage en indiquant une durée d'expiration en millisecondes. Une fois la zone de géorepérage expirée, les services de localisation la suppriment automatiquement.

Cette leçon explique comment ajouter et supprimer des zones de géorepérage, puis comment écouter les transitions de géorepérage à l'aide d'un élément BroadcastReceiver.

Configurer le contrôle du géorepérage

La première étape pour demander un contrôle de géorepérage consiste à demander les autorisations nécessaires. Pour utiliser le géorepérage, votre application doit demander les éléments suivants :

Pour en savoir plus, consultez le guide sur la manière de demander des autorisations de géolocalisation.

Si vous souhaitez utiliser un élément BroadcastReceiver pour écouter les transitions de géorepérage, ajoutez un élément spécifiant le nom du service. Cet élément doit être un enfant de l'élément <application> :

<application
   android:allowBackup="true">
   ...
   <receiver android:name=".GeofenceBroadcastReceiver"/>
<application/>

Pour accéder aux API de localisation, vous devez créer une instance du client de géorepérage. Pour savoir comment connecter votre client :

Kotlin

lateinit var geofencingClient: GeofencingClient

override fun onCreate(savedInstanceState: Bundle?) {
    // ...
    geofencingClient = LocationServices.getGeofencingClient(this)
}

Java

private GeofencingClient geofencingClient;

@Override
public void onCreate(Bundle savedInstanceState) {
    // ...
    geofencingClient = LocationServices.getGeofencingClient(this);
}

Créer et ajouter des zones de géorepérage

Votre application doit créer et ajouter des zones de géorepérage à l'aide de la classe de compilateur de l'API de localisation pour créer des objets de géorepérage, et de la classe permettant de les ajouter. En outre, pour gérer les intents envoyés par les services de localisation lorsque des transitions de géorepérage se produisent, vous pouvez définir un élément PendingIntent, comme indiqué dans cette section.

Remarque : Les appareils à utilisateur unique sont limités à 100 zones de géorepérage par application. Pour les appareils à plusieurs utilisateurs, la limite est de 100 zones de géorepérage par application et par utilisateur d'appareil.

Créer des objets de géorepérage

Tout d'abord, utilisez Geofence.Builder pour créer une zone de géorepérage en définissant le rayon, la durée et les types de transition souhaités. Par exemple, pour remplir un objet de liste :

Kotlin

geofenceList.add(Geofence.Builder()
        // Set the request ID of the geofence. This is a string to identify this
        // geofence.
        .setRequestId(entry.key)

        // Set the circular region of this geofence.
        .setCircularRegion(
                entry.value.latitude,
                entry.value.longitude,
                Constants.GEOFENCE_RADIUS_IN_METERS
        )

        // Set the expiration duration of the geofence. This geofence gets automatically
        // removed after this period of time.
        .setExpirationDuration(Constants.GEOFENCE_EXPIRATION_IN_MILLISECONDS)

        // Set the transition types of interest. Alerts are only generated for these
        // transition. We track entry and exit transitions in this sample.
        .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER or Geofence.GEOFENCE_TRANSITION_EXIT)

        // Create the geofence.
        .build())

Java

geofenceList.add(new Geofence.Builder()
    // Set the request ID of the geofence. This is a string to identify this
    // geofence.
    .setRequestId(entry.getKey())

    .setCircularRegion(
            entry.getValue().latitude,
            entry.getValue().longitude,
            Constants.GEOFENCE_RADIUS_IN_METERS
    )
    .setExpirationDuration(Constants.GEOFENCE_EXPIRATION_IN_MILLISECONDS)
    .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER |
            Geofence.GEOFENCE_TRANSITION_EXIT)
    .build());

Cet exemple extrait des données d'un fichier de constantes. En pratique, les applications peuvent créer des zones de géorepérage de manière dynamique en fonction de la localisation de l'utilisateur.

Spécifier les zones de géorepérage et les déclencheurs initiaux

L'extrait de code suivant utilise la classe GeofencingRequest et sa classe GeofencingRequestBuilder imbriquée pour indiquer les zones de géorepérage à surveiller et définir comment les événements de géorepérage associés sont déclenchés :

Kotlin

private fun getGeofencingRequest(): GeofencingRequest {
    return GeofencingRequest.Builder().apply {
        setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER)
        addGeofences(geofenceList)
    }.build()
}

Java

private GeofencingRequest getGeofencingRequest() {
    GeofencingRequest.Builder builder = new GeofencingRequest.Builder();
    builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER);
    builder.addGeofences(geofenceList);
    return builder.build();
}

Cet exemple illustre l'utilisation de deux déclencheurs de géorepérage. La transition GEOFENCE_TRANSITION_ENTER se déclenche lorsqu'un appareil accède à une zone de géorepérage, et la transition GEOFENCE_TRANSITION_EXIT se déclenche lorsqu'un appareil quitte une zone de géorepérage. En spécifiant INITIAL_TRIGGER_ENTER, vous indiquez aux services de localisation que GEOFENCE_TRANSITION_ENTER doit être déclenché si l'appareil se trouve déjà dans la zone de géorepérage.

Dans de nombreux cas, il peut être préférable d'utiliser plutôt INITIAL_TRIGGER_DWELL, qui ne déclenche des événements que lorsque l'utilisateur s'arrête pendant une durée définie dans une zone de géorepérage. Cette approche permet de réduire les "fausses alertes" résultant de nombreuses notifications lorsqu'un appareil entre et sort brièvement dans une zone de géorepérage. Pour obtenir les meilleurs résultats possibles avec vos zones de géorepérage, vous pouvez également définir un rayon minimal de 100 mètres. Cela permet de tenir compte de la précision de la localisation des réseaux Wi-Fi classiques et de réduire la consommation d'énergie de l'appareil.

Définir un broadcast receiver pour les transitions de géorepérage

Un élément Intent envoyé depuis les services de localisation peut déclencher diverses actions dans votre application, mais vous ne devez pas lui demander de démarrer une activité ou un fragment, car les composants ne doivent être visibles qu'en réponse à une action de l'utilisateur. Dans de nombreux cas, un élément BroadcastReceiver est un bon moyen de gérer une transition de géorepérage. Un élément BroadcastReceiver reçoit des mises à jour lorsqu'un événement se produit, par exemple une transition à l'intérieur ou à l'extérieur d'une zone de géorepérage, et peut démarrer une opération d'arrière-plan de longue durée.

L'extrait de code suivant montre comment définir un élément PendingIntent qui lance un élément BroadcastReceiver :

Kotlin

class MainActivity : AppCompatActivity() {

    // ...

    private val geofencePendingIntent: PendingIntent by lazy {
        val intent = Intent(this, GeofenceBroadcastReceiver::class.java)
        // We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when calling
        // addGeofences() and removeGeofences().
        PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
    }
}

Java

public class MainActivity extends AppCompatActivity {

    // ...

    private PendingIntent getGeofencePendingIntent() {
        // Reuse the PendingIntent if we already have it.
        if (geofencePendingIntent != null) {
            return geofencePendingIntent;
        }
        Intent intent = new Intent(this, GeofenceBroadcastReceiver.class);
        // We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when
        // calling addGeofences() and removeGeofences().
        geofencePendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.
                FLAG_UPDATE_CURRENT);
        return geofencePendingIntent;
    }

Ajouter des zones de géorepérage

Pour ajouter des zones de géorepérage, utilisez la méthode GeofencingClient.addGeofences(). Fournissez l'objet GeofencingRequest et l'élément PendingIntent. L'extrait de code suivant illustre le traitement des résultats :

Kotlin

geofencingClient?.addGeofences(getGeofencingRequest(), geofencePendingIntent)?.run {
    addOnSuccessListener {
        // Geofences added
        // ...
    }
    addOnFailureListener {
        // Failed to add geofences
        // ...
    }
}

Java

geofencingClient.addGeofences(getGeofencingRequest(), getGeofencePendingIntent())
        .addOnSuccessListener(this, new OnSuccessListener<Void>() {
            @Override
            public void onSuccess(Void aVoid) {
                // Geofences added
                // ...
            }
        })
        .addOnFailureListener(this, new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
                // Failed to add geofences
                // ...
            }
        });

Gérer les transitions de géorepérage

Lorsque les services de localisation détectent que l'utilisateur est entré ou a quitté une zone de géorepérage, l'élément Intent inclus dans l'élément PendingIntent que vous avez associé à la demande d'ajout de zones de géorepérage est envoyé. Un broadcast receiver tel que GeofenceBroadcastReceiver remarque que l'élément Intent a été appelé et peut ensuite obtenir l'événement de géorepérage à partir de l'intent, déterminer le type de transition(s) de géorepérage et repérer quelles zones de géorepérage définies ont été déclenchées. Le broadcast receiver peut demander à une application d'effectuer des tâches en arrière-plan ou, si vous le souhaitez, envoyer une notification en sortie.

Remarque : Sur Android version 8.0 (niveau 26 d'API) ou ultérieure, si une application s'exécute en arrière-plan pendant qu'elle surveille une zone de géorepérage, l'appareil répond aux événements de géorepérage toutes les quelques minutes. Pour savoir comment adapter votre application à ces limites de réponse, consultez les limites de localisation en arrière-plan.

L'extrait de code suivant montre comment définir un BroadcastReceiver qui publie une notification lors d'une transition de géorepérage. Lorsque l'utilisateur clique sur la notification, l'activité principale de l'application s'affiche :

Kotlin

class GeofenceBroadcastReceiver : BroadcastReceiver() {
    // ...
    override fun onReceive(context: Context?, intent: Intent?) {
        val geofencingEvent = GeofencingEvent.fromIntent(intent)
        if (geofencingEvent.hasError()) {
            val errorMessage = GeofenceStatusCodes
                    .getStatusCodeString(geofencingEvent.errorCode)
            Log.e(TAG, errorMessage)
            return
        }

        // Get the transition type.
        val geofenceTransition = geofencingEvent.geofenceTransition

        // Test that the reported transition was of interest.
        if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER |
                geofenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT) {

            // Get the geofences that were triggered. A single event can trigger
            // multiple geofences.
            val triggeringGeofences = geofencingEvent.triggeringGeofences

            // Get the transition details as a String.
            val geofenceTransitionDetails = getGeofenceTransitionDetails(
                    this,
                    geofenceTransition,
                    triggeringGeofences
            )

            // Send notification and log the transition details.
            sendNotification(geofenceTransitionDetails)
            Log.i(TAG, geofenceTransitionDetails)
        } else {
            // Log the error.
            Log.e(TAG, getString(R.string.geofence_transition_invalid_type,
                    geofenceTransition))
        }
    }
}

Java

public class GeofenceBroadcastReceiver extends BroadcastReceiver {
    // ...
    protected void onReceive(Context context, Intent intent) {
        GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
        if (geofencingEvent.hasError()) {
            String errorMessage = GeofenceStatusCodes
                    .getStatusCodeString(geofencingEvent.getErrorCode());
            Log.e(TAG, errorMessage);
            return;
        }

        // Get the transition type.
        int geofenceTransition = geofencingEvent.getGeofenceTransition();

        // Test that the reported transition was of interest.
        if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER ||
                geofenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT) {

            // Get the geofences that were triggered. A single event can trigger
            // multiple geofences.
            List<Geofence> triggeringGeofences = geofencingEvent.getTriggeringGeofences();

            // Get the transition details as a String.
            String geofenceTransitionDetails = getGeofenceTransitionDetails(
                    this,
                    geofenceTransition,
                    triggeringGeofences
            );

            // Send notification and log the transition details.
            sendNotification(geofenceTransitionDetails);
            Log.i(TAG, geofenceTransitionDetails);
        } else {
            // Log the error.
            Log.e(TAG, getString(R.string.geofence_transition_invalid_type,
                    geofenceTransition));
        }
    }
}

Après avoir détecté l'événement de transition via PendingIntent, BroadcastReceiver obtient le type de transition de géorepérage et vérifie s'il s'agit de l'un des événements que l'application utilise pour déclencher des notifications, dans ce cas-ci soit GEOFENCE_TRANSITION_ENTER, soit GEOFENCE_TRANSITION_EXIT. Le service envoie ensuite une notification et consigne les détails de la transition.

Arrêter la surveillance des zones de géorepérage

En arrêtant la surveillance des zones de géorepérage lorsque vous n'en avez plus besoin ou envie, vous pouvez économiser de la batterie et des cycles de processeur de l'appareil. Vous pouvez arrêter la surveillance des zones de géorepérage dans l'activité principale permettant d'ajouter et de supprimer des zones de géorepérage. La suppression d'une zone de géorepérage l'arrête immédiatement. L'API propose des méthodes pour supprimer les zones de géorepérage via les identifiants de requête ou en supprimant les zones de géorepérage associées à un élément PendingIntent spécifique.

L'extrait de code suivant supprime les zones de géorepérage de PendingIntent, arrêtant toutes les notifications lorsque l'appareil accède à ces zones précédemment ajoutées ou les quitte :

Kotlin

geofencingClient?.removeGeofences(geofencePendingIntent)?.run {
    addOnSuccessListener {
        // Geofences removed
        // ...
    }
    addOnFailureListener {
        // Failed to remove geofences
        // ...
    }
}

Java

geofencingClient.removeGeofences(getGeofencePendingIntent())
        .addOnSuccessListener(this, new OnSuccessListener<Void>() {
            @Override
            public void onSuccess(Void aVoid) {
                // Geofences removed
                // ...
            }
        })
        .addOnFailureListener(this, new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
                // Failed to remove geofences
                // ...
            }
        });

Vous pouvez combiner le géorepérage avec d'autres fonctionnalités basées sur la localisation, telles que des notifications de position régulières. Pour en savoir plus, consultez les autres leçons de ce cours.

Bonnes pratiques de géorepérage

Cette section présente les bonnes pratiques d'utilisation du géorepérage avec les API de localisation pour Android.

Réduire la consommation d'énergie

Vous pouvez utiliser les techniques suivantes pour optimiser la consommation d'énergie de vos applications utilisant le géorepérage :

  • Définissez la réactivité des notifications sur une valeur plus élevée. Cela permet d'optimiser la consommation d'énergie en augmentant la latence des alertes de géorepérage. Par exemple, si vous définissez une valeur de réactivité de cinq minutes, votre application ne recherche les alertes d'entrée ou de sortie qu'une fois toutes les cinq minutes. Définir des valeurs inférieures ne signifie pas nécessairement que les utilisateurs sont avertis dans ce délai (par exemple, si vous définissez une valeur de 5 secondes, la réception de l'alerte peut prendre un peu plus de temps).

  • Utilisez un rayon de géorepérage plus étendu dans les zones où les utilisateurs passent beaucoup de temps (domicile ou travail, par exemple). Un rayon plus grand ne réduit pas directement la consommation d'énergie, mais la fréquence à laquelle l'application recherche l'entrée ou la sortie est réduite, une manière efficace de diminuer la consommation globale d'énergie.

Choisir le rayon optimal de géorepérage

Pour un résultat optimal, le rayon minimal de la zone de géorepérage doit être compris entre 100 et 150 mètres. Lorsque le Wi-Fi est disponible, la précision de la localisation est généralement comprise entre 20 et 50 mètres. Lorsque la localisation intérieure est disponible, la portée du champ de précision peut être inférieure à 5 mètres. À moins de savoir que la position en intérieur est disponible dans la zone de géorepérage, partez du principe que la précision de la localisation Wi-Fi est d'environ 50 mètres.

Si la localisation Wi-Fi n'est pas disponible (par exemple, lorsque vous conduisez dans une zone rurale), la précision de la localisation diminue. La portée de précision peut aller de plusieurs centaines de mètres à plusieurs kilomètres. Créez alors des zones de géorepérage avec un rayon plus large.

Expliquer aux utilisateurs pourquoi votre application utilise le géorepérage

Étant donné que votre application accède aux données de localisation en arrière-plan lorsque vous utilisez le géorepérage, tenez compte des avantages qu'elle offre aux utilisateurs. Par souci de compréhension et de transparence, expliquez clairement aux utilisateurs pourquoi votre application a besoin de cette autorisation.

Pour en savoir plus sur les bonnes pratiques concernant l'accès aux données de localisation, y compris le géorepérage, consultez la page Bonnes pratiques en matière de confidentialité.

Utiliser le type de transition "Temps d'arrêt" pour réduire les fausses alertes

Si vous recevez un grand nombre d'alertes après avoir traversé brièvement une zone de géorepérage, le meilleur moyen de réduire ces alertes consiste à utiliser le type de transition GEOFENCE_TRANSITION_DWELL au lieu de GEOFENCE_TRANSITION_ENTER. L'alerte de temps d'arrêt n'est alors envoyée que si l'utilisateur s'attarde dans une zone de géorepérage pendant une période donnée. Vous pouvez choisir la durée en définissant un délai de temps d'arrêt.

Réenregistrer les zones de géorepérage uniquement si nécessaire

Les zones de géorepérage enregistrées sont conservées dans le processus com.google.process.location appartenant au package com.google.android.gms. L'application n'a rien à faire pour gérer les événements suivants, car le système restaure les géorepérages après ces événements :

  • Mise à niveau des services Google Play ;
  • Suppression des services Google Play et redémarrage du système en raison de restrictions sur les ressources ;
  • Plantage du processus de localisation.

L'application doit réenregistrer les zones de géorepérage si elles sont toujours nécessaires après les événements suivants, car le système ne peut pas les restaurer dans les cas suivants :

  • Redémarrage de l'appareil. L'application doit écouter la fin du démarrage de l'appareil, puis réenregistrer les zones de géorepérage requises ;
  • Désinstallation et réinstallation de l'application ;
  • Effacement des données de l'application ;
  • Effacement des données des services Google Play ;
  • Réception d'une alerte GEOFENCE_NOT_AVAILABLE par l'application. Cela se produit généralement après la désactivation du TLN (fournisseur de localisation du réseau Android).

Résoudre les problèmes d'entrée de géorepérage

Si les zones de géorepérage ne se déclenchent pas lorsque l'appareil entre dans une zone (l'alerte GEOFENCE_TRANSITION_ENTER ne se déclenche pas), assurez-vous d'abord que vos zones de géorepérage sont correctement enregistrées, comme décrit dans ce guide.

Plusieurs raisons peuvent expliquer que les alertes ne fonctionnent pas comme prévu :

  • La localisation exacte à l'intérieur de votre zone de géorepérage n'est pas disponible ou celle-ci est trop restreinte. Sur la plupart des appareils, le service de géorepérage n'utilise que la localisation réseau pour déclencher des zones de géorepérage. Le service utilise cette approche, car la localisation réseau est bien moins énergivore. Les localisations discrètes sont plus rapides à obtenir et, surtout, elles sont disponibles en intérieur.
  • Le Wi-Fi est désactivé sur l'appareil. L'activation du Wi-Fi peut améliorer considérablement la précision de la localisation. Si le Wi-Fi est désactivé, votre application risque donc de ne jamais recevoir d'alertes de géorepérage en fonction de plusieurs paramètres, comme le rayon de la zone de géorepérage, le modèle de l'appareil ou la version d'Android. À partir d'Android 4.3 (niveau 18 d'API), nous avons ajouté la fonction "Mode de recherche Wi-Fi uniquement", qui permet aux utilisateurs de désactiver le Wi-Fi tout en bénéficiant d'une bonne localisation sur le réseau. Il est recommandé d'inviter l'utilisateur et de lui fournir un raccourci lui permettant d'activer le mode Wi-Fi ou le mode de recherche Wi-Fi uniquement si les deux sont désactivés. Utilisez SettingsClient pour vous assurer que les paramètres système de l'appareil sont correctement configurés afin d'optimiser la détection de la localisation.

    Remarque : Si votre application cible Android version 10 (niveau 29 d'API) ou ultérieure, vous ne pouvez pas appeler WifiManager.setEnabled() directement, sauf s'il s'agit d'une application système ou d'un outil de contrôle des règles relatives aux appareils (DPC). Utilisez plutôt un panneau de paramètres.

  • Il n'y a pas de connectivité réseau fiable dans votre zone de géorepérage. En l'absence de connexion de données fiable, les alertes risquent de ne pas être générées. En effet, le service de géorepérage dépend du fournisseur de localisation réseau, qui lui-même requiert une connexion de données.
  • Les alertes peuvent se déclencher trop tard. Le service de géorepérage ne demande pas en permanence la localisation. Vous devez donc vous attendre à une certaine latence lorsque vous recevez des alertes. En général, la latence est inférieure à 2 minutes, voire moins si l'appareil est en mouvement. Si des limites de localisation en arrière-plan s'appliquent, la latence est d'environ 2 à 3 minutes en moyenne. Si l'appareil est à l'arrêt pendant une période prolongée, la latence peut augmenter (jusqu'à 6 minutes).

Ressources supplémentaires

Pour en savoir plus sur le géorepérage, consultez les ressources suivantes :

Exemples

Application exemple pour la création et la surveillance des zones de géorepérage