Créez votre propre application Uber/Lyft avec suivi de géolocalisation

PubNub Developer Relations - Feb 22 - - Dev Community

Dans cet article tiré de nos archives, nous expliquons comment créer votre propre clone d'Uber ou de Lyft, en utilisant PubNub pour suivre la position du véhicule. Bien que certains extraits de code fassent référence à d'anciennes versions de la bibliothèque, tout ce qui figure dans cet article est approprié pour le public d'aujourd'hui.

Le suivi de la position en temps réel d'un autre appareil est à la base de nombreuses applications mobiles populaires axées sur la localisation, en particulier lorsqu'il s'agit de construire des clones de Lyber ou de Lyft.

comme Lyft et Uber. Construire cette fonctionnalité est un défi et peut être fastidieux, mais nous allons le simplifier avec un modèle facile de publication/abonnement entre les conducteurs et les passagers, alimenté par PubNub.

Vue d'ensemble du tutoriel

Lecode source complet de ce tutoriel est disponible ici.

Dans ce tutoriel, nous allons vous montrer comment mettre en œuvre ce cas d'utilisation courant dans une application Android. L'exemple d'application que nous allons créer demande d'abord à l'utilisateur s'il est le conducteur ou le passager. Si l'utilisateur est le conducteur, sa position actuelle sera publiée sur un canal, mise à jour toutes les 5 secondes (dans cet exemple), ou le nombre de fois requis par votre application.

Si un utilisateur est un passager, il s'abonnera au même canal pour recevoir des mises à jour sur la position actuelle de son conducteur. Ce modèle de publication/abonnement est illustré dans le graphique ci-dessous.

Uber/Lyft App

API Google Maps et configuration de PubNub

Nous devons d'abord écrire nos dépendances Gradle pour utiliser le SDK Android PubNub et les services Google Play pour la fonctionnalité de carte. Nous allons également implémenter lesbibliothèques

com.fasterxml.jackson
Enter fullscreen mode Exit fullscreen mode

, afin de réaliser une analyse JSON de base des messages PubNub .

Dans le fichier build.gradle pour le module app sous dependencies, entrez le codeblock de la commande ci-dessous pour vous assurer que nous avons ce dont nous avons besoin. Assurez-vous que Google Play Services est installé à partir du gestionnaire SDK Android dans la partie outils du menu, afin que nous puissions implémenter les bibliothèques nécessaires.

implementation group: 'com.pubnub', name: 'pubnub-gson', version: '4.12.0'
implementation 'com.google.android.gms:play-services-maps:15.0.1'
implementation "com.google.android.gms:play-services-location:15.0.1"
implementation group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.9.2'
implementation group: 'com.fasterxml.jackson.core', name: 'jackson-annotations', version: '2.9.2'
implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.9.2'
Enter fullscreen mode Exit fullscreen mode

Maintenant que nous avons les dépendances nécessaires, assurons-nous que nous avons les permissions nécessaires dans notre fichier Android Manifest. Avec la permission d'accéder à l'état du réseau et d'établir une connexion internet, l'utilisateur peut maintenant se connecter à l'API PubNub. Nous utiliserons la permission de localisation plus tard, lorsque nous demanderons la localisation du conducteur.

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
Enter fullscreen mode Exit fullscreen mode

Afin de configurer PubNub dans votre application Android, vous devez créer une application PubNub dans le PubNub Admin Dashboard (c'est gratuit). Lors de la création de l'application, une clé de souscription et une clé de publication vous seront attribuées. Dans la partie suivante, nous utiliserons ces informations d'identification dans notre classe MainActivity pour établir la connexion.

Nous devons maintenant configurer notre clé API Google Maps, afin de pouvoir l'utiliser dans notre application. Pour ce faire, rendez-vous dans la console Developer API de Google. Nous y créerons un nouveau projet, puis nous générerons une clé API. Maintenant que nous avons notre clé API, nous pouvons écrire le code suivant pour mettre en place notre API Google Maps et la configurer avec notre clé API générée.

<meta-data
   android:name="com.google.android.gms.version"
   android:value="@integer/google_play_services_version" />
<meta-data
   android:name="com.google.android.geo.API_KEY"
   android:value=ENTER_KEY_HERE" />
Enter fullscreen mode Exit fullscreen mode

Activité principale

La classe MainActivity aura trois fonctionnalités importantes :

  1. Diriger l'utilisateur vers son interface respective, selon qu'il est conducteur ou passager.
  2. Établir une connexion à PubNub pour tous les utilisateurs de l'application.
  3. Vérifier les autorisations d'accès à la localisation

Tout d'abord, nous voulons séparer nos utilisateurs en conducteurs et passagers et leur fournir leur interface utilisateur respective. Pour ce faire, nous allons créer une classe DriverActivity et une classe PassengerActivity, dont nous parlerons en détail dans les parties 2 et 3.

Dans MainActivity, nous créerons une interface simple basée sur un bouton qui invitera l'utilisateur à indiquer à l'application son rôle en tant que conducteur ou passager. Grâce à la classe Intent d'Android, nous pouvons passer de la classe MainActivity à la classe DriverActivity ou de la classe MainActivity à la classe PassengerActivity, en fonction du bouton sur lequel l'utilisateur a appuyé.

L'interface se présentera comme suit :

screenshot

driverButton.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View v) {
       startActivity(new Intent(MainActivity.this, DriverActivity.class));
   }
});
passengerButton.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View v) {
       startActivity(new Intent(MainActivity.this, PassengerActivity.class));
   }
});
Enter fullscreen mode Exit fullscreen mode

Ensuite, en utilisant nos identifiants PubNub du tableau de bord d'administration, nous allons créer une instance

PNConfiguration
Enter fullscreen mode Exit fullscreen mode

, qui nous permet de créer une instance

PubNub
Enter fullscreen mode Exit fullscreen mode

connectant notre application et PubNub. Cette instance PubNub dans la classe MainActivity sera publique et statique afin d'être accessible à la fois depuis DriverActivity et PassengerActivity. Nous appellerons la méthode ci-dessous dans notre méthode

onCreate()
Enter fullscreen mode Exit fullscreen mode

de l'activité principale, afin que PubNub soit correctement initialisé pour tous les utilisateurs de notre application. Tout ceci est illustré dans le code suivant.

private void initPubnub() {
        PNConfiguration pnConfiguration = new PNConfiguration();
        pnConfiguration.setSubscribeKey(Constants.PUBNUB_SUBSCRIBE_KEY);
        pnConfiguration.setPublishKey(Constants.PUBNUB_PUBLISH_KEY);
        pnConfiguration.setSecure(true);
        pubnub = new PubNub(pnConfiguration);
    }
Enter fullscreen mode Exit fullscreen mode

Enfin, nous devons nous assurer que notre utilisateur a activé ses services de localisation pour notre application. Cela peut être fait avec le code suivant. Si l'utilisateur ne donne pas l'autorisation d'accéder à la localisation fine ou à la localisation grossière, notre application demandera cette autorisation.

public void checkPermission() {
   if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED ||
           ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED
           ) {//Can add more as per requirement
       ActivityCompat.requestPermissions(this,
               new String[]{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION},
               123);
   }
}
Enter fullscreen mode Exit fullscreen mode

Activité du conducteur

La seule fonction de la classe DriverActivity est d'obtenir la position du conducteur et de publier ces données à un certain intervalle de temps sur le canal PubNub.

Tout d'abord, voyons comment effectuer une demande de localisation. Nous utiliserons laclasse

FusedLocationProviderClient
Enter fullscreen mode Exit fullscreen mode

de l'API de localisation de Google afin de demander au conducteur des mises à jour de sa position. Nous utiliserons également la classe

LocationRequest
Enter fullscreen mode Exit fullscreen mode

pour spécifier les paramètres importants de notre demande de localisation, tels que la priorité, le plus petit déplacement et l'intervalle de temps.

Pour notre exemple, nous utiliserons un intervalle de temps de 5000 millisecondes, de sorte que la localisation soit mise à jour toutes les 5 secondes, et un plus petit déplacement de 10 mètres pour s'assurer que la localisation n'est mise à jour que s'il y a un changement d'au moins 10 mètres. Enfin, nous voulons que la demande de localisation utilise le GPS de l'appareil mobile, pour un suivi de localisation de haute précision. Cette précision est importante pour permettre au passager de voir un affichage détaillé de son conducteur (représenté par l'icône de la voiture) se déplaçant sur la carte jusqu'à sa destination.

LocationRequest locationRequest = LocationRequest.create();
locationRequest.setInterval(5000);
locationRequest.setFastestInterval(5000);
locationRequest.setSmallestDisplacement(10);
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
Enter fullscreen mode Exit fullscreen mode

Maintenant que nous avons défini nos paramètres de demande de localisation, nous allons passer cet objet LocationRequest à notre objet FusedLocationProviderClient pour les mises à jour de localisation.

mFusedLocationClient.requestLocationUpdates(locationRequest, new LocationCallback() {
   @Override
   public void onLocationResult(LocationResult locationResult) {
       Location location = locationResult.getLastLocation();
...
Enter fullscreen mode Exit fullscreen mode

En utilisant l'emplacement que nous obtenons de notre demande, nous devons le convertir en un LinkedHashMap afin qu'il corresponde au format souhaité d'un message d'emplacement dans le canal PubNub. Le LinkedHashMap aura deux clés "lat" et "lng" et les valeurs correspondantes pour ces clés, toutes deux sous forme de chaînes.

Maintenant que notre message est au format LinkedHashMap, nous pouvons le publier sur le canal PubNub. C'est ce que montre le code suivant. Notez que nous devons utiliser

MainActivity.pubnub
Enter fullscreen mode Exit fullscreen mode

pour obtenir l'instance PubNub que nous avons précédemment dans la méthode onCreate de l'activité principale. Nous ne voulons pas établir plusieurs connexions en initialisant à nouveau PubNub. Au lieu de cela, nous utilisons simplement celle que nous avons déjà créée dans la classe MainActivity.

MainActivity.pubnub.publish()
       .message(message)
       .channel(Constants.PUBNUB_CHANNEL_NAME)
       .async(new PNCallback<PNPublishResult>() {
           @Override
           public void onResponse(PNPublishResult result, PNStatus status) {
               // handle publish result, status always present, result if successful
               // status.isError() to see if error happened
               if (!status.isError()) {
                   System.out.println("pub timetoken: " + result.getTimetoken());
               }
               System.out.println("pub status code: " + status.getStatusCode());
           }
       });
Enter fullscreen mode Exit fullscreen mode

Activité Passager

Dans notre classe PassengerActivity, nous voulons créer une vue de carte pour montrer l'emplacement du conducteur mis à jour. Pour ajouter l'élément MapView à notre activité Passenger, nous devons inclure le code suivant dans le fichier layout.xml correspondant à cette activité.

<fragment xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
   android:id="@+id/map"
   android:name="com.google.android.gms.maps.SupportMapFragment"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   tools:context="com.example.mapwithmarker.MapsMarkerActivity" />
Enter fullscreen mode Exit fullscreen mode

Ensuite, instanciez l'objet

MapFragment
Enter fullscreen mode Exit fullscreen mode

en utilisant l'ID que nous avons défini dans le fichier de mise en page. Dans notre exemple de code, nous avons utilisé "map" comme ID. Nous allons également mettre en place notre méthode

onMapReady
Enter fullscreen mode Exit fullscreen mode

en demandant à PassengerActivity d'implémenter

onMapReadyCallback
Enter fullscreen mode Exit fullscreen mode

. Nous transmettrons ensuite

PassengerActivity.this
Enter fullscreen mode Exit fullscreen mode

en tant que paramètre à la méthode

getMapAsync
Enter fullscreen mode Exit fullscreen mode

de notre objet MapFragment .Cela nous permet de configurer notre méthode de rappel onMapReady pour notre objet MapFragment.

mMapFragment = (SupportMapFragment) getSupportFragmentManager()
       .findFragmentById(R.id.map);
mMapFragment.getMapAsync(PassengerActivity.this);
Enter fullscreen mode Exit fullscreen mode

Nous voulons maintenant nous abonner au canal de localisation du conducteur afin de recevoir des mises à jour sur sa position actuelle. Cela doit être fait une fois que la carte est prête, nous inclurons donc ce code dans notre méthode de rappel. Deplus, nous devons ajouter un listener avec un

SubscribeCallback
Enter fullscreen mode Exit fullscreen mode

, afin que notre application sache comment agir lors de la réception d'un message .

Comme spécifié dans la documentation PubNub Android, nous devons effectuer ceci dans l'ordre où nous ajoutons d'abord le listener et ensuite souscrivons l'instance PubNub au canal. Une fois que nous avons reçu un message, nous voulons convertir la sortie JSON en un LinkedHashMap, que nous pouvons facilement analyser pour obtenir la longitude et la latitude. Pourconvertir la sortie JSON en LinkedHashMap, nous devons importer la classe

JsonUtil
Enter fullscreen mode Exit fullscreen mode

, qui contient la méthode

fromJson()
Enter fullscreen mode Exit fullscreen mode

qui nous permet de le faire.Cette classe est présentée dans le code source complet. Une fois que nous avons la position du conducteur, nous devons mettre à jour l'interface utilisateur en appelant notre méthode

updateUI()
Enter fullscreen mode Exit fullscreen mode

et en passant la nouvelle position en paramètre .

MainActivity.pubnub.addListener(new SubscribeCallback() {
    @Override
    public void status(PubNub pub, PNStatus status) {
    }
    @Override
    public void message(PubNub pub, final PNMessageResult message) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                try {
                    Map<String, String> newLocation = JsonUtil.fromJson(message.getMessage().toString(), LinkedHashMap.class);
                    updateUI(newLocation);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }
    @Override
    public void presence(PubNub pub, PNPresenceEventResult presence) {
    }
});
MainActivity.pubnub.subscribe()
        .channels(Arrays.asList(Constants.PUBNUB_CHANNEL_NAME)) // subscribe to channels
        .execute();
Enter fullscreen mode Exit fullscreen mode

Notre méthode updateUI rencontre deux cas de figure.

Dans lepremier cas, il n'existe pas encore de marqueur pour le conducteur. Dans ce cas, nous instancions l'objet

Marker
Enter fullscreen mode Exit fullscreen mode

et définissons sa position sur le premier emplacement suivi du conducteur. Nous devons également effectuer un zoom avant sur la carte afin de conserver une vue de la voiture du conducteur.

Dans le second cas, il existe déjà un marqueur pour le conducteur et, dans ce cas, il n'est pas nécessaire de réinstancier l'objet Marqueur. Nous déplaçons simplement l'emplacement du marqueur sur la carte vers son nouvel emplacement. Pour cefaire, nous appelons la méthode

animateCar()
Enter fullscreen mode Exit fullscreen mode

. Nous devons également nous assurer que nous déplaçons la caméra du MapView vers le nouvel emplacement, s'il est hors des limites de la caméra. Ainsi, le marqueur du conducteur sera toujours dans les limites du MapView. C'est ce que montre le code suivant.

private void updateUI(Map<String, String> newLoc) {
        LatLng newLocation = new LatLng(Double.valueOf(newLoc.get("lat")), Double.valueOf(newLoc.get("lng")));
        if (driverMarker != null) {
            animateCar(newLocation);
            boolean contains = mGoogleMap.getProjection()
                    .getVisibleRegion()
                    .latLngBounds
                    .contains(newLocation);
            if (!contains) {
                mGoogleMap.moveCamera(CameraUpdateFactory.newLatLng(newLocation));
            }
        } else {
            mGoogleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(
                    newLocation, 15.5f));
            driverMarker = mGoogleMap.addMarker(new MarkerOptions().position(newLocation).
                    icon(BitmapDescriptorFactory.fromResource(R.drawable.car)));
        }
    }
Enter fullscreen mode Exit fullscreen mode

Dans notre méthode animateCar, nous devons déplacer notre marqueur de voiture vers son nouvel emplacement, d'une manière fluide et en forme de ligne. L'animation doit durer 5 secondes, car notre application reçoit des mises à jour de la position du conducteur toutes les 5 secondes. Ainsi, chaque fois que l'animation est terminée, la nouvelle position est mise à jour pour lancer l'animation suivante. De cette façon, nous minimisons le décalage dans l'animation de la voiture.

Nous utiliserons également l'interface

LatLngInterpolator
Enter fullscreen mode Exit fullscreen mode

ci-dessous. Celle-ci nous permet d'implémenter la méthode

interpolate()
Enter fullscreen mode Exit fullscreen mode

qui renvoie une nouvelle coordonnée LatLng correspondant à une fraction de la nouvelle position finale. Cette fraction est déterminée par la méthode

getAnimatedFraction()
Enter fullscreen mode Exit fullscreen mode

. Nous appellerons cette méthode à partir d'une instance

ValueAnimator
Enter fullscreen mode Exit fullscreen mode

que nous obtiendrons grâce à la méthode

onAnimationUpdate()
Enter fullscreen mode Exit fullscreen mode

méthode de rappel. Cette fraction augmentera à un rythme régulier de sorte qu'au bout de 5 secondes, elle sera de 1 (jusqu'au nouvel emplacement). Cette animation de la voiture est illustrée dans les extraits de code suivants.

private void animateCar(final LatLng destination) {
        final LatLng startPosition = driverMarker.getPosition();
        final LatLng endPosition = new LatLng(destination.latitude, destination.longitude);
        final LatLngInterpolator latLngInterpolator = new LatLngInterpolator.LinearFixed();
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1);
        valueAnimator.setDuration(5000); // duration 5 seconds
        valueAnimator.setInterpolator(new LinearInterpolator());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                try {
                    float v = animation.getAnimatedFraction();
                    LatLng newPosition = latLngInterpolator.interpolate(v, startPosition, endPosition);
                    driverMarker.setPosition(newPosition);
                } catch (Exception ex) {
                }
            }
        });
        valueAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
            }
        });
        valueAnimator.start();
    }
Enter fullscreen mode Exit fullscreen mode
private interface LatLngInterpolator {
       LatLng interpolate(float fraction, LatLng a, LatLng b);
       class LinearFixed implements LatLngInterpolator {
           @Override
           public LatLng interpolate(float fraction, LatLng a, LatLng b) {
               double lat = (b.latitude - a.latitude) * fraction + a.latitude;
               double lngDelta = b.longitude - a.longitude;
               if (Math.abs(lngDelta) > 180) {
                   lngDelta -= Math.signum(lngDelta) * 360;
               }
               double lng = lngDelta * fraction + a.longitude;
               return new LatLng(lat, lng);
           }
       }
   }
Enter fullscreen mode Exit fullscreen mode

Améliorations supplémentaires

Si vous souhaitez développer davantage cet exemple, vous pourriez ajouter une fonctionnalité qui consisterait à faire tourner le marqueur en même temps que la voiture. Pour ce faire, nous devrions ajouter une paire clé-valeur à la structure de notre message. Nous devrions inclure la clé "bearing" et la valeur. Il suffirait ensuite de faire pivoter le marqueur en fonction de l'orientation du conducteur.

Félicitations !

Maintenant que nous avons créé les activités MainActivity, DriverActivity et PassengerActivity, nous avons réussi à construire le modèle publish/subscribe nécessaire à la création d'une démo Android Lyft/Uber simple. Cliquez ici pour accéder au code source complet.

Si le contenu intégré n'est pas disponible sur cette page, il peut également être consulté à l'adresse https://www.youtube.com/embed/kQ6EzqoQu5A.

Plus de ressources

Comment PubNub peut-il vous aider ?

Cet article a été publié à l'origine sur PubNub.com

Notre plateforme aide les développeurs à construire, livrer et gérer l'interactivité en temps réel pour les applications web, les applications mobiles et les appareils IoT.

La base de notre plateforme est le réseau de messagerie en temps réel le plus grand et le plus évolutif de l'industrie. Avec plus de 15 points de présence dans le monde, 800 millions d'utilisateurs actifs mensuels et une fiabilité de 99,999 %, vous n'aurez jamais à vous soucier des pannes, des limites de concurrence ou des problèmes de latence causés par les pics de trafic.

Découvrez PubNub

Découvrez le Live Tour pour comprendre les concepts essentiels de chaque application alimentée par PubNub en moins de 5 minutes.

S'installer

Créez un compte PubNub pour un accès immédiat et gratuit aux clés PubNub.

Commencer

La documentation PubNub vous permettra de démarrer, quel que soit votre cas d'utilisation ou votre SDK.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .