Usar a biblioteca Android for Cars App

A biblioteca Android for Cars App permite que você use seus apps de navegação, ponto de interesse (PDI) e Internet das Coisas (IoT) no carro. Para isso, ela fornece um conjunto de modelos 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 apresenta uma visão geral dos principais recursos e conceitos da biblioteca e oferece orientações sobre o processo de configuração de um app básico.

Antes de começar

  1. Leia as páginas Design para dirigir que abordam a biblioteca de apps para carros
  2. Revise os principais termos e conceitos na seção a seguir.
  3. Conheça a IU do sistema do Android Auto e o design do Android Automotive OS.
  4. Leia as Notas da versão.
  5. 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. Os modelos são um subconjunto dos modelos principais 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 de 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. 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 tela. 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. Uma Screen tem um ciclo de vida e fornece o mecanismo para que o app envie o modelo a ser exibido quando a tela estiver visível. As instâncias de Screen também podem ser enviadas e abertas na pilha Screen e retiradas dela, o que garante que elas sigam as restrições de fluxo de modelo.
CarAppService
CarAppService é uma classe abstrata Service que o app precisa implementar e exportar para ser descoberto e gerenciado pelo host. A CarAppService do app é responsável por validar que uma conexão de host pode ser confiável usando createHostValidator e, em seguida, fornecendo instâncias da Session para cada conexão usando onCreateSession.
Session

Session é uma classe abstrata que seu app precisa implementar e retornar usando CarAppService.onCreateSession. Ele serve como ponto de entrada para mostrar informações na tela do carro. Ele tem um ciclo de vida que informa o estado atual do app na tela do carro, por exemplo, quando ele está visível ou oculto.

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

Instalar a biblioteca de apps para carros

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

Configurar os arquivos de manifesto do app

Antes de criar o app para carro, configure os arquivos de manifesto dele da seguinte maneira.

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 e se conecte ao app.

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

O snippet de código abaixo mostra como declarar um serviço de app para carros em 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

Para declarar a categoria do app, adicione um ou mais dos seguintes valores de categoria ao filtro de intent ao declarar o CarAppService, conforme descrito na seção anterior:

  • androidx.car.app.category.NAVIGATION: um app que fornece rotas de navegação guiada. Consulte Criar apps de navegação para carros.
  • androidx.car.app.category.POI: um app que oferece uma funcionalidade relevante para encontrar pontos de interesse, como vagas de estacionamento, estações de recarga e postos de gasolina. Consulte Criar apps de ponto de interesse para carros.
  • androidx.car.app.category.IOT: um app que permite que os usuários realizem ações relevantes em dispositivos conectados de dentro do carro. Consulte Criar apps de Internet das Coisas para carros.
  • androidx.car.app.category.WEATHER: um app que permite que os usuários vejam informações meteorológicas relevantes relacionadas ao local atual ou ao longo do trajeto. Consulte Criar apps de clima para carros.

Consulte Qualidade de apps Android para carros para descrições detalhadas de cada categoria e critérios para que os apps pertençam a elas.

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 usando os atributos 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 elemento <application>.

Definir um tema personalizado

Para definir um tema personalizado para o app de carro, adicione um elemento <meta-data> no arquivo de manifesto, conforme mostrado abaixo:

<meta-data
    android:name="androidx.car.app.theme"
    android:resource="@style/MyCarAppTheme />

Em seguida, declare seu recurso de estilo para definir os seguintes atributos para o tema personalizado do app de 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 são compatíveis com o host do modelo em um veículo. Para recuperar o nível mais alto da API Car App compatível com um host, use o método getCarAppApiLevel().

Declare no arquivo AndroidManifest.xml o nível mínimo da API Car App compatível com o app:

<manifest ...>
    <application ...>
        <meta-data
            android:name="androidx.car.app.minCarApiLevel"
            android:value="1"/>
    </application>
</manifest>

Consulte a documentação da anotação RequiresCarApi para saber mais detalhes sobre como manter a compatibilidade com versões anteriores e declarar o nível mínimo da 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 de Screen a ser usada na primeira vez em que o app é 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 gerenciar situações em que o 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 onGetTemplate, que retorna a instância de Template que representa o estado da IU a ser exibida na tela do carro.

O snippet abaixo mostra como declarar um Screen que usa um modelo PaneTemplate para mostrar uma string simples "Hello world!":

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. Ele fornece acesso a serviços de carro, como o ScreenManager para gerenciar a pilha de telas, o AppManager para funcionalidades gerais relacionadas a apps, como acessar o objeto Surface para desenhar mapas, e o NavigationManager usado por apps de navegação passo a passo para comunicar metadados de navegação e outros eventos relacionados à navegação com o host.

Consulte Acessar os modelos de navegação para conferir 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 drawable usando a configuração da tela do carro, iniciar um app no carro usando intents e sinalizar se o app precisa exibir o mapa no tema escuro.

Implementar a navegação na tela

Os apps costumam apresentar uma série de telas diferentes, cada uma possivelmente usando modelos 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 a um modelo de mensagem, bem como uma ação que abre 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 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 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 contabilize o novo modelo na cota. Consulte a seção Restrições de modelo para mais detalhes.

Recomendamos que você estruture 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 onGetTemplate.

Desenhar mapas

Os apps de navegação e ponto de interesse (PDI) que usam os modelos a seguir podem desenhar mapas acessando um Surface:

Modelo Permissão do modelo Orientações sobre categorias
NavigationTemplate androidx.car.app.NAVIGATION_TEMPLATES Navegação
MapWithContentTemplate androidx.car.app.NAVIGATION_TEMPLATES OU
androidx.car.app.MAP_TEMPLATES
Navegação, POI, Clima
MapTemplate (descontinuado) androidx.car.app.NAVIGATION_TEMPLATES Navegação
PlaceListNavigationTemplate (descontinuado) androidx.car.app.NAVIGATION_TEMPLATES Navegação
RoutePreviewNavigationTemplate (descontinuado) androidx.car.app.NAVIGATION_TEMPLATES Navegação

Declarar a permissão de superfície

Além da permissão necessária para o modelo que o app está usando, ele precisa declarar a permissão androidx.car.app.ACCESS_SURFACE no arquivo AndroidManifest.xml para ter acesso à plataforma:

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

Acessar a plataforma

Para acessar o Surface fornecido pelo host, é necessário implementar um SurfaceCallback e fornecer essa implementação ao serviço de carro AppManager. O Surface atual é transmitido para o SurfaceCallback no parâmetro SurfaceContainer dos callbacks onSurfaceAvailable() e onSurfaceDestroyed().

Kotlin

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

Java

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

Entender a área visível da superfície

O host pode desenhar elementos da interface do usuário para os modelos na parte de cima do mapa. O host comunica a área da superfície que certamente não estará obstruída e que vai ficar totalmente visível para o usuário chamando o método SurfaceCallback.onVisibleAreaChanged. Além disso, para minimizar o número de mudanças, ele chama o método SurfaceCallback.onStableAreaChanged com o menor retângulo, que sempre fica visível com base no modelo atual.

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

Oferecer suporte ao tema escuro

Os apps precisam redesenhar o mapa na instância de Surface com as cores escuras adequadas quando o host determina que as condições justificam esse uso, conforme descrito em Qualidade do app Android para carros.

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

Permitir que os usuários interajam com o mapa

Ao usar os modelos a seguir, você pode adicionar suporte à interação dos usuários com os mapas que você desenha, como permitir que eles vejam diferentes partes de um mapa usando zoom e movimento.

Modelo Interatividade com suporte desde o nível da API Car App
NavigationTemplate 2
PlaceListNavigationTemplate (descontinuado) 4
RoutePreviewNavigationTemplate (descontinuado) 4
MapTemplate (descontinuado) 5 (introdução do modelo)
MapWithContentTemplate 7 (introdução do modelo)

Implementar callbacks de interatividade

A interface SurfaceCallback tem vários métodos de callback que podem ser implementados para adicionar interatividade aos mapas criados com os modelos na seção anterior:

Interação Método SurfaceCallback Com suporte desde o nível da API Car App
Tocar onClick 5
Fazer gesto de pinça para aplicar zoom onScale 2
Arrastar com um único toque onScroll 2
Deslizar rapidamente com um único toque onFling 2
Tocar duas vezes onScale (com fator de escalonamento determinado pelo host do modelo) 2
Alerta giratório no modo "Movimentar" onScroll (com fator de distância determinado pelo host do modelo) 2

Adicionar uma faixa de ação no mapa

Esses modelos podem ter uma faixa de ações no mapa para ações relacionadas a ele, como aumentar e diminuir o zoom, recentralizar, mostrar uma bússola e outras ações que você escolher mostrar. A faixa de ações no mapa pode ter até quatro botões somente de ícones que podem ser atualizados sem afetar a profundidade da tarefa. Ela é ocultada no estado inativo e reaparece no estado ativo.

Para receber callbacks de interatividade, é necessário adicionar um botão Action.PAN à faixa de ações no mapa. Quando o usuário pressiona o botão de movimentação, o host entra no modo de movimentação, conforme descrito na seção a seguir.

Se o app omitir o botão Action.PAN na faixa de ações, ele não vai receber entradas do usuário dos métodos SurfaceCallback, e o host sairá de modos de movimentação ativados anteriormente.

Em uma tela touchscreen, o botão de movimentação não é mostrado.

Entender o modo panorâmico

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

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 abaixo mostra como criar um modelo Action que define um OnClickListener que chama 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 padrão de navegaçã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 mais detalhes 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. Você pode usar o ParkedOnlyOnClickListener para implementar essas ações. Se o carro não estiver parado, o host vai mostrar uma indicação ao usuário de que a ação não é permitida nesse caso. Se o carro estiver 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:

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 ao dispositivo móvel só aparecem 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, substituindo os atributos da notificação quando forem exibidos 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.
  • Em apps de navegação, a notificação pode ser exibida no widget de coluna, conforme descrito em Notificações passo a passo.

É possível 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 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 o guia de design do Google para dirigir 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 de acesso a dados ou ações restritos (por exemplo, localização), as regras padrão de 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 você não precisa iniciar sua própria Activity para criar a caixa de diálogo de permissões. Além disso, é possível usar o mesmo código no Android Auto e no Android Automotive OS, em vez de criar fluxos dependentes da plataforma.

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 vai aparecer no smartphone. Por padrão, não haverá plano de fundo por trá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 carros.

<meta-data
    android:name="androidx.car.app.theme"
    android:resource="@style/MyCarAppTheme />

Em seguida, defina o atributo carPermissionActivityLayout para o tema do app de carro:

<resources>
  <style name="MyCarAppTheme">
    <item name="carPermissionActivityLayout">@layout/my_custom_background</item>
  </style>
</resources>

Iniciar um app para carro com uma intent

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

  • Abrir o discador para fazer uma chamada telefônica
  • Iniciar a navegação guiada 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:

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());

Seu 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 ainda não esteja na parte superior:

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 determinada tarefa até o máximo de cinco. Nesse caso, o último dos modelos precisa ser um dos tipos a seguir:

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, em seguida, abrir a tela B, ele poderá enviar mais três modelos. Como alternativa, se cada tela for 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 do mesmo tipo e com o mesmo conteúdo principal do 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á abrindo uma Screen da pilha ScreenManager e atualiza a cota restante com base no número de modelos que o app está retornando.

Por exemplo, se o app enviar dois modelos enquanto estiver na tela A, depois abrir a tela B e enviar mais dois, ele terá uma cota restante. Se o app voltar à tela A, o host vai redefinir a cota para três, porque o app voltou 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, o NavigationTemplate é uma visualização que permanece na tela e é atualizada com novas instruções passo a passo para o consumo do usuário. Quando ele chega a um desses modelos, o host redefine a cota do modelo, tratando-o 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 até mesmo quando um app já está vinculado e em primeiro plano.

Consulte a seção Mostrar notificações para mais detalhes sobre como exibir as notificações do app na tela do carro. Consulte a seção Iniciar um app para carro com uma intent para saber como iniciar o app por 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 no momento da execução.

Por exemplo, na Session do app de carro, inicialize uma CarConnection e assinale as atualizações de LiveData:

Kotlin

CarConnection(carContext).type.observe(this, ::onConnectionStateUpdated)

Java

new CarConnection(getCarContext()).getType().observe(this, this::onConnectionStateUpdated);

No observador, é possível reagir a mudanças no estado de 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 por vez. Use o ConstraintManager para verificar o limite de conteúdo no momento da execução e definir o número adequado de itens nos modelos.

Comece acessando 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 encontrar o limite de conteúdo relevante. Por exemplo, para saber 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 de login para os usuários, você pode usar modelos como o SignInTemplate e o LongMessageTemplate com a API Car App nível 2 e mais recentes para processar o login no seu app na unidade principal do carro.

Para criar um SignInTemplate, defina um SignInMethod. No momento, a biblioteca Car App é compatível com os seguintes métodos de login:

  • InputSignInMethod para login com nome de usuário/senha.
  • PinSignInMethod para login com o PIN, em que o usuário vincula a conta pelo smartphone usando um PIN exibido na unidade principal.
  • ProviderSignInMethod para login no provedor, por exemplo, Login do Google e One Tap.
  • QRCodeSignInMethod para login com código QR, em que o usuário faz a leitura de um código QR para concluir o login no smartphone. Esse recurso está disponível com a API Car de nível 4 e 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 visitante no veículo, em que 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.

Você pode adicionar variantes de string de texto a um 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 um GridItem.

Kotlin

GridItem.Builder()
    .addTitle(itemTitle)
    ...
    .build()

Java

new GridItem.Builder()
    .addTitle(itemTitle)
    ...
    build();

Adicione as strings em ordem de 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 ícones de carro inline para linhas

É possível adicionar ícones inline com texto para enriquecer o apelo visual do app usando CarIconSpan. Consulte a documentação do CarIconSpan.create para mais informações sobre a criação desses spans. Consulte Estilo de texto Spantastic com spans para ter uma visão geral de como o estilo de texto com spans 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

A partir do nível 3 da API Car App, a biblioteca Car App tem APIs que podem ser usadas para acessar sensores e propriedades do veículo.

Requisitos

Para usar as APIs com o Android Auto, comece adicionando uma dependência de androidx.car.app:app-projected ao arquivo build.gradle do módulo do Android Auto. Para o Android Automotive OS, adicione uma dependência de 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 pelo usuário. É possível usar o mesmo código no Android Auto e no Android Automotive OS, em vez de criar fluxos dependentes da plataforma. No entanto, as permissões necessárias são diferentes.

CarInfo

Esta tabela descreve as propriedades exibidas pelas APIs CarInfo e as permissões necessárias para usá-las:

Métodos Propriedades Permissões do Android Auto Permissões do Android Automotive OS Com suporte desde o nível da API Car App
fetchModel Marca, modelo, ano android.car.permission.CAR_INFO 3
fetchEnergyProfile Tipos de conector de VE, tipos de combustível com.google.android.gms.permission.CAR_FUEL android.car.permission.CAR_INFO 3
fetchExteriorDimensions

Esses dados estão disponíveis apenas em alguns veículos com o Android Automotive OS que executam a API 30 ou mais recente

Dimensões externas N/A android.car.permission.CAR_INFO 7
addTollListener
removeTollListener
Estado e tipo do cartão de pedágio 3
addEnergyLevelListener
removeEnergyLevelListener
Nível da 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
3
addSpeedListener
removeSpeedListener
Velocidade bruta, velocidade de exibição (mostrada no painel do carro) com.google.android.gms.permission.CAR_SPEED android.car.permission.CAR_SPEED,
android.car.permission.READ_CAR_DISPLAY_UNITS
3
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 na Play Store. 3

Por exemplo, para receber o intervalo restante, instancie um objeto CarInfo e 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 presuma que os dados do carro estão sempre disponíveis. Se você receber um erro, verifique o status do valor solicitado para entender melhor por que os dados não puderam ser recuperados. Consulte a documentação de referência para a definição completa da classe CarInfo.

CarSensors

A classe CarSensors dá acesso ao acelerômetro, giroscópio, bússola e 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 que você receberia 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 localização do carro, você também precisa declarar e solicitar a permissão android.permission.ACCESS_FINE_LOCATION.

Teste

Para simular dados de sensores ao testar no Android Auto, consulte as seções Sensores e Configuração do sensor do guia da unidade principal do computador. Para simular dados de sensores ao testar no Android Automotive OS, consulte a seção Emulate hardware state 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. Conforme 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

Figura 1. O ciclo de vida da Session.

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

O ciclo de vida de Screen

Figura 2. O ciclo de vida da Screen.

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

Gravar do microfone do carro

Usando o CarAppService do app e a API CarAudioRecord, é possível dar acesso ao microfone do carro do usuário. Os usuários precisam conceder permissão ao app para acessar o microfone do carro. O app pode gravar e processar a entrada do usuário.

Permissão para gravar

Antes de gravar qualquer áudio, declare a permissão para gravar no AndroidManifest.xml e solicite que o usuário a conceda.

<manifest ...>
   ...
   <uses-permission android:name="android.permission.RECORD_AUDIO" />
   ...
</manifest>

Você precisa solicitar a permissão para gravar no momento da execução. Consulte a seção Pedir permissões para saber como solicitar uma permissão no app para carros.

Gravar áudio

Depois que o usuário der permissão para gravar, 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 com o microfone do carro, primeiro adquira o foco de áudio para garantir que todas as mídias em andamento sejam interrompidas. Se você perder o foco de áudio, interrompa a gravação.

Confira um exemplo de como adquirir o foco 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 para carros oferece classes auxiliares que podem ser usadas para validar o comportamento do app 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 de uso 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 solicitadas no modelo de problema.

Criar novo problema (link em inglês)

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 Issue Tracker. Para saber mais, consulte Inscrever-se em um problema.