Esta página detalha os diferentes recursos da biblioteca Car App que podem ser usados para implementar a funcionalidade do seu app de navegação guiada.
Declarar suporte à navegação no manifesto
Seu app de navegação precisa declarar a categoria de app para carros androidx.car.app.category.NAVIGATION
no filtro de intent do 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>
Suporte a intents de navegação
Vários formatos de intent permitem que apps de navegação funcionem com outros apps, como apps de ponto de interesse e assistentes de voz.
Para oferecer suporte a esses formatos de intent, primeiro declare o suporte adicionando filtros de intent no manifesto do app. O local desses filtros de intent depende da plataforma:
- Android Auto: no elemento de manifesto
<activity>
para oActivity
usado para processar a intent quando um usuário não está usando o Android Auto. - Android Automotive OS: no elemento de manifesto
<activity>
para oCarAppActivity
.
Em seguida, leia e processe as intents nos callbacks onCreateScreen()
e onNewIntent()
na implementação Session
do app.
Formatos de intent obrigatórios
Para atender ao requisito de qualidade NF-6
, o app precisa processar intenções de navegação.
Formatos de intent opcionais
Você também pode oferecer suporte aos seguintes formatos de intent para aumentar ainda mais a interoperabilidade do app:
Acessar os modelos de navegação
Os apps de navegação podem acessar os seguintes modelos, que mostram uma plataforma em segundo plano com o mapa e as instruções de navegação guiada (quando ativa).
NavigationTemplate
: também mostra uma mensagem informativa opcional e estimativas da viagem durante a navegação ativa.MapWithContentTemplate
: um modelo que permite que um app renderize blocos de mapa com algum tipo de conteúdo (por exemplo, uma lista). O conteúdo geralmente é renderizado como uma sobreposição sobre os blocos do mapa, com as áreas visíveis e estáveis do mapa se ajustando ao conteúdo.
Para saber mais sobre como projetar a interface do usuário do seu app de navegação usando esses modelos, consulte Apps de navegação.
Para ter acesso aos modelos de navegação, seu app precisa declarar a permissão androidx.car.app.NAVIGATION_TEMPLATES
no arquivo AndroidManifest.xml
:
<manifest ...> ... <uses-permission android:name="androidx.car.app.NAVIGATION_TEMPLATES"/> ... </manifest>
É necessária uma permissão adicional para criar mapas.
Migrar para o MapWithContentTemplate
A partir do nível 7 da API Car App, as funções MapTemplate
, PlaceListNavigationTemplate
e RoutePreviewNavigationTemplate
foram descontinuadas. Os modelos descontinuados vão continuar sendo aceitos, mas a migração para o MapWithContentTemplate
é altamente recomendada.
A funcionalidade fornecida por esses modelos pode ser implementada usando MapWithContentTemplate
. Confira os exemplos nos snippets abaixo:
MapTemplate
Kotlin
// MapTemplate (deprecated) val template = MapTemplate.Builder() .setPane(paneBuilder.build()) .setActionStrip(actionStrip) .setHeader(header) .setMapController(mapController) .build() // MapWithContentTemplate val template = MapWithContentTemplate.Builder() .setContentTemplate( PaneTemplate.Builder(paneBuilder.build()) .setHeader(header) .build()) .setActionStrip(actionStrip) .setMapController(mapController) .build()
Java
// MapTemplate (deprecated) MapTemplate template = new MapTemplate.Builder() .setPane(paneBuilder.build()) .setActionStrip(actionStrip) .setHeader(header) .setMapController(mapController) .build(); // MapWithContentTemplate MapWithContentTemplate template = new MapWithContentTemplate.Builder() .setContentTemplate(new PaneTemplate.Builder(paneBuilder.build()) .setHeader(header) build()) .setActionStrip(actionStrip) .setMapController(mapController) .build();
PlaceListNavigationTemplate
Kotlin
// PlaceListNavigationTemplate (deprecated) val template = PlaceListNavigationTemplate.Builder() .setItemList(itemListBuilder.build()) .setHeader(header) .setActionStrip(actionStrip) .setMapActionStrip(mapActionStrip) .build() // MapWithContentTemplate val template = MapWithContentTemplate.Builder() .setContentTemplate( ListTemplate.Builder() .setSingleList(itemListBuilder.build()) .setHeader(header) .build()) .setActionStrip(actionStrip) .setMapController( MapController.Builder() .setMapActionStrip(mapActionStrip) .build()) .build()
Java
// PlaceListNavigationTemplate (deprecated) PlaceListNavigationTemplate template = new PlaceListNavigationTemplate.Builder() .setItemList(itemListBuilder.build()) .setHeader(header) .setActionStrip(actionStrip) .setMapActionStrip(mapActionStrip) .build(); // MapWithContentTemplate MapWithContentTemplate template = new MapWithContentTemplate.Builder() .setContentTemplate(new ListTemplate.Builder() .setSingleList(itemListBuilder.build()) .setHeader(header) .build()) .setActionStrip(actionStrip) .setMapController(new MapController.Builder() .setMapActionStrip(mapActionStrip) .build()) .build();
RoutePreviewNavigationTemplate
Kotlin
// RoutePreviewNavigationTemplate (deprecated) val template = RoutePreviewNavigationTemplate.Builder() .setItemList( ItemList.Builder() .addItem( Row.Builder() .setTitle(title) .build()) .build()) .setHeader(header) .setNavigateAction( Action.Builder() .setTitle(actionTitle) .setOnClickListener { ... } .build()) .setActionStrip(actionStrip) .setMapActionStrip(mapActionStrip) .build() // MapWithContentTemplate val template = MapWithContentTemplate.Builder() .setContentTemplate( ListTemplate.Builder() .setSingleList( ItemList.Builder() .addItem( Row.Builder() .setTitle(title) .addAction( Action.Builder() .setTitle(actionTitle) .setOnClickListener { ... } .build()) .build()) .build()) .setHeader(header) .build()) .setActionStrip(actionStrip) .setMapController( MapController.Builder() .setMapActionStrip(mapActionStrip) .build()) .build()
Java
// RoutePreviewNavigationTemplate (deprecated) RoutePreviewNavigationTemplate template = new RoutePreviewNavigationTemplate.Builder() .setItemList(new ItemList.Builder() .addItem(new Row.Builder() .setTitle(title)) .build()) .build()) .setHeader(header) .setNavigateAction(new Action.Builder() .setTitle(actionTitle) .setOnClickListener(() -> { ... }) .build()) .setActionStrip(actionStrip) .setMapActionStrip(mapActionStrip) .build(); // MapWithContentTemplate MapWithContentTemplate template = new MapWithContentTemplate.Builder() .setContentTemplate(new ListTemplate.Builder() .setSingleList(new ItemList.Builder() .addItem(new Row.Builder() .setTitle(title)) .addAction(new Action.Builder() .setTitle(actionTitle) .setOnClickListener(() -> { ... }) .build()) .build()) .build())) .setHeader(header) .build()) .setActionStrip(actionStrip) .setMapController(new MapController.Builder() .setMapActionStrip(mapActionStrip) .build()) .build();
Informar metadados de navegação
Os apps de navegação precisam comunicar outros metadados ao host, que usa as informações para fornecer dados à unidade principal do veículo e evitar que esses apps entrem em conflito com os recursos compartilhados.
Os metadados de navegação são fornecidos pelo serviço de carro NavigationManager
, que pode ser acessado em 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 trajeto e dados de cluster do veículo, ele precisa estar ciente do estado de navegação atual. Quando um usuário iniciar a navegação, chame NavigationManager.navigationStarted
. Da mesma forma, quando a navegação terminar, por exemplo, ao chegar no destino ou cancelar a navegação, chame NavigationManager.navigationEnded
.
Chame NavigationManager.navigationEnded
apenas quando o usuário terminar a navegação. Por exemplo, se você precisar recalcular o trajeto no meio de uma viagem, use Trip.Builder.setLoading(true)
.
Ocasionalmente, o host precisa que um app interrompa a navegação e chame onStopNavigation
em um objeto NavigationManagerCallback
fornecido pelo seu app usando o NavigationManager.setNavigationManagerCallback
. Para isso, o app precisará 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.
Atualizar as informações da viagem
Durante a navegação ativa, chame NavigationManager.updateTrip
. As informações fornecidas nessa chamada podem ser usadas pelo cluster do veículo e pelos avisos na tela. Dependendo do veículo específico que está sendo dirigido, nem todas as informações são mostradas para o usuário. Por exemplo, a unidade principal da área de trabalho (DHU, na sigla em inglês) mostra a Step
adicionada à Trip
, mas não mostra a informação de Destination
.
Como desenhar na tela do cluster
Para oferecer uma experiência do usuário mais imersiva, vá além de mostrar metadados básicos na tela do cluster do veículo. A partir do nível 6 da API Car App, os apps de navegação têm a opção de renderizar o conteúdo diretamente na tela do cluster (em veículos com suporte), com as seguintes limitações:
- A API de tela do cluster não oferece suporte a controles de entrada.
- Diretriz de qualidade de apps para carros
NF-9
: a tela do cluster só pode mostrar blocos de mapas. Uma rota de navegação ativa pode ser mostrada nos blocos, se você quiser. - A API de tela do cluster só oferece suporte ao
NavigationTemplate
.- Ao contrário das telas principais, as telas do cluster podem não mostrar de maneira consistente todos os elementos da interface
NavigationTemplate
, como instruções passo a passo, cartões HEC e ações. Os blocos de mapas são o único elemento de interface mostrado de forma consistente.
- Ao contrário das telas principais, as telas do cluster podem não mostrar de maneira consistente todos os elementos da interface
Declarar suporte a cluster
Para informar ao aplicativo hospedado que seu app oferece suporte à renderização em telas de clusters, adicione um elemento androidx.car.app.category.FEATURE_CLUSTER
<category>
ao <intent-filter>
do CarAppService
, conforme mostrado no snippet a seguir:
<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"/> <category android:name="androidx.car.app.category.FEATURE_CLUSTER"/> </intent-filter> </service> ... </application>
Ciclo de vida e gerenciamento do estado
A partir do nível 6 da API, o fluxo de ciclo de vida do app para carro permanece o mesmo, mas agora CarAppService::onCreateSession
aceita um parâmetro do tipo SessionInfo
que oferece mais informações sobre a Session
que está sendo criada, (ou seja, o tipo de tela e o conjunto de modelos aceitos).
Os apps têm a opção de usar a mesma classe Session
para lidar com o cluster e a tela principal, ou de criar Sessions
específicas para personalizar o comportamento em cada tela, conforme mostrado no snippet a seguir.
Kotlin
override fun onCreateSession(sessionInfo: SessionInfo): Session { return if (sessionInfo.displayType == SessionInfo.DISPLAY_TYPE_CLUSTER) { ClusterSession() } else { MainDisplaySession() } }
Java
@Override @NonNull public Session onCreateSession(@NonNull SessionInfo sessionInfo) { if (sessionInfo.getDisplayType() == SessionInfo.DISPLAY_TYPE_CLUSTER) { return new ClusterSession(); } else { return new MainDisplaySession(); } }
Não há garantias sobre quando ou se a tela do cluster é fornecida. Também é possível que a Session
do cluster seja a única Session
(por exemplo, o usuário trocou a tela principal por outro app enquanto seu app está navegando). O acordo "padrão" é de que o app assume o controle da tela do cluster somente depois que NavigationManager::navigationStarted
é chamado. No entanto, é possível que o app receba a tela do cluster sem que haja nenhuma navegação ativa ou que nunca a receba. Cabe ao seu app lidar com esses cenários renderizando o estado inativo dos blocos de mapas.
O host cria instâncias separadas de binders e CarContext
por Session
. Isso significa que, ao usar métodos como ScreenManager::push
ou Screen::invalidate
, apenas a Session
na qual eles são chamados é afetada. Os apps precisam criar os próprios canais de comunicação entre essas instâncias se a comunicação entre cada Session
for necessária (por exemplo, usando transmissões, um Singleton compartilhado ou algo assim).
Como testar o suporte a clusters
Você pode testar a implementação no Android Auto e no Android Automotive OS. No Android Auto, isso é feito configurando a unidade principal do computador para emular uma tela de cluster secundária. No Android Automotive OS, as imagens genéricas do sistema para o nível 30 da API e versões mais recentes emulam uma tela de cluster.
Personalizar a TravelEstimate com texto ou ícone
Para personalizar a estimativa de viagem com texto, ícone ou ambos, use os métodos setTripIcon
ou setTripText
da classe TravelEstimate.Builder
. O NavigationTemplate
usa a TravelEstimate
para definir texto e ícones opcionais ao lado ou no lugar dos dados de horário previsto de chegada, tempo restante e distância restante.

O snippet a seguir usa setTripIcon
e setTripText
para personalizar a estimativa de viagem:
Kotlin
TravelEstimate.Builder(Distance.create(...), DateTimeWithZone.create(...)) ... .setTripIcon(CarIcon.Builder(...).build()) .setTripText(CarText.create(...)) .build()
Java
new TravelEstimate.Builder(Distance.create(...), DateTimeWithZone.create(...)) ... .setTripIcon(CarIcon.Builder(...).build()) .setTripText(CarText.create(...)) .build();
Fornecer notificações de navegação guiada
Dê instruções de navegação guiada usando uma notificação atualizada com frequência. Para ser tratado como uma notificação de navegação na tela do carro, o builder da notificação precisa fazer o seguinte:
- Marcar a notificação como em andamento com o método
NotificationCompat.Builder.setOngoing
. - Definir a categoria da notificação como
Notification.CATEGORY_NAVIGATION
. - Estender a notificação com um
CarAppExtender
.
Uma notificação de navegação é mostrada no widget de coluna na parte de baixo da tela do carro. Se o nível de importância da notificação estiver definido como IMPORTANCE_HIGH
, ela também será mostrada como notificação de alerta (HUN, na sigla em inglês). Se a importância não for 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 é enviada ao app quando o usuário toca na HUN ou no widget de coluna.
Se o método NotificationCompat.Builder.setOnlyAlertOnce
for chamado com um 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();
Atualize a notificação da navegação guiada regularmente para mudanças de distância, o que atualiza o widget de coluna, e só mostre a notificação como uma HUN. É possível controlar o comportamento da HUN definindo a importância da notificação com CarAppExtender.Builder.setImportance
. A definição da importância como IMPORTANCE_HIGH
mostra uma HUN. A definição com qualquer outro valor atualiza apenas o widget de coluna.
Atualizar o conteúdo do PlaceListNavigationTemplate
Você pode permitir que os motoristas atualizem o conteúdo com um simples toque de botão enquanto navegam em listas de lugares criadas com o PlaceListNavigationTemplate
. Para ativar a atualização da lista, implemente o método onContentRefreshRequested
da interface OnContentRefreshListener
e use PlaceListNavigationTemplate.Builder.setOnContentRefreshListener
para definir o listener no modelo.
O snippet a seguir mostra como definir o listener no modelo:
Kotlin
PlaceListNavigationTemplate.Builder() ... .setOnContentRefreshListener { // Execute any desired logic ... // Then call invalidate() so onGetTemplate() is called again invalidate() } .build()
Java
new PlaceListNavigationTemplate.Builder() ... .setOnContentRefreshListener(() -> { // Execute any desired logic ... // Then call invalidate() so onGetTemplate() is called again invalidate(); }) .build();
O botão de atualização só aparece no cabeçalho do PlaceListNavigationTemplate
quando o listener tem um valor.
Quando o motorista clica no botão, o método onContentRefreshRequested
da sua implementação de OnContentRefreshListener
é chamado. No onContentRefreshRequested
, chame o método Screen.invalidate
. Em seguida, o host vai chamar o método Screen.onGetTemplate
do app para recuperar o modelo com o conteúdo atualizado. Consulte Atualizar o conteúdo de um modelo para saber mais sobre a atualização de modelos. Contanto que o próximo modelo retornado por onGetTemplate
seja do mesmo tipo, ele será contabilizado como uma atualização, e não na cota do modelo.
Oferecer orientações por áudio
Para tocar a orientação de navegação nos alto-falantes do carro, seu app precisa solicitar seleção de áudio. Como parte da AudioFocusRequest
, defina o uso como AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE
. Além disso, defina o ganho da seleção como AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
.
Simular a navegação
Para verificar a funcionalidade de navegação do app ao enviá-lo à Google Play Store, é necessário que ele implemente 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 poderá 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
Isso é mostrado neste exemplo:
adb shell dumpsys activity service androidx.car.app.samples.navigation.car.NavigationCarAppService AUTO_DRIVE
App de navegação padrão para carros
No Android Auto, o app de navegação padrão para carros corresponde ao último app desse tipo que o usuário iniciou. O app padrão recebe as intents de navegação quando o usuário invoca comandos pelo Google Assistente ou quando outro app envia uma intent para iniciar a navegação.
Mostrar alertas de navegação no contexto
O Alert
mostra informações importantes para o motorista com ações opcionais sem sair do contexto da tela de navegação. Para oferecer a melhor experiência ao motorista, o Alert
funciona no NavigationTemplate
para evitar o bloqueio do trajeto de navegação e minimizar a distração do motorista.
O Alert
está disponível apenas no NavigationTemplate
. Para notificar um usuário fora do NavigationTemplate
, use uma notificação de alerta, conforme explicado em Mostrar notificações.
Por exemplo, use Alert
para:
- informar o motorista sobre uma atualização relevante para a navegação atual, como uma mudança nas condições de trânsito;
- pedir ao motorista uma atualização relacionada à navegação atual, como a existência de um radar móvel;
- propor uma próxima tarefa e perguntar se o motorista a aceita. Por exemplo, se o motorista quer pegar alguém no caminho.
Na forma básica, um Alert
consiste em um título e na duração do Alert
. A duração é representada por uma barra de progresso. Também é possível adicionar um subtítulo, um ícone e até dois objetos Action
.

Depois que um Alert
é mostrado, ele não é transferido para outro modelo quando uma interação do motorista resulta na saída do NavigationTemplate
. Ele permanece no NavigationTemplate
original até o Alert
expirar, o usuário realizar uma ação ou o app dispensar o Alert
.
Criar um alerta
Use Alert.Builder
para criar uma instância de Alert
:
Kotlin
Alert.Builder( /*alertId*/ 1, /*title*/ CarText.create("Hello"), /*durationMillis*/ 5000 ) // The fields below are optional .addAction(firstAction) .addAction(secondAction) .setSubtitle(CarText.create(...)) .setIcon(CarIcon.APP_ICON) .setCallback(...) .build()
Java
new Alert.Builder( /*alertId*/ 1, /*title*/ CarText.create("Hello"), /*durationMillis*/ 5000 ) // The fields below are optional .addAction(firstAction) .addAction(secondAction) .setSubtitle(CarText.create(...)) .setIcon(CarIcon.APP_ICON) .setCallback(...) .build();
Para ouvir o cancelamento ou a dispensa do Alert
, crie uma implementação da interface AlertCallback
. Estes são os caminhos de chamada de AlertCallback
:
Se o
Alert
expirar, o host vai chamar o métodoAlertCallback.onCancel
com o valorAlertCallback.REASON_TIMEOUT
. Em seguida, ele chamará o métodoAlertCallback.onDismiss
.Se o motorista clicar em um dos botões de ação, o host vai chamar
Action.OnClickListener
e depoisAlertCallback.onDismiss
.Se não houver suporte para o
Alert
, o host chamaráAlertCallback.onCancel
com o valorAlertCallback.REASON_NOT_SUPPORTED
. O host não chamaAlertCallback.onDismiss
porque oAlert
não foi mostrado.
Configurar a duração do alerta
Escolha uma duração de Alert
que corresponda às necessidades do app. A duração recomendada para um Alert
de navegação é de 10 segundos. Consulte Alertas de navegação para mais informações.
Mostrar um alerta
Para mostrar um Alert
, chame o método AppManager.showAlert
, disponível no CarContext
do app.
// Show an alert carContext.getCarService(AppManager.class).showAlert(alert)
- Chamar
showAlert
com umAlert
que tenha umalertId
igual ao ID doAlert
mostrado na tela não gera nenhuma ação. OAlert
não é atualizado. Para atualizar umAlert
, ele precisa ser recriado com um novoalertId
. - Chamar
showAlert
com umAlert
que tenha umalertId
diferente doAlert
mostrado na tela dispensa oAlert
que está em exibição no momento.
Dispensar um alerta
Embora um Alert
seja dispensado automaticamente devido ao tempo limite ou à interação do motorista, você também pode dispensar um Alert
de forma manual, por exemplo, caso as informações fiquem desatualizadas. Para dispensar um Alert
chame o método dismissAlert
com o alertId
do Alert
.
// Dismiss the same alert carContext.getCarService(AppManager.class).dismissAlert(alert.getId())
Chamar dismissAlert
com um alertId
que não corresponde ao Alert
mostrado no momento não resulta em nenhuma ação. Isso não gera uma exceção.