Criar apps de navegação, estacionamento e carregamento para o Android Auto

A biblioteca Android for Cars App permite que você leve seus apps de navegação, estacionamento e carregamento para o carro. Para isso, ela fornece um conjunto de moldes projetados para atender aos padrões de distração do motorista e cuidar de detalhes, como a variedade de fatores da tela do carro e as modalidades de entrada.

Este guia traz uma visão geral dos principais recursos e conceitos da biblioteca e oferece orientações sobre o processo de configuração de um app simples.

Antes de começar

  1. Leia as Diretrizes de design da biblioteca Android for Cars App.
  2. Revise os principais termos e conceitos listados nesta seção.
  3. Conheça melhor a IU do sistema do Android Auto.
  4. Leia as Notas da versão.
  5. Analise os Exemplos (link em inglês).
  6. [Apenas para bibliotecas de código fechado] Leia os Termos de Uso da biblioteca Android for Cars App.

Principais termos e conceitos

Modelos e moldes
A interface do usuário é representada por um gráfico de objetos de modelo que podem ser organizados de maneiras diferentes, conforme permitido pelo molde a que pertencem. Os moldes são um subconjunto dos modelos que podem funcionar como uma raiz nesses gráficos. Os modelos incluem as informações a serem exibidas ao usuário, na forma de texto e imagens, bem como atributos para configurar aspectos da aparência visual dessas informações (por exemplo, cores de texto ou tamanhos de imagem). O host converte os modelos em visualizações projetadas para atender aos padrões de distração do motorista e cuida de detalhes como a variedade de fatores da tela do carro e modalidades de entrada.
Host
O host é o componente back-end que implementa a funcionalidade oferecida pelas APIs da biblioteca para que o app seja executado no carro. As responsabilidades do host variam desde descobrir o app e gerenciar o ciclo de vida dele até converter os modelos em visualizações e notificar o app sobre interações do usuário. Em dispositivos móveis, esse host é implementado pelo Android Auto.
Restrições de moldes
Diferentes moldes impõem restrições ao conteúdo dos modelos. Por exemplo, os moldes de lista têm limites quanto ao número de itens que podem ser apresentados ao usuário. Os moldes também têm restrições quanto à maneira como podem ser conectados para formar o fluxo de uma tarefa. Por exemplo, o aplicativo pode enviar até cinco moldes para a pilha de tela. Consulte Restrições de moldes para mais detalhes.
Screen
Uma Screen é uma classe fornecida pela biblioteca que os apps implementam para gerenciar a interface do usuário exibida. Uma Screen tem um lifecycle e fornece o mecanismo para que o app envie o molde a ser exibido quando a tela estiver visível. Instâncias de Screen também podem ser enviadas e abertas na Pilha de tela e retiradas dela, o que garante que elas sigam as restrições de fluxos de molde.
CarAppService
Uma CarAppService é uma classe abstrata Service que o app precisa implementar e exportar para ser descoberto e gerenciados pelo host. A CarAppService do app é responsável por confirmar que uma conexão de host pode ser confiável usando CarAppService.createHostValidator e, em seguida, fornecendo instâncias de Session para cada conexão usando CarAppService.onCreateSession.
Session
Uma Session é uma classe abstrata que seu app precisa implementar e retornar usando CarAppService.onCreateSession. Ela serve como ponto de entrada para exibir informações na tela do carro e tem um ciclo de vida que informa o estado atual do app na tela, como quando ele está visível ou oculto.

Quando uma Session é iniciada (como quando o app é aberto pela primeira vez), o host solicita a Screen inicial a exibida usando o método Session.onCreateScreen.

Instalar a biblioteca

Siga a página de versões da biblioteca do Jetpack para ver instruções sobre como adicionar a biblioteca ao seu app.

Configurar os arquivos de manifesto do app

Antes de criar o app para carro, é preciso configurar os arquivos de manifesto dele.

Declarar o CarAppService

O host se conecta ao seu app usando a implementação CarAppService. Declare esse serviço no manifesto para permitir que o host descubra o app e se conecte a ele.

Também é necessário declarar a categoria do app no elemento category do filtro de intent. Veja a lista de categorias de apps compatíveis com os valores permitidos para esse elemento.

O snippet de código a seguir mostra como declarar um serviço de app para carros em um app de estacionamento no seu manifesto:

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

    ...
<application>

Categorias de apps compatíveis

Para ser listado na Play Store para Android Auto, o app precisa pertencer a uma das categorias de apps para carro compatíveis. Para declarar a categoria do seu app, adicione um ou mais dos seguintes valores de categorias compatíveis ao filtro de intent ao declarar o serviço do app para carro:

  • androidx.car.app.category.NAVIGATION: um app que fornece rotas de navegação passo a passo.
  • androidx.car.app.category.PARKING: um app que oferece uma funcionalidade relevante para encontrar vagas de estacionamento.
  • androidx.car.app.category.CHARGING: um app que oferece uma funcionalidade relevante para encontrar estações de recarga de veículos elétricos.

Consulte Qualidade de apps Android para carros e veja a descrição e os critérios detalhados de apps que pertencem a cada categoria.

Especificar o nome e o ícone do aplicativo

Especifique o nome e o ícone do app que o host pode usar para representar seu app na IU do sistema.

É possível especificar o nome e o ícone do app usados para representar seu app com os elementos label e icon do CarAppService:

...
<service
   android:name=".MyCarAppService"
   android:exported="true"
   android:label="@string/my_app_name"
   android:icon="@drawable/my_app_icon">
   ...
</service>
...

Se o rótulo ou o ícone não forem declarados no elemento service, o host usará os valores especificados para o aplicativo.

Definir a minSdkVersion do app

O Android Auto exige que seu app seja destinado ao Android 6.0 (API de nível 23) ou versões mais recentes.

Para especificar esse valor no projeto, defina o atributo minSdkVersion no elemento uses-sdk como 23 ou maior no arquivo AndroidManifest.xml do módulo do app para smartphones, conforme mostrado no exemplo a seguir.

<manifest xmlns:android="http://schemas.android.com/apk/res/android" ... >
    <uses-sdk android:minSdkVersion="23" android:targetSdkVersion="23" />
    ...
</manifest>

Declarar compatibilidade com o Android Auto

O host do Android Auto verifica se o aplicativo declarou compatibilidade com o Android Auto. Para ativar essa compatibilidade, inclua a seguinte entrada no manifesto do app:

<application>
    ...
    <meta-data
        android:name="com.google.android.gms.car.application"
        android:resource="@xml/automotive_app_desc"/>
    ...
</application>

Essa entrada de manifesto se refere a outro arquivo XML que você precisa criar com o caminho YourAppProject/app/src/main/res/xml/automotive_app_desc.xml, onde você declara quais recursos do Android Auto são compatíveis com seu app.

Os apps que usam a biblioteca Android for Cars App precisam declarar o recurso template no arquivo automotive_app_desc.xml:

<automotiveApp>
    <uses name="template" />
</automotiveApp>

Criar CarAppService e Session

Seu app precisa ampliar a classe CarAppService e implementar o método CarAppService.onCreateSession, que retorna uma Session correspondente à conexão atual com o host:

public final class HelloWorldService extends CarAppService {
  ...
  @Override
  @NonNull
  public Session onCreateSession() {
    return new HelloWorldSession();
  }
  ...
}

A instância de Session é responsável por retornar a instância de Screen a ser usada na primeira vez em que o aplicativo for iniciado:

public final class HelloWorldSession extends Session {
  ...
  @Override
  @NonNull
  public Screen onCreateScreen(@NonNull Intent intent) {
    return new HelloWorldScreen();
  }
  ...
}

Para gerenciar situações em que seu app para carro precise ser iniciado em uma tela que não seja a inicial ou de destino do app (como no processamento de links diretos), é possível pré-buscar uma pilha de retorno de telas usando ScreenManager.push. antes de retornar de onCreateScreen. A pré-busca permite que os usuários naveguem de volta para as telas anteriores pela primeira tela que seu app está exibindo.

Criar a tela inicial

Para criar as telas exibidas pelo app, defina classes que ampliem a classe Screen e implemente o método Screen.onGetTemplate, que retorna a instância de Template que representa o estado da IU a ser exibida na tela do carro.

O snippet a seguir mostra como declarar uma Screen que usa um molde PaneTemplate para exibir um simples "Hello world!":

public class HelloWorldScreen extends Screen {
  @NonNull
  @Override
  public Template onGetTemplate() {
    Pane pane = new Pane.Builder()
        .addRow(new Row.Builder()
            .setTitle("Hello world!")
            .build())
        .build();
    return new PaneTemplate.Builder(pane)
            .setHeaderAction(Action.APP_ICON)
            .build();
  }
}

Classe CarContext

A classe CarContext é uma subclasse de ContextWrapper acessível às suas instâncias de Session e Screen. Ela dá acesso a serviços para carros como o ScreenManager para gerenciar a pilha de tela, o AppManager para funcionalidades gerais relacionadas a apps, como acessar o objeto Surface para desenhar o mapa do app de navegação, e o NavigationManager, usado por aplicativos de navegação passo a passo para comunicar metadados de navegação e outros eventos relacionados à navegação ao o host. Consulte Acessar os moldes de navegação para ver uma lista completa das funcionalidades da biblioteca disponíveis para apps de navegação.

O CarContext também oferece outras funcionalidades, como permitir o carregamento de recursos desenháveis usando a configuração da tela do carro, iniciar um app no carro usando intents e sinalizar se o app de navegação precisa exibir o mapa no modo escuro.

Implementar a navegação na tela

Os apps costumam apresentar uma série de telas diferentes, cada uma possivelmente usando moldes diferentes, em que o usuário pode navegar enquanto interage com a interface exibida na tela.

A classe ScreenManager fornece uma pilha de tela a ser usada para enviar telas que podem ser abertas automaticamente quando o usuário seleciona um botão "Voltar" na tela do carro ou usa o botão de retorno do hardware disponível em alguns carros.

O snippet a seguir mostra como adicionar uma ação de retorno ao molde de mensagem, bem como uma ação que abre uma nova tela quando selecionada pelo usuário:

MessageTemplate template = new MessageTemplate.Builder("Hello world!")
    .setHeaderAction(Action.BACK)
    .addAction(
        new Action.Builder()
            .setTitle("Next screen")
            .setOnClickListener(() -> getScreenManager().push(new NextScreen()))
            .build())
    .build();

O objeto Action.BACK é uma Action padrão que invoca automaticamente ScreenManager.pop. Esse comportamento pode ser modificado usando a instância OnBackPressedDispatcher disponível no CarContext.

Para garantir que o app seja seguro ao dirigir, a pilha de tela pode ter uma profundidade máxima de cinco telas. Consulte Restrições de moldes para mais detalhes.

Atualizar o conteúdo de um molde

Seu app pode solicitar que o conteúdo de uma Screen seja invalidado chamando o método Screen.invalidate. Em seguida, o host chamará o método Screen.onGetTemplate do app para recuperar o molde com o novo conteúdo.

Ao atualizar uma Screen, é importante entender o conteúdo específico do molde que pode ser atualizado, para que o host não contabilize o novo molde na cota. Consulte Restrições de moldes para mais detalhes.

É recomendável estruturar as telas para que haja um mapeamento de um para um entre uma Screen e o tipo de modelo que ela retorna pela implementação de Screen.onGetTemplate.

Processar entrada do usuário

Seu app pode responder à entrada do usuário transmitindo os listeners adequados aos modelos compatíveis. O snippet a seguir mostra como criar um modelo de Action que define um OnClickListener que retorna a um método definido pelo código do aplicativo:

Action action = new Action.Builder()
    .setTitle("Navigate")
    .setOnClickListener(this::onClickNavigate)
    .build();

O método onClickNavigate pode iniciar o app padrão de navegação para carro usando o método CarContext.startCarApp:

private void onClickNavigate() {
  Intent intent = new Intent(CarContext.ACTION_NAVIGATE, Uri.parse("geo:0,0?q=" + address));
  getCarContext().startCarApp(intent);
}

Para ver mais detalhes sobre como iniciar apps, incluindo o formato da intent ACTION_NAVIGATE, consulte Iniciar um app para carro com uma intent.

Algumas ações, como as que exigem direcionar o usuário para continuar a interação nos dispositivos móveis, são permitidas apenas quando o carro está parado. Você pode usar o ParkedOnlyOnClickListener para implementar essas ações. Se o carro não estiver parado, o host exibirá uma indicação ao usuário de que a ação não é permitida nesse caso. Com o carro estacionado, o código será executado normalmente. O snippet a seguir mostra como usar ParkedOnlyOnClickListener para abrir uma tela de configurações no dispositivo móvel:

Row row = new Row.Builder()
    .setTitle("Open Settings")
    .setOnClickListener(
        ParkedOnlyOnClickListener.create(this::openSettingsOnPhone)
    .build();

Mostrar notificações

As notificações enviadas ao dispositivo móvel só aparecerão na tela do carro se forem estendidas com um CarAppExtender. Alguns atributos de notificação, como título, texto, ícone e ações do conteúdo, podem ser definidos no CarAppExtender, modificando os atributos da notificação quando aparecerem na tela do carro.

O snippet a seguir mostra como enviar uma notificação para a tela do carro com um título diferente daquele exibido no dispositivo móvel:

Notification notification =
    new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID)
        .setContentTitle(titleOnThePhone)
        .extend(
            new CarAppExtender.Builder()
                .setContentTitle(titleOnTheCar)
                ...
                .build())
        .build();

As notificações podem afetar as seguintes partes da interface do usuário:

  • Uma notificação de alerta (HUN, sigla em inglês) poderá ser exibida para o usuário.
  • É possível adicionar uma entrada à central de notificações, com a opção de um ícone visível na coluna.
  • Em apps de navegação, a notificação pode ser exibida no widget de coluna, conforme descrito em Notificações passo a passo.

Os aplicativos podem escolher como configurar as notificações para afetar cada um desses elementos da interface do usuário usando a prioridade da notificação, conforme descrito na documentação do CarAppExtender.

Se NotificationCompat.Builder.setOnlyAlertOnce for chamado com o valor true, uma notificação de alta prioridade será exibida como HUN apenas uma vez.

Para saber mais sobre como projetar as notificações do seu app para carros, consulte Notificações.

Mostrar avisos

Seu app pode exibir um aviso usando CarToast, conforme mostrado neste snippet:

CarToast.makeText(getCarContext(), "Hello!", CarToast.LENGTH_SHORT).show();

Iniciar um app para carro com uma intent

É possível chamar o método CarContext.startCarApp para executar uma das seguintes ações:

  • Abrir o discador para fazer uma chamada telefônica
  • Iniciar a navegação passo a passo em um local com o app de navegação padrão.
  • Iniciar seu app com uma intent

O exemplo a seguir mostra como criar uma notificação com uma ação que abre seu app com uma tela que mostra os detalhes de uma reserva de estacionamento. Amplie a instância de notificação com uma intent de conteúdo que tenha uma PendingIntent envolvendo uma intent explícita para a ação do seu app:

Notification notification =
    notificationBuilder.
        …
        .extend(
            new CarAppExtender.Builder()
                .setContentIntent(
                    PendingIntent.getBroadcast(
                        context,
                        ACTION_VIEW_PARKING_RESERVATION.hashCode(),
                        new Intent(ACTION_VIEW_PARKING_RESERVATION)
                            .setComponent(
                                new ComponentName(context, MyNotificationReceiver.class)),
                        0))
                .build())

Seu aplicativo também precisa declarar um BroadcastReceiver, que é invocado para processar a intent quando o usuário seleciona a ação na interface de notificação e invoca CarContext.startCarApp com uma intent que inclui o URI de dados:

public class MyNotificationReceiver extends BroadcastReceiver {
  @Override
  public void onReceive(Context context, Intent intent) {
    String intentAction = intent.getAction();
    if (ACTION_VIEW_PARKING_RESERVATION.equals(intentAction)) {
      CarContext.startCarApp(
         intent,
          new Intent(Intent.ACTION_VIEW)
              .setComponent(new ComponentName(context, MyCarAppService.class))
              .setData(Uri.fromParts(MY_URI_SCHEME, MY_URI_HOST, intentAction)));
    }
  }
}

Por fim, o método Session.onNewIntent no seu app processa essa intent enviando a tela de reserva de estacionamento à pilha, caso ainda não esteja na parte superior:

@Override
public void onNewIntent(@NonNull Intent intent) {
  Uri uri = intent.getData();
  if (uri != null
      && MY_URI_SCHEME.equals(uri.getScheme())
      && MY_URI_HOST.equals(uri.getSchemeSpecificPart())
      && ACTION_VIEW_PARKING_RESERVATION.equals(uri.getFragment())) {

    Screen top = screenManager.getTop();
    if (!(top instanceof ParkingReservationScreen)) {
      ScreenManager screenManager = getCarContext().getCarService(ScreenManager.class);
      screenManager.push(new ParkingReservationScreen(getCarContext()));
    }
  }
}

Consulte Mostrar notificações para mais informações sobre como lidar com notificações do app para carro.

Restrições de moldes

O host limita o número de moldes a serem exibidos para determinada tarefa a até cinco deles, em que o último desses moldes precisa ser um dos tipos a seguir:

  1. NavigationTemplate
  2. PaneTemplate
  3. MessageTemplate

Se a cota de moldes estiver esgotada e o aplicativo tentar enviar um novo molde, o host exibirá uma mensagem de erro para o usuário. Observe que esse limite se aplica ao número de moldes, e não ao número de instâncias de Screen na pilha. Por exemplo, enquanto está na tela A, um app envia dois moldes e, ao abrir a tela B, pode enviar mais três moldes. Como alternativa, se cada tela for estruturada para enviar um único molde, o app poderá enviar cinco instâncias de tela para a pilha ScreenManager.

Há casos especiais para essas restrições: atualizações de moldes e operações de retorno e redefinição.

Atualizações de moldes

Algumas atualizações de conteúdo não são contabilizadas no limite de moldes. Em geral, contanto que um app envie um novo molde que seja do mesmo tipo e tenha o mesmo conteúdo principal do molde anterior, o novo molde não será contabilizado na cota. Por exemplo, a atualização do estado de alternância de uma linha em um ListTemplate não é considerada na cota. Consulte a documentação de moldes individuais para saber mais sobre quais tipos de atualizações de conteúdo podem ser considerados uma atualização.

Operações de retorno

Para ativar subfluxos em uma tarefa, o host detecta quando um app está abrindo uma Screen da pilha ScreenManager e atualiza a cota restante com base no número de moldes que o app está retornando.

Por exemplo, se enquanto estiver na tela A o app enviar dois moldes, depois abrir a tela B e enviar mais dois, o app terá uma cota restante. Se o aplicativo voltar à tela A, o host redefinirá a cota para três, porque o app voltou dois moldes.

Ao retornar a uma tela, um aplicativo precisa enviar um molde do mesmo tipo que o último enviado por essa tela. O envio de qualquer outro tipo de molde causaria um erro. Porém, contanto que o tipo permaneça o mesmo durante uma operação de retorno, um aplicativo poderá modificar livremente o conteúdo do molde sem afetar a cota.

Redefinir operações

Alguns moldes têm uma semântica especial que representa o final de uma tarefa. Por exemplo, o NavigationTemplate é uma visualização que permanecerá na tela e será atualizada com novas instruções passo a passo para o consumo do usuário. Ao chegar a um desses moldes, o host redefinirá a cota e tratará o molde como se fosse a primeira etapa de uma nova tarefa, permitindo que o app a inicie. Consulte a documentação de moldes individuais para ver quais deles acionam uma redefinição no host.

Se o host receber uma intent para iniciar o app por uma ação de notificação ou pela tela de início, a cota também será redefinida. Esse mecanismo permite que um app inicie um novo fluxo de tarefas pelas notificações e é válido quando se um app já está vinculado e em primeiro plano.

Consulte Exibir notificações para mais detalhes sobre como exibir as notificações do seu app na tela do carro e Iniciar um app para carro com uma intent para ver como iniciar o app por uma ação de notificação.

Criar um app de estacionamento ou carregamento

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

Declarar compatibilidade com categoria no manifesto

Seu app precisa declarar a categoria do app para carro androidx.car.app.category.PARKING ou androidx.car.app.category.CHARGING no filtro de intent de CarAppService. Exemplo:

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

Acessar o modelo de mapa

Os apps podem acessar o PlaceListMapTemplate projetado especificamente para exibir uma lista de pontos de interesse ao lado de um mapa renderizado pelo host.

Para ter acesso a esse molde, seu app precisa declarar a permissão androidx.car.app.MAP_TEMPLATES no AndroidManifest.xml:

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

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 oferecer compatibilidade com intents de navegação ao seu app, incluindo aquelas provenientes do Google Assistente usando uma consulta por voz, seu app precisa processar a intent CarContext.ACTION_NAVIGATE em Session.onCreateScreen e Session.onNewIntent.

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

Acessar os moldes de navegação

Os apps de navegação podem acessar os seguintes moldes projetados especificamente para apps de navegação. Todos estes moldes 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 molde.

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

Para ter acesso aos moldes 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 moldes relevantes.

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

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

O SurfaceCallback fornece um callback quando o SurfaceContainer está disponível, junto 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 moldes 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 molde 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 do app Android Auto.

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

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:

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.

Você só precisará chamar NavigationManager.navigationEnded quando o usuário terminar de navegar. 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 exibidas para o usuário, dependendo do veículo em particular.

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:

new NotificationCompat.Builder(context, myNotificationChannelId)
    ...
    .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())
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 mostrará uma HUN, e defini-lo com qualquer outro valor 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 foco 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.

Ciclos de vida de CarAppService, Session e Screen

As classes Session e Screen implementam a interface LifecycleOwner. Conforme o usuário interage com o app, os callbacks do ciclo de vida dos objetos Session e Screen são invocados, conforme descrito nos diagramas a seguir.

Os ciclos de vida de CarAppService e de Session

Figura 1. O ciclo de vida de Session.

Para ver todos os detalhes, consulte a documentação do método Session.getLifecycle.

O ciclo de vida de Screen

Figura 2. O ciclo de vida de Screen.

Para ver todos os detalhes, consulte a documentação de Screen.getLifecycle.

Biblioteca de testes

A Biblioteca de testes do Android para carros oferece classes auxiliares que podem ser usadas para validar o comportamento dos seus apps em um ambiente de teste. Por exemplo, o SessionController permite que você simule uma conexão com o host e verifique se a Screen e o Template corretos são criados e retornados.

Consulte os exemplos (link em inglês) de uso.

Como executar o app em uma unidade principal real

Para que seu app seja executado em uma unidade principal real (não na unidade principal de área de trabalho que fornecemos), ele precisa ser distribuído pela Google Play Store. Isso garante que o aplicativo tenha sido testado e aprovado quanto à conformidade com nossas diretrizes. Essas diretrizes garantem que o aplicativo seja relevante para o ambiente do carro e para passar nos testes de distração do motorista.

Para testar durante o desenvolvimento, há duas opções: a unidade principal de área de trabalho e o envio do aplicativo à faixa de teste interno da Google Play Store. Na faixa de teste interno, você adiciona manualmente sua equipe para realizar testes internos. As versões dessa faixa não requerem avaliações da Play Store.

Sempre que você lança o APK em qualquer outra faixa, incluindo faixas fechadas, seu app passa por um processo de avaliação antes de ser aprovado para essa faixa na Play Store. Se o aplicativo for reprovado no processo de avaliação, você receberá informações sobre o motivo da reprovação. Esse processo permite que você corrija qualquer problema para cumprir nossas diretrizes.

Informar um problema na biblioteca Android for Cars App

Se você encontrar um problema com a biblioteca, informe-o usando o Google Issue Tracker. Preencha todas as informações solicitadas no modelo de problema.

Criar novo problema

Antes de registrar um novo problema, verifique se ele está listado nas notas da versão da biblioteca ou relatado na lista de problemas. Inscreva-se e vote nos problemas clicando na estrela de um deles no rastreador. Para saber mais, consulte Inscrever-se em um problema.