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
Uma variedade de formatos de intent permite que os 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. A localização desses filtros de intent depende da plataforma:
- Android Auto: no elemento de manifesto
<activity>para aActivityusada 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 de Session do app.
Formatos de intent obrigatórios
Para atender ao requisito de qualidade NF-6, seu app precisa processar
intents 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: um modelo que mostra uma mensagem informativa opcional e estimativas de viagem durante a navegação ativa.MapWithContentTemplate: um modelo que permite que um app renderize blocos de mapas com algum tipo de conteúdo (por exemplo, uma lista). O conteúdo geralmente é renderizado como uma sobreposição na parte de cima dos blocos de mapas, com o mapa visível e áreas estáveis ajustadas ao conteúdo.
Para mais detalhes sobre como projetar a interface do usuário do 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 outra permissão para desenhar mapas.
Migrar para o MapWithContentTemplate
A partir do nível 7 da API Car App, os
MapTemplate,
PlaceListNavigationTemplate,
e RoutePreviewNavigationTemplate
foram descontinuados. Os modelos descontinuados continuarão sendo aceitos, mas é altamente recomendável migrar para o MapWithContentTemplate.
A funcionalidade fornecida por esses modelos pode ser implementada usando o MapWithContentTemplate. Consulte os snippets a seguir para exemplos:
MapTemplate
// MapTemplate (deprecated) val templateDeprecated = 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()
PlaceListNavigationTemplate
// PlaceListNavigationTemplate (deprecated) val templateDeprecated = 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()
RoutePreviewNavigationTemplate
// RoutePreviewNavigationTemplate (deprecated) val templateDeprecated = RoutePreviewNavigationTemplate.Builder() .setItemList( ItemList.Builder() .addItem( Row.Builder() .setTitle(title) .build() ) .build() ) .setHeader(header) .setNavigateAction( Action.Builder() .setTitle(actionTitle) .setOnClickListener { /* onClick */ } .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 { /* onClick */ } .build() ) .build() ) .build() ) .setHeader(header) .build() ) .setActionStrip(actionStrip) .setMapController( 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. O host usa as informações para fornecer dados à unidade principal do veículo e impedir que aplicativos de navegação entrem em conflito com os recursos compartilhados.
Os metadados de navegação são fornecidos pelo
NavigationManager
serviço de carro acessível em
CarContext:
val navigationManager = carContext.getCarService(NavigationManager::class.java)
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
o Step adicionado ao
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ó mostra blocos de mapas. Uma rota de navegação ativa pode ser mostrada nesses blocos, se desejar. - 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 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.
override fun onCreateSession(sessionInfo: SessionInfo): Session { return if (sessionInfo.displayType == SessionInfo.DISPLAY_TYPE_CLUSTER) { ClusterSession() } else { 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:
TravelEstimate.Builder( Distance.create(350.0, Distance.UNIT_METERS), arrivalTimeAtDestination ) .setTripIcon( CarIcon.Builder( IconCompat.createWithResource(carContext, R.drawable.ic_garage) ).build() ) .setTripText(CarText.create("Custom Text")) .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
NotificationCompat.Builder.setOngoingmétodo. - Definir a categoria da notificação como
Notification.CATEGORY_NAVIGATION. - Estender a notificação com a
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:
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) ), PendingIntent.FLAG_IMMUTABLE ) ) .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:
PlaceListNavigationTemplate.Builder() .setOnContentRefreshListener { // Execute any desired logic // Then call invalidate() so onGetTemplate() is called again screen.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
Screen.invalidate método.
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
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,
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:
Alert.Builder( 1, // alertId CarText.create("Hello"), // title 5000 // durationMillis ) // The fields below are optional .addAction(firstAction) .addAction(secondAction) .setSubtitle(CarText.create("Subtitle")) .setIcon(CarIcon.APP_ICON) .setCallback(alertCallback) .build()
Para ouvir o Alert
cancelamento ou a dispensa, crie uma implementação da
AlertCallback interface.
Estes são os caminhos de chamada de AlertCallback:
Se o
Alertexpirar, o host vai chamar o métodoAlertCallback.onCancelcom 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.OnClickListenere depois chamarAlertCallback.onDismiss.Se não houver suporte para o
Alert, o host chamaráAlertCallback.onCancelcom oAlertCallback.REASON_NOT_SUPPORTEDvalor. O host não chamaAlertCallback.onDismissporque oAlertnão foi mostrado.
Configurar a duração do alerta
Escolha uma Alert duração 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
AppManager.showAlert
método disponível no
CarContext do app.
carContext.getCarService(AppManager::class.java).showAlert(alert)
- Chamar
showAlertcom umAlertque tenha umalertIdigual ao ID doAlertjá mostrado não gera nenhuma ação. OAlertnão é atualizado. Para atualizar umAlert, ele precisa ser recriado com um novoalertId. - Chamar
showAlertcom umAlertque tenha umalertIddiferente doAlertjá mostrado dispensa o alertaAlertmostrado.
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
dismissAlert
método com o
alertId
do Alert.
carContext.getCarService(AppManager::class.java).dismissAlert(alert.id)
Chamar dismissAlert com um alertId que não corresponde ao Alert já
mostrado não gera nenhuma ação. Isso não gera uma exceção.