Primeiros passos com o SDK do driver para Android

Você pode usar o SDK do Driver para oferecer navegação e rastreamento aprimorados ao aplicativo de viagem e andamento do pedido. Ele fornece atualizações de localização e tarefas do veículo para o mecanismo de frota da solução de viagens e entregas sob demanda.

O SDK do Driver mantém os serviços do Fleet Engine e seus serviços personalizados cientes da localização e do estado do veículo. Por exemplo, o veículo pode ser ONLINE ou OFFLINE, e a localização dele muda conforme a viagem progride.

Requisitos mínimos do sistema

O dispositivo móvel precisa ter o Android 6.0 (nível 23 da API) ou mais recente.

Configuração do Maven

As versões 4.99 e mais recentes do SDK do Driver estão disponíveis no repositório Maven do Google.

Gradle

Adicione a instrução a seguir ao seu arquivo build.gradle:

repositories {
    ...
    google()
}

Maven

Adicione a instrução a seguir ao seu arquivo pom.xml:

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

Configuração do projeto

Para usar o SDK do Driver, o app precisa ser direcionado ao minSdkVersion 23 ou mais recente.

Para executar um app criado com o SDK do driver, o dispositivo Android precisa ter o Google Play Services instalado.

Configurar seu projeto de desenvolvimento

Para configurar o projeto de desenvolvimento e receber uma chave de API para o projeto no Console do Google Cloud:

  1. Crie um novo projeto do Console do Google Cloud ou selecione um projeto existente para usar com o SDK do Driver. Aguarde alguns minutos até que o novo projeto esteja visível no Console do Google Cloud.

  2. Para executar o app de demonstração, seu projeto precisa ter acesso ao SDK do Maps para Android. No Console do Google Cloud, selecione APIs e serviços > Biblioteca, pesquise e ative o SDK do Maps para Android.

  3. Para gerar uma chave de API para o projeto, selecione APIs e serviços > Credenciais > Criar credenciais > Chave de API. Para mais informações sobre como conseguir uma chave de API, consulte Acessar uma chave de API.

Adicionar o SDK do Driver ao seu app

O SDK do Driver está disponível no repositório Maven do Google. O repositório inclui os arquivos do Modelo de objeto de projeto (.pom) do SDK e Javadocs. Para adicionar o SDK do Driver ao seu app:

  1. Adicione a seguinte dependência à configuração do Gradle ou do Maven, substituindo o marcador VERSION_NUMBER pela versão desejada do SDK do Driver.

    Gradle

    Adicione o seguinte ao seu build.gradle:

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

    Maven

    Adicione o seguinte ao seu pom.xml:

    <dependencies>
      ...
      <dependency>
        <groupId>com.google.android.libraries.mapsplatform.transportation</groupId>
        <artifactId>transportation-driver</artifactId>
        <version>VERSION_NUMBER</version>
      </dependency>
    </dependencies>
    

Adicionar a chave de API ao seu app

Depois de adicionar o SDK do driver ao app, inclua a chave de API nele. É necessário usar a chave de API do projeto que você recebeu ao configurar seu projeto de desenvolvimento.

Nesta seção, descrevemos como armazenar sua chave de API para que ela possa ser referenciada pelo seu app com mais segurança. Não faça a verificação da sua chave de API no sistema de controle de versões. Ele precisa ser armazenado no arquivo local.properties, localizado no diretório raiz do projeto. Para saber mais sobre o arquivo local.properties, consulte Arquivos de propriedades do Gradle.

Para otimizar essa tarefa, use o Plug-in Secrets Gradle para Android (em inglês).

Para instalar o plug-in e armazenar sua chave de API, siga estas instruções:

  1. Abra o arquivo build.gradle no nível raiz e adicione o seguinte código ao elemento dependencies em 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. Abra o arquivo build.gradle no nível do app e adicione o seguinte código ao elemento plugins.

    Groovy

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

    Kotlin

    id("com.google.android.libraries.mapsplatform.secrets-gradle-plugin")
    
  3. Se você usa o Android Studio, sincronize seu projeto com o Gradle.

  4. Abra o local.properties no diretório do nível do projeto e adicione o código abaixo. Substitua YOUR_API_KEY pela sua chave de API.

    MAPS_API_KEY=YOUR_API_KEY
    
  5. No seu arquivo AndroidManifest.xml, acesse com.google.android.geo.API_KEY e atualize o atributo android:value da seguinte maneira:

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

O exemplo a seguir mostra um manifesto completo para um app de exemplo:

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

Incluir as atribuições necessárias no seu app

Se você usa o SDK do Driver no seu app, é necessário incluir texto de atribuição e licenças de código aberto como parte da seção de avisos legais do app. É melhor incluir as atribuições como um item de menu independente ou como parte de um item de menu Sobre.

As informações de licenças podem ser encontradas no arquivo "third_party_licenses.txt" no arquivo AAR desarquivado.

Consulte https://developers.google.com/android/guides/opensource para saber como incluir avisos de código aberto.

Dependências

Se você usa o ProGuard para otimizar seus builds, talvez seja necessário adicionar as seguintes linhas ao arquivo de configuração do ProGuard:

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

O nível mínimo da API com suporte é 23.

Initializing the SDK

Um ID de provedor (geralmente o ID do projeto do Google Cloud) é necessário para inicializar o objeto DriverContext. Para mais detalhes sobre como configurar o projeto do Google Cloud, consulte Autenticação e autorização.

Antes de usar o SDK do Driver, primeiro é necessário inicializar o SDK do Navigation. Para iniciar o SDK:

  1. Extraia um objeto Navigator do 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. Crie um objeto DriverContext preenchendo os campos obrigatórios.

    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. Use o objeto DriverContext para inicializar o *DriverApi.

    Java

    RidesharingDriverApi ridesharingDriverApi = RidesharingDriverApi.createInstance(driverContext);
    

    Kotlin

    val ridesharingDriverApi = RidesharingDriverApi.createInstance(driverContext)
    
  4. Consiga o RidesharingVehicleReporter do objeto da API. (*VehicleReporter estende NavigationVehicleReporter.)

    Java

    RidesharingVehicleReporter vehicleReporter = ridesharingDriverApi.getRidesharingVehicleReporter();
    

    Kotlin

    val vehicleReporter = ridesharingDriverApi.getRidesharingVehicleReporter()
    

Como autenticar com AuthTokenFactory

Quando o SDK do Driver gera atualizações de localização, ele precisa enviá-las ao servidor do Fleet Engine. Para autenticar essas solicitações, o SDK do driver chamará uma instância de AuthTokenFactory fornecida pelo autor da chamada. A fábrica é responsável por gerar tokens de autenticação no momento da atualização do local.

A forma exata como os tokens são gerados vai depender da situação de cada desenvolvedor. No entanto, a implementação provavelmente vai precisar:

  • buscar um token de autenticação, possivelmente no formato JSON, de um servidor HTTPS;
  • analise e armazene o token em cache
  • atualizar o token quando ele expirar;

Para detalhes sobre os tokens esperados pelo servidor do Fleet Engine, consulte Como criar um JSON Web Token (JWT) para autorização.

Confira uma implementação de esqueleto de um 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 implementação específica usa o cliente HTTP Java integrado para buscar um token no formato JSON no servidor de autenticação do desenvolvedor. O token é salvo para reutilização. O token será buscado novamente se o token antigo estiver dentro de 10 minutos após o prazo de validade.

Sua implementação pode fazer as coisas de forma diferente, como usar uma linha de execução em segundo plano para atualizar os tokens.

As exceções em AuthTokenFactory serão tratadas como temporárias, a menos que ocorram repetidamente. Após várias tentativas, o SDK do driver vai presumir que o erro é permanente e vai parar de tentar enviar atualizações.

Status e relatórios de erros com StatusListener

Como o SDK do Driver executa ações em segundo plano, use o StatusListener para acionar notificações quando determinados eventos ocorrerem, como erros, avisos ou mensagens de depuração. Os erros podem ser temporários por natureza (como BACKEND_CONNECTIVITY_ERROR) ou podem causar a interrupção permanente das atualizações de local, como VEHICLE_NOT_FOUND, indicando um erro de configuração.

Você fornece uma implementação StatusListener opcional como esta:

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.
  }
}

Observações sobre SSL/TLS

Internamente, a implementação do SDK do Driver usa SSL/TLS para se comunicar de maneira segura com o servidor do Fleet Engine. Versões mais antigas do Android (versão 19 ou anteriores da API) podem exigir um patch SecurityProvider para se comunicar com o servidor. Consulte este artigo para mais informações sobre como trabalhar com SSL no Android. O artigo também contém exemplos de código para corrigir o provedor de segurança.

Ativando atualizações de localização

Quando você tiver uma instância de *VehicleReporter, a ativação das atualizações de localização é simples:

Java

RidesharingVehicleReporter reporter = ...;

reporter.enableLocationTracking();

Kotlin

val reporter = ...

reporter.enableLocationTracking()

As atualizações de localização são enviadas em intervalos regulares quando o estado do veículo é ONLINE. Observe que chamar reporter.enableLocationTracking() não define automaticamente o estado do veículo como ONLINE. É necessário definir o estado do veículo explicitamente.

Por padrão, o intervalo do relatório é de 10 segundos. O intervalo do relatório pode ser alterado com reporter.setLocationReportingInterval(long, TimeUnit). O intervalo mínimo de atualização permitido é de cinco segundos. Atualizações mais frequentes podem resultar em solicitações mais lentas e erros.

Desativando atualizações de local

Quando o turno do motorista termina, as atualizações de localização podem ser interrompidas e o veículo marcado como off-line chamando DeliveryVehicleReporter.disableLocationTracking ou RidesharingVehicleReporter.disableLocationTracking.

Essa chamada fará com que uma atualização final seja agendada para entrega imediata, indicando que o veículo está off-line. Essa atualização não vai incluir a localização do usuário.

Definir o estado do veículo

Quando as atualizações de localização estão ativadas, definir o estado do veículo como ONLINE o disponibiliza para consultas por SearchVehicles. Da mesma forma, marcar um veículo como OFFLINE o marcará como indisponível.

Você pode definir o estado do veículo no lado do servidor (consulte Atualizar um veículo) ou diretamente no SDK do motorista:

Java

RidesharingVehicleReporter reporter = ...;

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

Kotlin

val reporter = ...

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

Quando as atualizações de local estiverem ativadas, uma chamada para setVehicleState será propagada na próxima atualização de local.

Marcar um veículo como ONLINE quando o monitoramento de localização não estiver ativado vai resultar em uma IllegalStateException. Um veículo pode ser marcado como OFFLINE quando o rastreamento de localização ainda não está ativado ou explicitamente desativado. Isso resultará em uma atualização imediata. Uma chamada para RidesharingVehicleReporter.disableLocationTracking() define o estado do veículo como OFFLINE.

Observe que setVehicleState retorna imediatamente e as atualizações são feitas na linha de execução de atualização de local. Assim como o tratamento de erros de atualizações de local, os erros de atualização do estado do veículo são propagados usando o StatusListener fornecido opcionalmente definido no DriverContext.