Cómo comenzar a usar el SDK del controlador para Android

Puedes usar el SDK del controlador para proporcionar una navegación y un seguimiento mejorados a tu aplicación de Progreso de viajes y pedidos. El SDK de Driver proporciona la ubicación del vehículo y actualizaciones de tareas para el motor de soluciones On-demand Rides and Deliveries Solution Fleet Engine.

El SDK de Driver mantiene los servicios de Fleet Engine y tus servicios personalizados al tanto de la ubicación y el estado del vehículo. Por ejemplo, el vehículo puede ser ONLINE o OFFLINE, y su ubicación cambia a medida que avanza un viaje.

Requisitos mínimos del sistema

El dispositivo móvil debe ejecutar Android 6.0 (nivel de API 23) o una versión posterior.

Configuración de compilaciones y dependencias

Las versiones 4.99 y posteriores del SDK de Driver están disponibles en el repositorio de Maven de Google.

Gradle

Agrega lo siguiente a tu archivo build.gradle:

repositories {
    ...
    google()
}

Maven

Agrega lo siguiente a tu archivo pom.xml:

<project>
  ...
  <repositories>
    <repository>
      <id>google-maven-repository</id>
      <url>https://maven.google.com</url>
    </repository>
  </repositories>
  ...
</project>

Configuración del proyecto

Para usar el SDK del controlador, tu app debe orientarse a minSdkVersion 23 o versiones posteriores.

Para ejecutar una app compilada con el SDK del controlador, el dispositivo Android debe tener instalados los Servicios de Google Play.

Configura tu proyecto de desarrollo

Si deseas configurar tu proyecto de desarrollo y obtener una clave de API para el proyecto en la consola de Google Cloud, sigue estos pasos:

  1. Crea un proyecto nuevo de la consola de Google Cloud o selecciona uno existente para usar con el SDK de Driver. Espera unos minutos hasta que el proyecto nuevo sea visible en la consola de Google Cloud.

  2. Para ejecutar la app de demostración, tu proyecto debe tener acceso al SDK de Maps para Android. En la consola de Google Cloud, selecciona APIs y servicios > Biblioteca y, luego, busca y habilita el SDK de Maps para Android.

  3. Para obtener una clave de API para el proyecto, selecciona APIs y servicios > Credenciales > Crear credenciales > Clave de API. Consulta Cómo obtener una clave de API para conocer más detalles al respecto.

Agrega el SDK de Driver a tu app

El SDK de Driver está disponible en el repositorio de Maven de Google. El repositorio incluye los archivos del modelo de objetos del proyecto (.pom) del SDK y Javadocs. Para agregar el SDK del controlador a tu app, haz lo siguiente:

  1. Agrega la siguiente dependencia a tu configuración de Gradle o Maven y reemplaza el marcador de posición VERSION_NUMBER por la versión deseada del SDK del controlador.

    Gradle

    Agrega lo siguiente a tu build.gradle:

    dependencies {
      ...
      implementation 'com.google.android.libraries.mapsplatform.transportation:transportation-driver:VERSION_NUMBER'
    }
    

    Maven

    Agrega lo siguiente a tu pom.xml:

    <dependencies>
      ...
      <dependency>
        <groupId>com.google.android.libraries.mapsplatform.transportation</groupId>
        <artifactId>transportation-driver</artifactId>
        <version>VERSION_NUMBER</version>
      </dependency>
    </dependencies>
    
  2. El SDK de Driver depende del SDK de Navigation. Esta dependencia está configurada de tal manera que, si se necesita una versión específica del SDK de Navigation, se debe definir de forma explícita en el archivo de configuración de compilación de la siguiente manera. Si omites el bloque de código mencionado, el proyecto podrá descargar siempre la versión más reciente del SDK de Navigation dentro de la versión de actualización principal. Ten en cuenta que los comportamientos combinados de las versiones más recientes del SDK de Driver y del SDK de Navigation se sometieron a pruebas rigurosas antes de sus lanzamientos.

    Organiza la configuración de dependencias de tus entornos de desarrollo y lanzamiento según corresponda.

    Gradle

    Agrega lo siguiente a tu build.gradle:

    dependencies {
      ...
      implementation 'com.google.android.libraries.navigation:navigation:5.0.0'
    }
    

    Maven

    Agrega lo siguiente a tu pom.xml:

    <dependencies>
      ...
      <dependency>
        <groupId>com.google.android.libraries.navigation</groupId>
        <artifactId>navigation</artifactId>
        <version>5.0.0</version>
      </dependency>
    </dependencies>
    

Agrega la clave de API a tu app

Una vez que hayas agregado el SDK del controlador a tu app, agrega la clave de API. Debes usar la clave de API del proyecto que obtuviste cuando configuraste tu proyecto de desarrollo.

En esta sección, se describe cómo almacenar tu clave de API para que tu app pueda hacer referencia a ella de manera más segura. No debes registrarla en el sistema de control de versión. Debe almacenarse en el archivo local.properties, que se encuentra en el directorio raíz de tu proyecto. Para obtener más información sobre el archivo local.properties, consulta los archivos de propiedades de Gradle.

Para optimizar esta tarea, puedes usar el complemento Secrets Gradle para Android.

Para instalar el complemento y almacenar tu clave de API, haz lo siguiente:

  1. Abre el archivo build.gradle en el nivel raíz y agrega el siguiente código al elemento dependencies en buildscript.

    Groovy

    buildscript {
        dependencies {
            // ...
            classpath "com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin:2.0.0"
        }
    }
    

    Kotlin

    buildscript {
        dependencies {
            // ...
            classpath("com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin:2.0.0")
        }
    }
    
  2. Abre el archivo build.gradle a nivel de la app y agrega el siguiente código al elemento plugins.

    Groovy

    id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin'
    

    Kotlin

    id("com.google.android.libraries.mapsplatform.secrets-gradle-plugin")
    
  3. Si usas Android Studio, sincroniza tu proyecto con Gradle.

  4. Abre el archivo local.properties en el directorio de nivel de proyecto y, luego, agrega el siguiente código. Reemplaza YOUR_API_KEY por tu clave de API.

    MAPS_API_KEY=YOUR_API_KEY
    
  5. En tu archivo AndroidManifest.xml, ve a com.google.android.geo.API_KEY y actualiza el atributo android:value de la siguiente manera:

    <meta-data
        android:name="com.google.android.geo.API_KEY"
        android:value="${MAPS_API_KEY}" />
    

En el siguiente ejemplo, se muestra un manifiesto completo para una app de ejemplo:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.driverapidemo">
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/_AppTheme">

        <meta-data
            android:name="com.google.android.geo.API_KEY"
            android:value="${MAPS_API_KEY}" />

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

Incluye las atribuciones requeridas en tu aplicación

Si usas el SDK del controlador en tu app, debes incluir el texto de atribución y las licencias de código abierto como parte de la sección de avisos legales de la app. Es mejor incluir las atribuciones como un elemento de menú independiente o como parte de un elemento de menú Acerca de.

La información sobre las licencias se puede encontrar en el archivo "third_party_licenses.txt" en el archivo AAR desarchivado.

Consulta https://developers.google.com/android/guides/opensource para obtener información sobre cómo incluir avisos de código abierto.

Dependencias

Si usas ProGuard para optimizar tus compilaciones, es posible que debas agregar las siguientes líneas a tu archivo de configuración de ProGuard:

-dontwarn com.google.**
-dontwarn okio.**

El nivel de API mínimo admitido es 23.

Inicializa el SDK

Se requiere un ID de proveedor (por lo general, el ID del proyecto de Google Cloud) para inicializar el objeto DriverContext. Para obtener más detalles sobre cómo configurar el proyecto de Google Cloud, consulta Autenticación y autorización.

Antes de usar el SDK del controlador, primero debes inicializar el SDK de Navigation. Cómo inicializar el SDK:

  1. Obtén un objeto Navigator de NavigationApi.

    Java

    NavigationApi.getNavigator(
        this, // Activity
        new NavigationApi.NavigatorListener() {
          @Override
          public void onNavigatorReady(Navigator navigator) {
            // Keep a reference to the Navigator (used to configure and start nav)
            this.navigator = navigator;
          }
        }
    );
    

    Kotlin

    NavigationApi.getNavigator(
      this, // Activity
      object : NavigatorListener() {
        override fun onNavigatorReady(navigator: Navigator) {
          // Keep a reference to the Navigator (used to configure and start nav)
          this@myActivity.navigator = navigator
        }
      },
    )
    
  2. Crea un objeto DriverContext y propaga los campos obligatorios.

    Java

    DriverContext driverContext = DriverContext.builder(application)
        .setProviderId(providerId)
        .setVehicleId(vehicleId)
        .setAuthTokenFactory(authTokenFactory)
        .setNavigator(navigator)
        .setRoadSnappedLocationProvider(
            NavigationApi.getRoadSnappedLocationProvider(application))
        .build();
    

    Kotlin

    val driverContext =
      DriverContext.builder(application)
        .setProviderId(providerId)
        .setVehicleId(vehicleId)
        .setAuthTokenFactory(authTokenFactory)
        .setNavigator(navigator)
        .setRoadSnappedLocationProvider(NavigationApi.getRoadSnappedLocationProvider(application))
        .build()
    
  3. Usa el objeto DriverContext para inicializar *DriverApi.

    Java

    RidesharingDriverApi ridesharingDriverApi = RidesharingDriverApi.createInstance(driverContext);
    

    Kotlin

    val ridesharingDriverApi = RidesharingDriverApi.createInstance(driverContext)
    
  4. Obtén el RidesharingVehicleReporter del objeto de la API. (*VehicleReporter extiende NavigationVehicleReporter).

    Java

    RidesharingVehicleReporter vehicleReporter = ridesharingDriverApi.getRidesharingVehicleReporter();
    

    Kotlin

    val vehicleReporter = ridesharingDriverApi.getRidesharingVehicleReporter()
    

Autentica con AuthTokenFactory

Cuando el SDK del controlador genera actualizaciones de ubicación, debe enviarlas al servidor de Fleet Engine. Para autenticar estas solicitudes, el SDK del controlador llamará a una instancia de AuthTokenFactory proporcionada por el llamador. La fábrica es responsable de generar tokens de autenticación cuando se actualiza la ubicación.

La forma exacta en que se generan los tokens será específica para la situación de cada desarrollador. Sin embargo, es probable que la implementación necesite realizar las siguientes acciones:

  • recuperar un token de autenticación, posiblemente en formato JSON, desde un servidor HTTPS
  • analizar y almacenar en caché el token
  • actualiza el token cuando caduque

Si deseas obtener detalles sobre los tokens que espera el servidor de Fleet Engine, consulta Crea un token web JSON (JWT) para autorización.

A continuación, se muestra una implementación básica de un AuthTokenFactory:

Java

class JsonAuthTokenFactory implements AuthTokenFactory {
  private String token;  // initially null
  private long expiryTimeMs = 0;

  // This method is called on a thread whose only responsibility is to send
  // location updates. Blocking is OK, but just know that no location updates
  // can occur until this method returns.
  @Override
  public String getToken(AuthTokenContext authTokenContext) {
    if (System.currentTimeMillis() > expiryTimeMs) {
      // The token has expired, go get a new one.
      fetchNewToken(authTokenContext.getVehicleId());
    }
    return token;
  }

  private void fetchNewToken(String vehicleId) {
    String url =
        new Uri.Builder()
            .scheme("https")
            .authority("yourauthserver.example")
            .appendPath("token")
            .appendQueryParameter("vehicleId", vehicleId)
            .build()
            .toString();

    try (Reader r = new InputStreamReader(new URL(url).openStream())) {
      com.google.gson.JsonObject obj
          = com.google.gson.JsonParser.parseReader(r).getAsJsonObject();
      token = obj.get("Token").getAsString();
      expiryTimeMs = obj.get("TokenExpiryMs").getAsLong();

      // The expiry time could be an hour from now, but just to try and avoid
      // passing expired tokens, we subtract 10 minutes from that time.
      expiryTimeMs -= 10 * 60 * 1000;
    } catch (IOException e) {
      // It's OK to throw exceptions here. The StatusListener you passed to
      // create the DriverContext class will be notified and passed along the failed
      // update warning.
      throw new RuntimeException("Could not get auth token", e);
    }
  }
}

Kotlin

class JsonAuthTokenFactory : AuthTokenFactory() {

  private var token: String = ""
  private var expiryTimeMs: Long = 0

  // This method is called on a thread whose only responsibility is to send
  // location updates. Blocking is OK, but just know that no location updates
  // can occur until this method returns.
  override fun getToken(context: AuthTokenContext): String {
    if (System.currentTimeMillis() > expiryTimeMs) {
      // The token has expired, go get a new one.
      fetchNewToken(authTokenContext.getVehicleId())
    }
     return token
  }

  fun fetchNewToken(vehicleId: String) {
    val url =
      Uri.Builder()
        .scheme("https")
        .authority("yourauthserver.example")
        .appendPath("token")
        .appendQueryParameter("vehicleId", vehicleId)
        .build()
        .toString()

    try {
      val reader = InputStreamReader(URL(url).openStream())

      reader.use {
        val obj = com.google.gson.JsonParser.parseReader(r).getAsJsonObject()

        token = obj.get("ServiceToken").getAsString()
        expiryTimeMs = obj.get("TokenExpiryMs").getAsLong()

        // The expiry time could be an hour from now, but just to try and avoid
        // passing expired tokens, we subtract 10 minutes from that time.
        expiryTimeMs -= 10 * 60 * 1000
      }
    } catch (e: IOException) {
      // It's OK to throw exceptions here. The StatusListener you passed to
      // create the DriverContext class will be notified and passed along the failed
      // update warning.
      throw RuntimeException("Could not get auth token", e)
    }
  }
}

Esta implementación en particular usa el cliente HTTP de Java integrado para recuperar un token en formato JSON del servidor de autenticación del desarrollador. El token se guarda para volver a usarlo. El token se vuelve a recuperar si el anterior está dentro de los 10 minutos posteriores a su hora de vencimiento.

Tu implementación puede realizar acciones de manera diferente, por ejemplo, usar un subproceso en segundo plano para actualizar tokens.

Las excepciones en AuthTokenFactory se tratarán como transitorias, a menos que ocurran repetidamente. Después de varios intentos, el SDK del controlador supondrá que el error es permanente y dejará de intentar enviar actualizaciones.

Informes de estado y errores con StatusListener

Dado que el SDK del controlador realiza acciones en segundo plano, usa StatusListener para activar notificaciones cuando se produzcan ciertos eventos, como errores, advertencias o mensajes de depuración. Los errores pueden ser de naturaleza transitoria (como BACKEND_CONNECTIVITY_ERROR) o hacer que las actualizaciones de ubicación se detengan de forma permanente (como VEHICLE_NOT_FOUND, que indica un error de configuración).

Proporcionas una implementación de StatusListener opcional como la siguiente:

Java

class MyStatusListener implements StatusListener {
  /** Called when background status is updated, during actions such as location reporting. */
  @Override
  public void updateStatus(
      StatusLevel statusLevel, StatusCode statusCode, String statusMsg) {
    // Status handling stuff goes here.
    // StatusLevel may be DEBUG, INFO, WARNING, or ERROR.
    // StatusCode may be DEFAULT, UNKNOWN_ERROR, VEHICLE_NOT_FOUND,
    // BACKEND_CONNECTIVITY_ERROR, or PERMISSION_DENIED.
  }
}

Kotlin

class MyStatusListener : StatusListener() {
  /** Called when background status is updated, during actions such as location reporting. */
  override fun updateStatus(statusLevel: StatusLevel, statusCode: StatusCode, statusMsg: String) {
    // Status handling stuff goes here.
    // StatusLevel may be DEBUG, INFO, WARNING, or ERROR.
    // StatusCode may be DEFAULT, UNKNOWN_ERROR, VEHICLE_NOT_FOUND,
    // BACKEND_CONNECTIVITY_ERROR, or PERMISSION_DENIED.
  }
}

Notas sobre SSL/TLS

De forma interna, la implementación del SDK del controlador usa SSL/TLS para comunicarse de forma segura con el servidor de Fleet Engine. Es posible que las versiones anteriores de Android (nivel de API 19 o anteriores) requieran un parche SecurityProvider para poder comunicarse con el servidor. Deberías consultar este artículo para obtener más información sobre cómo trabajar con SSL en Android. También se incluyen muestras de código para aplicar parches al proveedor de seguridad.

Cómo habilitar las actualizaciones de ubicación

Una vez que tengas una instancia de *VehicleReporter, podrás habilitar las actualizaciones de ubicación de forma sencilla:

Java

RidesharingVehicleReporter reporter = ...;

reporter.enableLocationTracking();

Kotlin

val reporter = ...

reporter.enableLocationTracking()

Las actualizaciones de ubicación se envían a intervalos regulares cuando el estado del vehículo es ONLINE. Ten en cuenta que llamar a reporter.enableLocationTracking() no establece automáticamente el estado del vehículo en ONLINE. Debes establecer el estado del vehículo de manera explícita.

De forma predeterminada, el intervalo del informe es de 10 segundos. El intervalo del informe se puede cambiar con reporter.setLocationReportingInterval(long, TimeUnit). El intervalo de actualización mínimo admitido es de 5 segundos. Las actualizaciones más frecuentes pueden provocar solicitudes y errores más lentos.

Cómo inhabilitar las actualizaciones de ubicación

Cuando finaliza el turno del conductor, se pueden detener las actualizaciones de ubicación y marcar el vehículo como sin conexión llamando a DeliveryVehicleReporter.disableLocationTracking o RidesharingVehicleReporter.disableLocationTracking.

Esta llamada provocará que se programe una actualización final para su entrega inmediata, lo que indica que el vehículo está sin conexión. Esta actualización no contendrá la ubicación del usuario.

Configuración del estado del vehículo

Cuando las actualizaciones de ubicación están habilitadas, establecer el estado del vehículo en ONLINE hará que el vehículo esté disponible para consultas de SearchVehicles. Del mismo modo, marcar un vehículo como OFFLINE lo marcará como no disponible.

Tienes la opción de configurar el estado del vehículo en el servidor (consulta Cómo actualizar un vehículo) o directamente en el SDK del controlador:

Java

RidesharingVehicleReporter reporter = ...;

reporter.enableLocationTracking();
reporter.setVehicleState(VehicleState.ONLINE);

Kotlin

val reporter = ...

reporter.enableLocationTracking()
reporter.setVehicleState(VehicleState.ONLINE)

Cuando las actualizaciones de ubicación están habilitadas, se propagará una llamada a setVehicleState en la próxima actualización de ubicación.

Marcar un vehículo como ONLINE cuando el seguimiento de ubicación no está habilitado generará una IllegalStateException. Un vehículo se puede marcar como OFFLINE cuando el seguimiento de ubicación aún no está habilitado o se inhabilitó explícitamente. El resultado será una actualización inmediata. Una llamada a RidesharingVehicleReporter.disableLocationTracking() establecerá el estado del vehículo en OFFLINE.

Ten en cuenta que setVehicleState se muestra de inmediato y que las actualizaciones se realizan en el subproceso de actualización de la ubicación. Al igual que con el manejo de errores de las actualizaciones de ubicaciones, los errores que actualizan el estado del vehículo se propagan usando el objeto StatusListener proporcionado opcionalmente en DriverContext.