A biblioteca Android for Cars App permite que você leve seus apps de navegação, ponto de interesse (PDI) e Internet das Coisas (IoT) para o carro. Para isso, ele fornece um conjunto de modelos projetados para atender aos padrões de distração do motorista e cuidando de detalhes como a variedade de fatores da tela do carro e modalidades de entrada.
Este guia fornece uma visão geral dos principais recursos e conceitos da biblioteca e orienta o processo de configuração de um app simples. Para uma introdução completa, confira o codelab de conceitos básicos da biblioteca Aprender Car App.
Antes de começar
- Consulte as páginas Design for Driving,
que abrangem a biblioteca Car App.
- Visão geral das categorias Apps de navegação e Outros apps relacionados a condução
- Visão geral de Criar apps com modelos
- Elementos básicos que abrangem Modelos e Componentes do modelo
- Fluxos de amostra que demonstram padrões comuns de UX
- Requisitos de apps de modelo
- Revise os principais termos e conceitos na seção a seguir.
- Familiarize-se com a interface do sistema do Android Auto e o design do Android Automotive OS.
- Leia as Notas da versão.
- Analise os Exemplos (link em inglês).
Principais termos e conceitos
- Estilos e modelos
- A interface do usuário é representada por um gráfico de objetos de modelo que podem ser organizados de maneiras diferentes, conforme permitido pelo modelo a que pertencem. Modelos são um subconjunto dos modelos que podem atuar 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, como cores do 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 de back-end que implementa a funcionalidade oferecida pelas APIs da biblioteca para que o app possa ser executado no carro. As responsabilidades do host variam de descobrir seu app e gerenciar o ciclo de vida dele até converter seus modelos em visualizações e notificar seu app sobre interações do usuário. Em dispositivos móveis, esse host é implementado pelo Android Auto. No Android Automotive OS, esse host é instalado como um app do sistema.
- Restrições de modelos
- Diferentes modelos impõem restrições ao conteúdo dos estilos principais. Por exemplo, os modelos de lista têm limites quanto ao número de itens que podem ser apresentados ao usuário. Os modelos também têm restrições quanto à maneira como podem ser conectados para formar o fluxo de uma tarefa. Por exemplo, o app só pode enviar até cinco modelos para a pilha de telas. Consulte Restrições de modelo para mais detalhes.
Screen
Screen
é uma classe fornecida pela biblioteca que os apps implementam para gerenciar a interface do usuário apresentada ao usuário. UmaScreen
tem um ciclo de vida e fornece o mecanismo para o app enviar o modelo a ser exibido quando a tela estiver visível. As instânciasScreen
também podem ser enviadas e exibidas de e para uma pilhaScreen
, o que garante que elas sigam as restrições de fluxo do modelo.CarAppService
CarAppService
é uma classeService
abstrata que o app precisa implementar e exportar para ser descoberto e gerenciado pelo host. OCarAppService
do app é responsável por validar se uma conexão de host é confiável usandocreateHostValidator
e, depois, fornecer instâncias deSession
para cada conexão usandoonCreateSession
.Session
Session
é uma classe abstrata que o app precisa implementar e retornar usandoCarAppService.onCreateSession
. Ela serve como ponto de entrada para mostrar informações na tela do carro. Ela tem um ciclo de vida que informa o estado atual do app na tela do carro, como quando o app está visível ou oculto.Quando um
Session
é iniciado, por exemplo, quando o app é iniciado pela primeira vez, o host solicita que oScreen
inicial seja mostrado usando o métodoonCreateScreen
.
Instalar a biblioteca Car App
Consulte a página de lançamento da biblioteca do Jetpack para instruções sobre como adicionar a biblioteca ao seu app.
Configurar os arquivos de manifesto do app
Antes de criar um app para carro, configure os arquivos de manifesto dele da seguinte maneira.
Declarar o CarAppService
O host se conecta ao app usando a
implementação do CarAppService
. Declare
esse serviço no manifesto para permitir que o host descubra e se conecte
ao seu app.
Também é necessário declarar a categoria do app no elemento
<category>
do filtro de intent. Consulte a lista de
categorias de apps com suporte para conferir os valores permitidos para
esse elemento.
O snippet de código abaixo mostra como declarar um serviço de app para carros para um app de ponto de interesse no 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.POI"/>
</intent-filter>
</service>
...
<application>
Categorias de apps compatíveis
Declare a categoria do seu app adicionando um ou mais dos seguintes valores
de categoria no filtro de intent ao declarar a CarAppService
conforme descrito
na seção anterior:
androidx.car.app.category.NAVIGATION
: um app que fornece rotas de navegação guiada. Confira Criar apps de navegação para carros para mais documentos sobre essa categoria.androidx.car.app.category.POI
: um app que oferece funcionalidades relevantes para encontrar pontos de interesse, como vagas de estacionamento, postos de recarga e postos de gasolina. Confira Criar apps de ponto de interesse para carros para conferir mais documentação sobre essa categoria.androidx.car.app.category.IOT
: um app que permite aos usuários realizar ações relevantes em dispositivos conectados de dentro do carro. Consulte Criar apps de Internet das Coisas para carros para conferir mais documentação sobre essa categoria.
Consulte Qualidade de apps Android para carros e conferir descrições detalhadas de cada categoria e critérios para que os apps pertençam a eles.
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 usados para representar seu app usando
os atributos label
e
icon
da
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 vai
recorrer aos valores especificados para o elemento
<application>
.
Definir um tema personalizado
Para definir um tema personalizado para seu app para carro, adicione um elemento
<meta-data>
ao
arquivo de manifesto desta forma:
<meta-data android:name="androidx.car.app.theme" android:resource="@style/MyCarAppTheme />
Em seguida, declare o recurso de estilo para definir os seguintes atributos para o tema personalizado do app para carro:
<resources> <style name="MyCarAppTheme"> <item name="carColorPrimary">@layout/my_primary_car_color</item> <item name="carColorPrimaryDark">@layout/my_primary_dark_car_color</item> <item name="carColorSecondary">@layout/my_secondary_car_color</item> <item name="carColorSecondaryDark">@layout/my_secondary_dark_car_color</item> <item name="carPermissionActivityLayout">@layout/my_custom_background</item> </style> </resources>
Nível da API Car App
A biblioteca Car App define os próprios níveis de API para que você saiba quais
recursos da biblioteca têm suporte do host de modelo em um veículo.
Para extrair o nível mais alto da API Car App compatível com um host, use o
método
getCarAppApiLevel()
.
Declare o nível mínimo da API Car App com suporte ao app no
arquivo AndroidManifest.xml
:
<manifest ...>
<application ...>
<meta-data
android:name="androidx.car.app.minCarApiLevel"
android:value="1"/>
</application>
</manifest>
Consulte a documentação da anotação
RequiresCarApi
para ver detalhes sobre como manter a compatibilidade com versões anteriores e declarar
o nível mínimo de API necessário para usar um recurso. Para ver uma definição de qual nível
da API é necessário para usar determinado recurso da biblioteca Car App, consulte a
documentação de referência de
CarAppApiLevels
.
Criar CarAppService e Session
Seu app precisa ampliar a classe
CarAppService
e implementar
o método
onCreateSession
, que retorna uma instância Session
correspondente à conexão atual com o host:
Kotlin
class HelloWorldService : CarAppService() { ... override fun onCreateSession(): Session { return HelloWorldSession() } ... }
Java
public final class HelloWorldService extends CarAppService { ... @Override @NonNull public Session onCreateSession() { return new HelloWorldSession(); } ... }
A instância Session
é responsável por
retornar a instância Screen
para usar na
primeira vez que o app for iniciado:
Kotlin
class HelloWorldSession : Session() { ... override fun onCreateScreen(intent: Intent): Screen { return HelloWorldScreen(carContext) } ... }
Java
public final class HelloWorldSession extends Session { ... @Override @NonNull public Screen onCreateScreen(@NonNull Intent intent) { return new HelloWorldScreen(getCarContext()); } ... }
Para lidar com cenários em que o app para carro precisa começar em uma tela que não seja
a inicial ou de destino do app, como ao processar links diretos, você pode
preparar uma backstack 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 mostradas pelo app, defina classes que estendam a
classe Screen
e implemente o método
onGetTemplate
,
que retorna a instância
Template
, que representa
o estado da interface que será mostrada na tela do carro.
O snippet a seguir mostra como declarar um
Screen
que usa um
modelo PaneTemplate
para
exibir uma string "Hello world!" simples:
Kotlin
class HelloWorldScreen(carContext: CarContext) : Screen(carContext) { override fun onGetTemplate(): Template { val row = Row.Builder().setTitle("Hello world!").build() val pane = Pane.Builder().addRow(row).build() return PaneTemplate.Builder(pane) .setHeaderAction(Action.APP_ICON) .build() } }
Java
public class HelloWorldScreen extends Screen { @NonNull @Override public Template onGetTemplate() { Row row = new Row.Builder().setTitle("Hello world!").build(); Pane pane = new Pane.Builder().addRow(row).build(); return new PaneTemplate.Builder(pane) .setHeaderAction(Action.APP_ICON) .build(); } }
Classe CarContext
A classe CarContext
é uma
subclasse ContextWrapper
acessível às instâncias Session
e
Screen
. Ela fornece acesso
a serviços do carro, como o
ScreenManager
para gerenciar a
pilha de telas; a
AppManager
para funcionalidades gerais
relacionadas ao app, como acessar o objeto Surface
para desenhar o
mapa do app de navegação e o
NavigationManager
usado por apps de navegação guiada para comunicar a navegação
de navegação e outros
eventos do
host.
Consulte Acessar os modelos de navegação para uma lista abrangente de funcionalidades da biblioteca disponíveis para apps de navegação.
O CarContext
também oferece outras
funcionalidades, como permitir o carregamento de recursos drawable 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
Geralmente, os apps apresentam várias telas diferentes, cada uma possivelmente usando modelos diferentes para que o usuário possa navegar à medida que interage com a interface mostrada na tela.
A classe ScreenManager
fornece
uma pilha de telas que pode 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 "Voltar"
do hardware disponível em alguns carros.
O snippet a seguir mostra como adicionar uma ação "Voltar" a um modelo de mensagem, bem como uma ação que envia uma nova tela quando selecionada pelo usuário:
Kotlin
val template = MessageTemplate.Builder("Hello world!") .setHeaderAction(Action.BACK) .addAction( Action.Builder() .setTitle("Next screen") .setOnClickListener { screenManager.push(NextScreen(carContext)) } .build()) .build()
Java
MessageTemplate template = new MessageTemplate.Builder("Hello world!") .setHeaderAction(Action.BACK) .addAction( new Action.Builder() .setTitle("Next screen") .setOnClickListener( () -> getScreenManager().push(new NextScreen(getCarContext()))) .build()) .build();
O objeto Action.BACK
é uma
Action
padrão que invoca automaticamente
ScreenManager.pop
.
Esse comportamento pode ser substituído usando a instância
OnBackPressedDispatcher
disponível em
CarContext
.
Para garantir que o app seja seguro para uso ao dirigir, a pilha de telas pode ter uma profundidade máxima de cinco telas. Consulte a seção Restrições de modelo para mais detalhes.
Atualizar o conteúdo de um modelo
Seu app pode solicitar que o conteúdo de uma
Screen
seja invalidado chamando o
método Screen.invalidate
.
Em seguida, o host vai chamar o método
Screen.onGetTemplate
do app para recuperar o modelo com o novo conteúdo.
Ao atualizar um Screen
, é
importante entender o conteúdo específico do modelo que pode ser atualizado
para que o host não conte o novo modelo na cota do modelo.
Consulte a seção Restrições de modelo para mais detalhes.
Recomendamos que você estruture as telas para que haja um mapeamento um para um
entre um Screen
e o tipo de modelo
que ele retorna com a implementação de onGetTemplate
.
Interagir com o usuário
Seu app pode interagir com o usuário usando padrões semelhantes a um app para dispositivos móveis.
Processar entrada do usuário
Seu app pode responder à entrada do usuário transmitindo os listeners adequados
aos estilos compatíveis. O snippet a seguir mostra como criar um
modelo de Action
que define um
OnClickListener
que
chama de volta um método definido pelo código do app:
Kotlin
val action = Action.Builder() .setTitle("Navigate") .setOnClickListener(::onClickNavigate) .build()
Java
Action action = new Action.Builder() .setTitle("Navigate") .setOnClickListener(this::onClickNavigate) .build();
O método onClickNavigate
pode iniciar o
app de navegação padrão para carro
usando o
método
CarContext.startCarApp
:
Kotlin
private fun onClickNavigate() { val intent = Intent(CarContext.ACTION_NAVIGATE, Uri.parse("geo:0,0?q=" + address)) carContext.startCarApp(intent) }
Java
private void onClickNavigate() { Intent intent = new Intent(CarContext.ACTION_NAVIGATE, Uri.parse("geo:0,0?q=" + address)); getCarContext().startCarApp(intent); }
Para saber mais sobre como iniciar apps, incluindo o formato da intent
ACTION_NAVIGATE
, consulte a seção
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.
Use o
ParkedOnlyOnClickListener
para implementar essas ações. Se o carro não estiver estacionado, o host vai mostrar uma
indicação ao usuário de que a ação não é permitida nesse caso. Com o carro
estacionado, o código é executado normalmente. O snippet a seguir mostra como
usar ParkedOnlyOnClickListener
para abrir uma tela de configurações no dispositivo móvel:
Kotlin
val row = Row.Builder() .setTitle("Open Settings") .setOnClickListener(ParkedOnlyOnClickListener.create(::openSettingsOnPhone)) .build()
Java
Row row = new Row.Builder() .setTitle("Open Settings") .setOnClickListener(ParkedOnlyOnClickListener.create(this::openSettingsOnPhone)) .build();
Mostrar notificações
As notificações enviadas para o dispositivo móvel só vão aparecer 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 eles aparecem 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:
Kotlin
val notification = NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID) .setContentTitle(titleOnThePhone) .extend( CarAppExtender.Builder() .setContentTitle(titleOnTheCar) ... .build()) .build()
Java
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.
- Para apps de navegação, a notificação pode ser mostrada no widget de coluna, conforme descrito em Notificações passo a passo.
Você pode escolher como configurar as notificações do app para afetar cada um
desses elementos da interface do usuário usando a prioridade da notificação, conforme descrito
na
documentação
CarAppExtender
.
Se o método
NotificationCompat.Builder.setOnlyAlertOnce
for chamado com um valor true
, uma notificação de alta prioridade vai ser mostrada como
HUN apenas uma vez.
Para saber mais sobre como criar as notificações do seu app para carro, consulte o guia do Google Design para direção sobre Notificações.
Mostrar avisos
Seu app pode mostrar um aviso usando
CarToast
, conforme mostrado neste snippet:
Kotlin
CarToast.makeText(carContext, "Hello!", CarToast.LENGTH_SHORT).show()
Java
CarToast.makeText(getCarContext(), "Hello!", CarToast.LENGTH_SHORT).show();
Solicitar permissões
Se o app precisar acessar dados ou ações restritos, por exemplo,
a localização, as regras padrão das permissões
do Android
serão aplicadas. Para solicitar uma permissão, use o
método
CarContext.requestPermissions()
.
A vantagem de usar
CarContext.requestPermissions()
, em vez de usar
APIs padrão do Android, é
que não é necessário iniciar seu próprio Activity
para
criar a caixa de diálogo de permissões. Além disso, você pode usar o mesmo código no
Android Auto e no Android Automotive OS, em vez de ter que criar
fluxos dependentes da plataforma.
Definir o estilo da caixa de diálogo de permissões no Android Auto
No Android Auto, a caixa de diálogo de permissões do usuário aparece no smartphone.
Por padrão, não haverá plano de fundo atrás da caixa de diálogo. Para definir um plano de fundo
personalizado, declare um tema de app para carros no
arquivo AndroidManifest.xml
e defina o atributo carPermissionActivityLayout
para o tema do app para carro.
<meta-data android:name="androidx.car.app.theme" android:resource="@style/MyCarAppTheme />
Em seguida, defina o atributo carPermissionActivityLayout
para o tema do app para carro:
<resources> <style name="MyCarAppTheme"> <item name="carPermissionActivityLayout">@layout/my_custom_background</item> </style> </resources>
Iniciar um app para carro com uma intent
Você pode chamar o método
CarContext.startCarApp
para realizar uma das seguintes ações:
- Abrir o discador para fazer uma chamada telefônica
- Inicie a navegação guiada em um local com o app de navegação padrão para carros.
- 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 contenha um
PendingIntent
envolvendo uma intent
explícita para a ação do app:
Kotlin
val notification = notificationBuilder ... .extend( CarAppExtender.Builder() .setContentIntent( PendingIntent.getBroadcast( context, ACTION_VIEW_PARKING_RESERVATION.hashCode(), Intent(ACTION_VIEW_PARKING_RESERVATION) .setComponent(ComponentName(context, MyNotificationReceiver::class.java)), 0)) .build())
Java
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());
O app 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:
Kotlin
class MyNotificationReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { val intentAction = intent.action if (ACTION_VIEW_PARKING_RESERVATION == intentAction) { CarContext.startCarApp( intent, Intent(Intent.ACTION_VIEW) .setComponent(ComponentName(context, MyCarAppService::class.java)) .setData(Uri.fromParts(MY_URI_SCHEME, MY_URI_HOST, intentAction))) } } }
Java
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 ela ainda não esteja na parte de cima:
Kotlin
override fun onNewIntent(intent: Intent) { val screenManager = carContext.getCarService(ScreenManager::class.java) val uri = intent.data if (uri != null && MY_URI_SCHEME == uri.scheme && MY_URI_HOST == uri.schemeSpecificPart && ACTION_VIEW_PARKING_RESERVATION == uri.fragment ) { val top = screenManager.top if (top !is ParkingReservationScreen) { screenManager.push(ParkingReservationScreen(carContext)) } } }
Java
@Override public void onNewIntent(@NonNull Intent intent) { ScreenManager screenManager = getCarContext().getCarService(ScreenManager.class); 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.push(new ParkingReservationScreen(getCarContext())); } } }
Consulte a seção Mostrar notificações para mais informações sobre como lidar com notificações do app para carro.
Restrições de modelos
O host limita o número de modelos a serem exibidos para uma determinada tarefa a um máximo de cinco. O último modelo precisa ser um dos seguintes tipos:
Observe que esse limite se aplica ao número de modelos e não ao número de
instâncias de Screen
na pilha. Por
exemplo, se um app enviar dois modelos enquanto estiver na tela A e depois abrir a tela
B, ele poderá enviar mais três modelos. Como alternativa, se cada tela estiver estruturada
para enviar um único modelo, o app poderá enviar cinco instâncias de tela para a
pilha ScreenManager
.
Há casos especiais para essas restrições: atualizações de modelos e operações de retorno e redefinição.
Atualizações de modelos
Algumas atualizações de conteúdo não são contabilizadas no limite de modelos. Em geral,
se um app envia um novo modelo que é do mesmo tipo e tem
o mesmo conteúdo principal que o modelo anterior, o novo modelo não é
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 modelos 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á extraindo um
Screen
da pilha ScreenManager
e atualiza
a cota restante com base no número de modelos que o app está
recuando.
Por exemplo, se o app enviar dois modelos enquanto estiver na tela A, pressionar a tela B e enviar mais dois modelos, o app terá uma cota restante. Se o app voltar para a tela A, o host vai redefinir a cota para três, porque o app voltou em dois modelos.
Ao retornar a uma tela, um app precisa enviar um modelo do mesmo tipo que o último enviado por essa tela. O envio de qualquer outro tipo de modelo causa 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 modelo sem afetar a cota.
Redefinir operações
Alguns modelos têm uma semântica especial que representa o final de uma tarefa. Por
exemplo, a
NavigationTemplate
é uma visualização que precisa permanecer na tela e ser atualizada com novas
instruções detalhadas para o consumo do usuário. Quando chega a um desses
modelos, o host redefine a cota e trata o modelo como se
fosse a primeira etapa de uma nova tarefa. Isso permite que o app inicie uma nova tarefa.
Consulte a documentação de modelos 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 mesmo que um app já esteja vinculado e em primeiro plano.
Consulte a seção Mostrar notificações para mais detalhes sobre como mostrar as notificações do seu app na tela do carro. Consulte a seção Iniciar um app para carro com uma intent para informações sobre como iniciar o app com uma ação de notificação.
API Connection
É possível determinar se o app está sendo executado no Android Auto ou no Android
Automotive OS usando a
API CarConnection
para
extrair informações de conexão durante a execução.
Por exemplo, no Session
do app para carro, inicialize uma CarConnection
e
inscreva-se para receber as atualizações do LiveData
:
Kotlin
CarConnection(carContext).type.observe(this, ::onConnectionStateUpdated)
Java
new CarConnection(getCarContext()).getType().observe(this, this::onConnectionStateUpdated);
No observador, é possível reagir às mudanças no estado da conexão:
Kotlin
fun onConnectionStateUpdated(connectionState: Int) { val message = when(connectionState) { CarConnection.CONNECTION_TYPE_NOT_CONNECTED -> "Not connected to a head unit" CarConnection.CONNECTION_TYPE_NATIVE -> "Connected to Android Automotive OS" CarConnection.CONNECTION_TYPE_PROJECTION -> "Connected to Android Auto" else -> "Unknown car connection type" } CarToast.makeText(carContext, message, CarToast.LENGTH_SHORT).show() }
Java
private void onConnectionStateUpdated(int connectionState) { String message; switch(connectionState) { case CarConnection.CONNECTION_TYPE_NOT_CONNECTED: message = "Not connected to a head unit"; break; case CarConnection.CONNECTION_TYPE_NATIVE: message = "Connected to Android Automotive OS"; break; case CarConnection.CONNECTION_TYPE_PROJECTION: message = "Connected to Android Auto"; break; default: message = "Unknown car connection type"; break; } CarToast.makeText(getCarContext(), message, CarToast.LENGTH_SHORT).show(); }
API Constraints
Carros diferentes podem permitir que um número diferente de instâncias de
Item
seja mostrado ao
usuário de cada vez. Use o
ConstraintManager
para conferir o limite de conteúdo durante a execução e definir o número adequado de itens
nos modelos.
Comece recebendo um ConstraintManager
do CarContext
:
Kotlin
val manager = carContext.getCarService(ConstraintManager::class.java)
Java
ConstraintManager manager = getCarContext().getCarService(ConstraintManager.class);
Em seguida, consulte o objeto ConstraintManager
recuperado para o limite
de conteúdo relevante. Por exemplo, para conferir o número de itens que podem ser mostrados em
uma grade, chame
getContentLimit
com
CONTENT_LIMIT_TYPE_GRID
:
Kotlin
val gridItemLimit = manager.getContentLimit(ConstraintManager.CONTENT_LIMIT_TYPE_GRID)
Java
int gridItemLimit = manager.getContentLimit(ConstraintManager.CONTENT_LIMIT_TYPE_GRID);
Adicionar um fluxo de login
Caso seu app ofereça uma experiência com login para os usuários, você pode usar modelos como
SignInTemplate
e LongMessageTemplate
com a API Car App de nível 2 e mais recentes para processar o login no app na
unidade principal do carro.
Para criar uma SignInTemplate
, defina uma SignInMethod
. Atualmente, a biblioteca
Car App tem suporte aos seguintes métodos de login:
InputSignInMethod
para fazer login com nome de usuário/senha.PinSignInMethod
para login por PIN, em que o usuário vincula a conta do smartphone usando um PIN exibido na unidade principal.ProviderSignInMethod
para fazer login no provedor, como o Login do Google e o um toque.QRCodeSignInMethod
para fazer login com um QR code, em que o usuário lê um QR code para concluir o login no smartphone. Esse recurso está disponível no nível 4 da API Car e versões mais recentes.
Por exemplo, para implementar um modelo que colete a senha do usuário, comece criando um InputCallback
para processar e validar a entrada do usuário:
Kotlin
val callback = object : InputCallback { override fun onInputSubmitted(text: String) { // You will receive this callback when the user presses Enter on the keyboard. } override fun onInputTextChanged(text: String) { // You will receive this callback as the user is typing. The update // frequency is determined by the host. } }
Java
InputCallback callback = new InputCallback() { @Override public void onInputSubmitted(@NonNull String text) { // You will receive this callback when the user presses Enter on the keyboard. } @Override public void onInputTextChanged(@NonNull String text) { // You will receive this callback as the user is typing. The update // frequency is determined by the host. } };
É necessário um InputCallback
para o InputSignInMethod
Builder
.
Kotlin
val passwordInput = InputSignInMethod.Builder(callback) .setHint("Password") .setInputType(InputSignInMethod.INPUT_TYPE_PASSWORD) ... .build()
Java
InputSignInMethod passwordInput = new InputSignInMethod.Builder(callback) .setHint("Password") .setInputType(InputSignInMethod.INPUT_TYPE_PASSWORD) ... .build();
Por fim, use o novo InputSignInMethod
para criar um SignInTemplate
.
Kotlin
SignInTemplate.Builder(passwordInput) .setTitle("Sign in with username and password") .setInstructions("Enter your password") .setHeaderAction(Action.BACK) ... .build()
Java
new SignInTemplate.Builder(passwordInput) .setTitle("Sign in with username and password") .setInstructions("Enter your password") .setHeaderAction(Action.BACK) ... .build();
Usar o AccountManager
Os apps Android Automotive OS que têm autenticação precisam usar o AccountManager pelos seguintes motivos:
- Melhor UX e gerenciamento de contas facilitado: os usuários podem gerenciar com facilidade todas as contas pelo menu de contas nas configurações do sistema, incluindo o login e o logout.
- Experiências de"convidado": como os carros são dispositivos compartilhados, os OEMs podem ativar experiências de convidado no veículo. Nesse caso, não é possível adicionar contas.
Adicionar variantes de string de texto
Tamanhos de tela de carro diferentes podem mostrar quantidades distintas de texto. Com a API Car App
nível 2 e mais recentes, é possível especificar múltiplas variantes de uma string de texto para se adequar
melhor à tela. Para ver onde as variantes de texto são aceitas, procure modelos e
componentes que usem um CarText
.
É possível adicionar variantes de string de texto a uma CarText
com o
método
CarText.Builder.addVariant()
:
Kotlin
val itemTitle = CarText.Builder("This is a very long string") .addVariant("Shorter string") ... .build()
Java
CarText itemTitle = new CarText.Builder("This is a very long string") .addVariant("Shorter string") ... .build();
Você pode usar esse CarText
, por exemplo, como o texto principal de uma
GridItem
.
Kotlin
GridItem.Builder() .addTitle(itemTitle) ... .build()
Java
new GridItem.Builder() .addTitle(itemTitle) ... build();
Adicione as strings em ordem, da mais para a menos preferida. Por exemplo, da mais longa para a mais curta. O host escolhe a string de comprimento adequado, dependendo da quantidade de espaço disponível na tela do carro.
Adicionar CarIcons inline para linhas
Você pode adicionar ícones inline com texto para melhorar o apelo visual do seu app usando
CarIconSpan
.
Consulte a documentação de
CarIconSpan.create
para mais informações sobre como criar esses períodos. Consulte
Estilo de texto
espetacular com períodos para ter uma visão geral de como o estilo de texto com períodos funciona.
Kotlin
val rating = SpannableString("Rating: 4.5 stars") rating.setSpan( CarIconSpan.create( // Create a CarIcon with an image of four and a half stars CarIcon.Builder(...).build(), // Align the CarIcon to the baseline of the text CarIconSpan.ALIGN_BASELINE ), // The start index of the span (index of the character '4') 8, // The end index of the span (index of the last 's' in "stars") 16, Spanned.SPAN_INCLUSIVE_INCLUSIVE ) val row = Row.Builder() ... .addText(rating) .build()
Java
SpannableString rating = new SpannableString("Rating: 4.5 stars"); rating.setSpan( CarIconSpan.create( // Create a CarIcon with an image of four and a half stars new CarIcon.Builder(...).build(), // Align the CarIcon to the baseline of the text CarIconSpan.ALIGN_BASELINE ), // The start index of the span (index of the character '4') 8, // The end index of the span (index of the last 's' in "stars") 16, Spanned.SPAN_INCLUSIVE_INCLUSIVE ); Row row = new Row.Builder() ... .addText(rating) .build();
APIs de hardware para carros
No nível 3 da API Car App e mais recentes, a biblioteca Car App tem APIs que podem ser usadas para acessar propriedades e sensores do veículo.
Requisitos
Para usar as APIs com o Android Auto, comece adicionando uma dependência em
androidx.car.app:app-projected
ao arquivo build.gradle
do módulo do
Android Auto. Para o Android Automotive OS, adicione uma dependência em
androidx.car.app:app-automotive
ao arquivo build.gradle
do módulo do Android
Automotive OS.
Além disso, no arquivo AndroidManifest.xml
, é necessário
declarar as permissões relevantes necessárias para
solicitar os dados do carro que você quer usar. Essas permissões também precisam ser
concedidas a você pelo usuário. Você pode usar o
mesmo código no Android Auto e no Android Automotive OS, em vez
de criar fluxos que dependem da plataforma. No entanto, as permissões necessárias
são diferentes.
Informações do carro
Nesta tabela, descrevemos as propriedades exibidas pelas APIs
CarInfo
e as
permissões necessárias para solicitar o uso delas:
Métodos | Propriedades | Permissões do Android Auto | Permissões do Android Automotive OS |
---|---|---|---|
fetchModel |
Marca, modelo, ano | android.car.permission.CAR_INFO |
|
fetchEnergyProfile |
Tipos de conector de VE, tipos de combustível | com.google.android.gms.permission.CAR_FUEL |
android.car.permission.CAR_INFO |
addTollListener
removeTollListener |
Estado do cartão de pedágio, tipo de cartão de pedágio | ||
addEnergyLevelListener
removeEnergyLevelListener |
Nível de bateria, nível de combustível, nível de combustível baixo, autonomia restante | com.google.android.gms.permission.CAR_FUEL |
android.car.permission.CAR_ENERGY ,android.car.permission.CAR_ENERGY_PORTS ,android.car.permission.READ_CAR_DISPLAY_UNITS |
addSpeedListener
removeSpeedListener |
Velocidade bruta, velocidade de exibição (mostrada na tela do cluster do carro) | com.google.android.gms.permission.CAR_SPEED |
android.car.permission.CAR_SPEED ,android.car.permission.READ_CAR_DISPLAY_UNITS |
addMileageListener
removeMileageListener |
Distância do odômetro | com.google.android.gms.permission.CAR_MILEAGE |
Esses dados não estão disponíveis no Android Automotive OS para apps instalados pela Play Store. |
Por exemplo, para receber o intervalo restante, instancie um
objeto CarInfo
, crie e registre um OnCarDataAvailableListener
:
Kotlin
val carInfo = carContext.getCarService(CarHardwareManager::class.java).carInfo val listener = OnCarDataAvailableListener<EnergyLevel> { data -> if (data.rangeRemainingMeters.status == CarValue.STATUS_SUCCESS) { val rangeRemaining = data.rangeRemainingMeters.value } else { // Handle error } } carInfo.addEnergyLevelListener(carContext.mainExecutor, listener) … // Unregister the listener when you no longer need updates carInfo.removeEnergyLevelListener(listener)
Java
CarInfo carInfo = getCarContext().getCarService(CarHardwareManager.class).getCarInfo(); OnCarDataAvailableListener<EnergyLevel> listener = (data) -> { if(data.getRangeRemainingMeters().getStatus() == CarValue.STATUS_SUCCESS) { float rangeRemaining = data.getRangeRemainingMeters().getValue(); } else { // Handle error } }; carInfo.addEnergyLevelListener(getCarContext().getMainExecutor(), listener); … // Unregister the listener when you no longer need updates carInfo.removeEnergyLevelListener(listener);
Não suponha que os dados do carro estejam sempre disponíveis.
Se você receber um erro, verifique o status do valor solicitado para entender melhor por que os dados solicitados não puderam ser recuperados. Consulte a
documentação de referência para
a definição completa da classe CarInfo
.
Sensores para carros
A classe CarSensors
dá acesso ao acelerômetro, ao giroscópio, à bússola e aos dados de
localização do veículo. A disponibilidade desses valores pode depender do OEM. O formato dos dados do acelerômetro, do giroscópio e da bússola é
o mesmo da
API SensorManager
. Por exemplo,
para verificar a direção do veículo:
Kotlin
val carSensors = carContext.getCarService(CarHardwareManager::class.java).carSensors val listener = OnCarDataAvailableListener<Compass> { data -> if (data.orientations.status == CarValue.STATUS_SUCCESS) { val orientation = data.orientations.value } else { // Data not available, handle error } } carSensors.addCompassListener(CarSensors.UPDATE_RATE_NORMAL, carContext.mainExecutor, listener) … // Unregister the listener when you no longer need updates carSensors.removeCompassListener(listener)
Java
CarSensors carSensors = getCarContext().getCarService(CarHardwareManager.class).getCarSensors(); OnCarDataAvailableListener<Compass> listener = (data) -> { if (data.getOrientations().getStatus() == CarValue.STATUS_SUCCESS) { List<Float> orientations = data.getOrientations().getValue(); } else { // Data not available, handle error } }; carSensors.addCompassListener(CarSensors.UPDATE_RATE_NORMAL, getCarContext().getMainExecutor(), listener); … // Unregister the listener when you no longer need updates carSensors.removeCompassListener(listener);
Para acessar os dados de local do carro, você também precisa declarar e solicitar a
permissão android.permission.ACCESS_FINE_LOCATION
.
Testes
Para simular dados do sensor durante testes no Android Auto, consulte as seções Sensores e Configuração do sensor do guia da unidade principal para computadores. Para simular dados do sensor ao testar no Android Automotive OS, consulte a seção Emular estado de hardware do guia do emulador do Android Automotive OS.
Ciclos de vida de CarAppService, Session e Screen
As classes Session
e
Screen
implementam a interface
LifecycleOwner
. À medida que
o usuário interage com o app, os callbacks de ciclo de vida dos objetos
Session
e Screen
são invocados, conforme descrito nos diagramas a seguir.
Os ciclos de vida de CarAppService e Session
Para mais detalhes, consulte a documentação do
método
Session.getLifecycle
.
O ciclo de vida de Screen
Para mais detalhes, consulte a documentação do
método Screen.getLifecycle
.
Gravar do microfone do carro
Usando o
CarAppService
e a
API CarAudioRecord
,
você pode permitir que o app acesse o microfone do carro do usuário. Os usuários precisam conceder
permissão ao app para acessar o microfone do carro. Seu app pode gravar e
processar a entrada do usuário.
Permissão para gravar
Antes de gravar qualquer áudio, é necessário declarar a permissão de gravação no
AndroidManifest.xml
e solicitar que o usuário conceda a gravação.
<manifest ...>
...
<uses-permission android:name="android.permission.RECORD_AUDIO" />
...
</manifest>
É necessário solicitar permissão para gravar no momento da execução. Consulte a seção Solicitar permissões para ver detalhes sobre como solicitar uma permissão no app para carro.
Gravar áudio
Depois que o usuário permitir a gravação, você poderá gravar o áudio e processar a gravação.
Kotlin
val carAudioRecord = CarAudioRecord.create(carContext) carAudioRecord.startRecording() val data = ByteArray(CarAudioRecord.AUDIO_CONTENT_BUFFER_SIZE) while(carAudioRecord.read(data, 0, CarAudioRecord.AUDIO_CONTENT_BUFFER_SIZE) >= 0) { // Use data array // Potentially call carAudioRecord.stopRecording() if your processing finds end of speech } carAudioRecord.stopRecording()
Java
CarAudioRecord carAudioRecord = CarAudioRecord.create(getCarContext()); carAudioRecord.startRecording(); byte[] data = new byte[CarAudioRecord.AUDIO_CONTENT_BUFFER_SIZE]; while (carAudioRecord.read(data, 0, CarAudioRecord.AUDIO_CONTENT_BUFFER_SIZE) >= 0) { // Use data array // Potentially call carAudioRecord.stopRecording() if your processing finds end of speech } carAudioRecord.stopRecording();
Seleção de áudio
Ao gravar usando o microfone do carro, primeiro consiga o foco de áudio para garantir que qualquer mídia em andamento seja interrompida. Se você perder a seleção de áudio, pare de gravar.
Confira um exemplo de como conseguir a seleção de áudio:
Kotlin
val carAudioRecord = CarAudioRecord.create(carContext) // Take audio focus so that user's media is not recorded val audioAttributes = AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH) // Use the most appropriate usage type for your use case .setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE) .build() val audioFocusRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE) .setAudioAttributes(audioAttributes) .setOnAudioFocusChangeListener { state: Int -> if (state == AudioManager.AUDIOFOCUS_LOSS) { // Stop recording if audio focus is lost carAudioRecord.stopRecording() } } .build() if (carContext.getSystemService(AudioManager::class.java) .requestAudioFocus(audioFocusRequest) != AudioManager.AUDIOFOCUS_REQUEST_GRANTED ) { // Don't record if the focus isn't granted return } carAudioRecord.startRecording() // Process the audio and abandon the AudioFocusRequest when done
Java
CarAudioRecord carAudioRecord = CarAudioRecord.create(getCarContext()); // Take audio focus so that user's media is not recorded AudioAttributes audioAttributes = new AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH) // Use the most appropriate usage type for your use case .setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE) .build(); AudioFocusRequest audioFocusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE) .setAudioAttributes(audioAttributes) .setOnAudioFocusChangeListener(state -> { if (state == AudioManager.AUDIOFOCUS_LOSS) { // Stop recording if audio focus is lost carAudioRecord.stopRecording(); } }) .build(); if (getCarContext().getSystemService(AudioManager.class).requestAudioFocus(audioFocusRequest) != AUDIOFOCUS_REQUEST_GRANTED) { // Don't record if the focus isn't granted return; } carAudioRecord.startRecording(); // Process the audio and abandon the AudioFocusRequest when done
Biblioteca de testes
A Biblioteca de testes
do Android for Cars fornece classes
auxiliares que podem ser usadas para validar o comportamento do app em um ambiente de teste.
Por exemplo, o
SessionController
permite simular uma conexão com o host e verificar se a
Screen
e a
Template
corretas são criadas e
retornadas.
Consulte as Amostras para conferir exemplos de uso.
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 no modelo de problema.
Antes de informar 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 Issue Tracker. Para saber mais, consulte Inscrever-se em um problema.