Criar um app de navegação

Esta seção detalha os diferentes recursos da biblioteca que você pode usar para implementar a funcionalidade do seu app de navegação passo a passo.

Declarar compatibilidade com navegação no manifesto

Seu app de navegação precisa declarar a categoria do app para carros androidx.car.app.category.NAVIGATION no filtro de intent de CarAppService:

<application>
    ...
   <service
       ...
        android:name=".MyNavigationCarAppService"
        android:exported="true">
      <intent-filter>
        <action android:name="androidx.car.app.CarAppService" />
        <category android:name="androidx.car.app.category.NAVIGATION"/>
      </intent-filter>
    </service>
    ...
<application>

Compatibilidade com intents de navegação

Para ter suporte a intents de navegação no seu app, incluindo aquelas provenientes do Google Assistente usando uma consulta por voz, o app precisa processar a intent CarContext.ACTION_NAVIGATE nos métodos Session.onCreateScreen e Session.onNewIntent.

Consulte a documentação de CarContext.startCarApp para ver detalhes sobre o formato da intent.

Acessar os modelos de navegação

Os apps de navegação podem acessar os seguintes modelos projetados especificamente para apps de navegação. Todos estes modelos exibem uma superfície em segundo plano que o app pode acessar para desenhar o mapa, junto com outras informações fornecidas pelo app que variam de acordo com o modelo.

Para ver mais detalhes sobre como projetar a interface do usuário do seu app de navegação usando esses modelos, consulte as Diretrizes de design da biblioteca Android for Cars App.

Para ter acesso aos modelos de navegação, seu app precisa declarar a permissão androidx.car.app.NAVIGATION_TEMPLATES no AndroidManifest.xml:

<uses-permission android:name="androidx.car.app.NAVIGATION_TEMPLATES"/>

Desenhar o mapa

Os aplicativos de navegação podem acessar uma Surface para desenhar o mapa nos modelos relevantes.

Um objeto SurfaceContainer pode ser acessado ao definir uma instância de SurfaceCallback para o serviço de carro AppManager:

Kotlin

carContext.getCarService(AppManager::class.java).setSurfaceCallback(surfaceCallback)

Java

carContext.getCarService(AppManager.class).setSurfaceCallback(surfaceCallback);

O SurfaceCallback fornece um callback quando o SurfaceContainer está disponível com outros callbacks quando as propriedades da Surface mudam.

Para ter acesso à superfície, seu app precisa declarar a permissão androidx.car.app.ACCESS_SURFACE no AndroidManifest.xml:

<uses-permission android:name="androidx.car.app.ACCESS_SURFACE"/>

Área visível do mapa

O host pode desenhar elementos da interface do usuário para diferentes modelos na parte superior do mapa. O host comunicará a área que certamente não estará fechada e que ficará totalmente visível para o usuário chamando SurfaceCallback.onVisibleAreaChanged. Além disso, para minimizar o número de mudanças, o host também chamará o método SurfaceCallback.onStableAreaChanged com o maior retângulo que será visível com base no modelo atual.

Por exemplo, quando um app de navegação está usando o NavigationTemplate com uma faixa de ação na parte superior, essa faixa pode ficar oculta quando o usuário não está interagindo com a tela há algum tempo, para criar mais espaço para o mapa. Nesse caso, haverá um callback para onStableAreaChanged e onVisibleAreaChanged com o mesmo retângulo. Quando a faixa de ação estiver oculta, somente onVisibleAreaChanged será chamado com a área maior. Se o usuário interagir com a tela, novamente apenas onVisibleAreaChanged será chamado com o primeiro retângulo.

Modo escuro

Os apps de navegação precisam redesenhar o mapa na instância de Surface com as cores escuras adequadas quando o host determina que as condições as justificam, conforme descrito nas Diretrizes de qualidade de apps do Android Auto.

Para decidir se você precisa desenhar um mapa escuro, use o método CarContext.isDarkMode. Você vai receber uma chamada para Session.onCarConfigurationChanged sempre que o status do modo escuro for alterado.

Os aplicativos de navegação precisam comunicar outros metadados de navegação ao host. O host usa as informações para fornecer dados à unidade principal do veículo e para impedir que aplicativos de navegação entrem em conflito com os recursos compartilhados.

Os metadados de navegação são fornecidos pelo serviço de carro NavigationManager, acessível no CarContext:

Kotlin

val navigationManager = carContext.getCarService(NavigationManager::class.java)

Java

NavigationManager navigationManager = carContext.getCarService(NavigationManager.class);

Iniciar, encerrar e interromper a navegação

Para que o host gerencie vários apps de navegação, notificações de roteamento e dados de cluster do veículo, ele precisa estar ciente do estado atual da navegação. Quando um usuário inicia a navegação, o app precisa chamar NavigationManager.navigationStarted. Da mesma forma, quando a navegação termina, por exemplo, quando o usuário chega ao destino ou quando ele cancela a navegação, o app precisa chamar NavigationManager.navigationEnded.

Chame NavigationManager.navigationEnded apenas quando o usuário terminar a navegação. Por exemplo, se você precisar recalcular a rota no meio de uma viagem, use Trip.Builder.setLoading(true).

Ocasionalmente, o host precisará de um app para interromper a navegação e chamar stopNavigation em um objeto NavigationManagerListener fornecido pelo seu aplicativo pelo NavigationManager.setListener. O app precisa parar de emitir informações da próxima curva na exibição do cluster, nas notificações de navegação e na orientação por voz.

Informações da viagem

Durante a navegação ativa, o app precisa chamar NavigationManager.updateTrip. As informações fornecidas nessa chamada serão usadas no cluster do veículo e nos avisos na tela. Nem todas as informações podem ser mostradas para o usuário, dependendo do veículo em questão. Por exemplo, a Desktop Head Unit mostra a Step adicionada à Trip, mas não mostra a informação do Destination.

Para testar se as informações estão chegando ao cluster, a ferramenta Desktop Head Unit (DHU) pode ser configurada para mostrar uma exibição de cluster simples. Crie um arquivo cluster.ini com o seguinte conteúdo:

[general]
instrumentcluster = true

É possível invocar a DHU com um parâmetro de linha de comando adicional:

dhu -c cluster.ini

Notificações passo a passo

As instruções de navegação passo a passo (TBT, sigla em inglês) podem ser fornecidas com uma notificação de navegação atualizada com frequência. Para ser tratada como uma notificação de navegação na tela do carro, o builder da notificação precisa fazer o seguinte:

  1. Marcar a notificação como em andamento com o método NotificationCompat.Builder.setOngoing
  2. Definir a categoria da notificação como Notification.CATEGORY_NAVIGATION
  3. Estender a notificação com um CarAppExtender

Uma notificação de navegação será exibida no widget de coluna na parte inferior da tela do carro. Se o nível de importância da notificação estiver definido como IMPORTANCE_HIGH, ele também será exibido como uma notificação de alerta (HUN, sigla em inglês). Se a importância não estiver definida com o método CarAppExtender.Builder.setImportance, a importância do canal de notificação será usada.

O app pode definir uma PendingIntent no CarAppExtender que será enviada ao app quando o usuário tocar na HUN ou no widget de coluna.

Se NotificationCompat.Builder.setOnlyAlertOnce for chamado com o valor true, uma notificação de alta importância será emitida apenas uma vez na HUN.

O snippet a seguir mostra como criar uma notificação de navegação:

Kotlin

NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID)
    ...
    .setOnlyAlertOnce(true)
    .setOngoing(true)
    .setCategory(NotificationCompat.CATEGORY_NAVIGATION)
    .extend(
        CarAppExtender.Builder()
            .setContentTitle(carScreenTitle)
            ...
            .setContentIntent(
                PendingIntent.getBroadcast(
                    context,
                    ACTION_OPEN_APP.hashCode(),
                    Intent(ACTION_OPEN_APP).setComponent(
                        ComponentName(context, MyNotificationReceiver::class.java)),
                        0))
            .setImportance(NotificationManagerCompat.IMPORTANCE_HIGH)
            .build())
    .build()

Java

new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID)
    ...
    .setOnlyAlertOnce(true)
    .setOngoing(true)
    .setCategory(NotificationCompat.CATEGORY_NAVIGATION)
    .extend(
        new CarAppExtender.Builder()
            .setContentTitle(carScreenTitle)
            ...
            .setContentIntent(
                PendingIntent.getBroadcast(
                    context,
                    ACTION_OPEN_APP.hashCode(),
                    new Intent(ACTION_OPEN_APP).setComponent(
                        new ComponentName(context, MyNotificationReceiver.class)),
                        0))
            .setImportance(NotificationManagerCompat.IMPORTANCE_HIGH)
            .build())
    .build();

Diretrizes para notificações passo a passo

Os apps de navegação precisam atualizar a notificação TBT regularmente com mudanças de distância, o que atualiza o widget de coluna e mostra a notificação apenas como HUN. Os apps podem controlar o comportamento da HUN definindo a importância da notificação com o método CarAppExtender.Builder.setImportance. Definir a importância como IMPORTANCE_HIGH vai mostrar uma HUN, enquanto a definição com qualquer outro valor vai atualizar apenas o widget de coluna.

Orientação por voz

Para reproduzir a orientação de navegação nos alto-falantes do carro, seu app precisa solicitar seleção de áudio. Como parte da AudioFocusRequest, você precisa definir o uso como AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE. Defina também o ganho de seleção como AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK.

Simular a navegação

Para verificar a funcionalidade de navegação do seu app ao enviá-lo à Google Play Store, o app precisa implementar o callback NavigationManagerCallback.onAutoDriveEnabled. Quando esse callback é chamado, o app precisa simular o trajeto até o destino escolhido quando o usuário inicia a navegação. O app pode sair desse modo sempre que o ciclo de vida da Session atual atingir o estado Lifecycle.Event.ON_DESTROY.

Teste se a implementação de onAutoDriveEnabled é chamada executando o seguinte em uma linha de comando:

adb shell dumpsys activity service CAR_APP_SERVICE_NAME AUTO_DRIVE

Exemplo:

adb shell dumpsys activity service androidx.car.app.samples.navigation.car.NavigationCarAppService AUTO_DRIVE

App padrão de navegação para carro

No Android Auto, o app padrão de navegação para carro corresponde ao último app de navegação que o usuário iniciou. Esse é o app que, por exemplo, recebe as intents de navegação quando o usuário invoca comandos de navegação pelo assistente ou quando outro app envia uma intent para iniciar a navegação.

Permitir que os usuários interajam com seu mapa

A partir do nível 2 da API Car App, você pode aplicar zoom ao seu mapa ou movimentá-lo no NavigationTemplate para permitir que os usuários vejam diferentes partes do mapa.

Métodos de SurfaceCallback

O SurfaceCallback tem três métodos de callback que permitem adicionar interatividade de mapas ao NavigationTemplate: onScale, onScroll e onFling. Consulte a tabela abaixo para saber como esses callbacks estão relacionados às interações do usuário.

Interação Método SurfaceCallback
Fazer gesto de pinça (zoom) onScale
Arrastar com um único toque onScroll
Deslizar rapidamente com um único toque onFling
Tocar duas vezes onScale (com um fator de escalonamento determinado pelo host do modelo)
Alerta giratório no modo Movimentar onScroll (com um fator de distância determinado pelo host do modelo)

Faixa de ações no mapa

O NavigationTemplate pode ter uma faixa de ações no mapa para ações relacionadas ao mapa, por exemplo, aumentar e diminuir o zoom, recentralizar, exibir uma bússola ou qualquer outra ação que é possível exibir no app. A faixa de ações no mapa pode ter até quatro botões somente de ícones que podem ser atualizados sem afetar a profundidade da tarefa. Assim como a faixa de ações, a faixa de ações no mapa fica oculta no estado inativo e reaparece no estado ativo.

Para receber callbacks de interatividade no mapa, adicione um botão Action.PAN à faixa de ações no mapa. Se o app omitir o botão Action.PAN na faixa de ações, você não receberá entradas dos usuários dos métodos SurfaceCallback, e o host fechará qualquer modo de movimentação ativado anteriormente. Quando o usuário pressiona o botão de movimentação, o host entra no modo de movimentação. Em uma tela touchscreen, o botão de movimentação não será exibido.

Modo de movimentação

No modo de movimentação, o host do modelo converte a entrada do usuário de dispositivos de entrada sem toque, como controladores giratórios e touchpads, em métodos SurfaceCallback adequados. Responda à ação do usuário para entrar ou sair do modo de movimentação com o método setPanModeListener no NavigationTemplate Builder. O host pode ocultar outros componentes de IU no modelo enquanto o usuário está no modo de movimentação.

Área estável

A área estável é atualizada entre os estados ativo e inativo. Seu app precisa desenhar informações relacionadas à condução, como velocidade, limite de velocidade ou avisos de rua, dependendo do tamanho da área estável, para que informações importantes no mapa não sejam escondidas pela faixa de ações no mapa.