Primeiros passos com o SDK do driver para Android

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

O SDK do Driver mantém os serviços do Fleet Engine e os serviços personalizados cientes do local 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.

Requisitos mínimos do sistema

O dispositivo móvel precisa ter o Android 5.0 (nível 21 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, seu app precisa ser direcionado ao minSdkVersion 21 ou mais recente.

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

Configurar seu projeto de desenvolvimento

Para configurar o projeto de desenvolvimento e conseguir 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, depois 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 saber mais, consulte Acessar uma chave de API.

Adicionar o SDK do driver ao seu app

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

  1. Adicione a dependência abaixo à configuração do Gradle ou 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. Use 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 possa ser referenciada com mais segurança pelo app. Não verifique sua chave de API no sistema de controle de versões. Ele precisa ser armazenado no arquivo local.properties, que fica 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 seu 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 a seguir. 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 motorista no seu app, é necessário incluir o texto de atribuição e as 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.

Você pode encontrar o texto de atribuição necessário e as licenças de código aberto no arquivo ZIP do SDK do driver:

  • NOTICE.txt
  • LICENSES.txt

Dependências

Se você usa o ProGuard para otimizar seus builds, pode ser 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 é 21.

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 (em inglês).

Antes de usar o SDK do motorista, inicialize o SDK do Navigation. Para iniciar o SDK:

  1. Consiga 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 de 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 motorista chama 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
  • analisar e armazenar em cache o token
  • atualizar o token quando ele expirar.

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

Confira o modelo de implementação 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)
    }
  }
}

Essa 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 a data 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 uma série de tentativas, o SDK do driver vai presumir que o erro é permanente e vai parar de tentar enviar atualizações.

Status e Error Reporting com StatusListener

Como o SDK do driver realiza 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 fazer com que as atualizações de local sejam interrompidas permanentemente (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 com segurança com o servidor do Fleet Engine. As versões mais antigas do Android (versão 19 ou da API anteriores) 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, ativar as atualizações de localização será simples:

Java

RidesharingVehicleReporter reporter = ...;

reporter.enableLocationTracking();

Kotlin

val reporter = ...

reporter.enableLocationTracking()

As atualizações de localização são enviadas em um intervalo regular 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 com suporte é 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 terminar, as atualizações de localização poderão ser interrompidas e o veículo será 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 conter a localização do usuário.

Como 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 torna disponível para consultas SearchVehicles. Da mesma forma, marcar um veículo como OFFLINE marca o veículo 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 localização estão 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 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 resulta 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 no tratamento de erros das atualizações de local, os erros ao atualizar o estado do veículo são propagados usando o StatusListener fornecido opcionalmente, definido no DriverContext.