Usar a biblioteca Android for Cars App

A biblioteca Android for Cars App permite que você use a navegação, o ponto de interesse (PDI) e a Internet das Coisas (IoT) para o carro. Ele faz isso fornecendo um conjunto de modelos para atender à distração do motorista. e cuidar 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 vai ajudar você a configurar um app básico.

Antes de começar

  1. Revise o Design para direção páginas que cobrem a biblioteca Car App
  2. Revise os principais termos e conceitos a seguir nesta seção.
  3. Familiarize-se com o sistema Android Auto Interface e Android Automotive OS design.
  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 pode ser organizados de maneiras diferentes, conforme permitido pelo modelo ao qual pertencem Os modelos são um subconjunto dos modelos que podem atuar como raiz gráficos. Os modelos incluem as informações a serem exibidas para o usuário no texto e imagens, bem como atributos para configurar aspectos do a aparência visual dessas informações, como cores de texto ou imagens tamanhos. O organizador converte os modelos em visualizações projetadas para atender os padrões de distração do motorista e cuida de detalhes como a variedade dos fatores de 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. A responsabilidades do host variam de descobrir seu aplicativo e gerenciar o ciclo de vida até a conversão dos modelos em visualizações e a notificação do app das interações do usuário. Em dispositivos móveis, esse host é implementado pelo Android Automático. 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. Para exemplo, modelos de lista têm limites quanto ao número de itens que podem ser apresentado ao usuário. Os modelos também têm restrições estar conectado 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 pelo que os aplicativos implementam para gerenciar a interface do usuário apresentada à usuário. Um Screen tem um ciclo de vida e fornece o mecanismo para o app envie o modelo para ser exibido quando a tela estiver visível. Screen instâncias também podem ser enviadas e retirada de uma pilha Screen, que garante que eles sigam restrições de fluxo de modelo.
CarAppService
CarAppService é um classe abstrata Service que seu app deve implementar e exportar para que seja descoberto e gerenciado pelo host. O CarAppService do app é responsável por validar que uma conexão de host pode ser confiável usando createHostValidator e, em seguida, fornecendo Session instâncias 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. Ela tem um ciclo de vida que informa ao o estado atual do app na tela do carro, como quando ele está visíveis ou ocultas.

Quando uma Session é iniciada, como quando o app é iniciado pela primeira vez, o host solicita o URL Screen para exibir usando o onCreateScreen .

Instalar a biblioteca Car App

Ver a biblioteca Jetpack página de lançamento para instruções sobre como adicionar a biblioteca ao seu app.

Configurar os arquivos de manifesto do app

Antes de criar seu app para carro, configure o arquivos de manifesto da seguinte forma.

Declarar o CarAppService

O host se conecta ao app pelo seu Implementação do CarAppService. Você 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. Veja a lista de categorias de aplicativos compatíveis com os valores permitidos para esse elemento.

O snippet de código a seguir mostra como declarar um serviço de app para carros para um ponto de app 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 app adicionando uma ou mais das seguintes categorias valores no filtro de intent ao declarar o CarAppService conforme descrito na seção anterior:

  • androidx.car.app.category.NAVIGATION: um app que fornece navegação guiada rotas de navegação. Confira Criar apps de navegação para carros. para consultar outras documentações sobre a categoria.
  • androidx.car.app.category.POI: um app que oferece funcionalidades relevantes. para encontrar pontos de interesse, como vagas de estacionamento, estações de recarga e postos de gasolina. Finalizar compra Criar apps de ponto de interesse para carros para documentação adicional sobre esta categoria.
  • androidx.car.app.category.IOT: um app que permite aos usuários tomar decisões ações em dispositivos conectados de dentro do carro. Finalizar compra Criar apps de Internet das Coisas para carros para documentação adicional sobre esta categoria.
.

Consulte Qualidade do app Android para carros para descrições detalhadas de cada categoria e critérios para os aplicativos pertencerem 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 aplicativo usado para representar seu aplicativo usando os objetos label e icon da sua 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 ícone não forem declarados no Elemento <service>, o host volta para os valores especificados para <application>.

Definir um tema personalizado

Para definir um tema personalizado para o app para carros, adicione um O elemento <meta-data> na sua da seguinte forma:

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

Em seguida, declare seu recurso de estilo para defina os seguintes atributos para o tema personalizado do app para carros:

<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 são aceitos pelo 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 getCarAppApiLevel() .

Declarar 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 RequiresCarApi para mais 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 definir qual API para usar um determinado recurso da biblioteca Car App, verifique documentação de referência para CarAppApiLevels

Criar CarAppService e Session

Seu app precisa ampliar a CarAppService e implementar dela onCreateSession método, que retorna um Session instância 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 retornando 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 carros precisa ser iniciado em uma tela que não seja na tela inicial ou de destino do app, como no processamento de links diretos, é possível preparar previamente uma backstack de telas usando ScreenManager.push antes de retornar 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

Você cria as telas mostradas pelo app definindo classes que estendem a a classe Screen e implementar a onGetTemplate , que retorna o Instância de Template que representa o estado da interface a ser mostrado na tela do carro.

O snippet a seguir mostra como declarar Screen que usa um PaneTemplate modelo para exiba uma mensagem simples “Hello world!” string:

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íveis ao seu Session e Instâncias de Screen. Ela dá acesso até serviços automotivos, ScreenManager para gerenciar o pilha de tela as AppManager para informações gerais relacionadas a apps funcionalidade, como acessar o objeto Surface para desenhar mapas; e NavigationManager usada por apps de navegação guiada para comunicar a navegação metadados e outros relacionado à navegação eventos com do host.

Consulte Acessar a navegação modelos para um lista abrangente de funcionalidades da biblioteca disponíveis para apps de navegação.

O CarContext também oferece outras do aplicativo, como permitir que você carregue recursos drawable usando o arquivo de na tela do carro, iniciar um app no carro usando intents, e sinalizando se seu app deve exibir o mapa no tema escuro.

Implementar a navegação na tela

Os aplicativos geralmente apresentam várias telas diferentes, cada uma possivelmente usando os diferentes modelos pelos quais o usuário pode navegar à medida que interage 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 hardware correspondente disponível em alguns carros.

O snippet a seguir mostra como adicionar uma ação "Voltar" a um modelo de mensagem como 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. Para substituir esse comportamento, use o método OnBackPressedDispatcher disponível no CarContext.

Para garantir que o app seja seguro para usar enquanto dirige, a pilha de telas pode ter no máximo profundidade de cinco telas. Consulte as Restrições de modelo. para mais detalhes.

Atualizar o conteúdo de um modelo

Seu app pode solicitar o conteúdo de um Screen seja invalidado chamando o método Screen.invalidate. Em seguida, o anfitrião chama de volta para o Screen.onGetTemplate 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 portanto, o host não conta o novo modelo na cota de modelos. Consulte a seção Restrições de modelo para mais detalhes.

Recomendamos que você estruture suas telas para que haja uma um mapeamento entre um Screen e o tipo modelo retornado pela implementação de onGetTemplate.

Desenhar mapas

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

Modelo Permissão do modelo Orientações sobre a categoria
NavigationTemplate androidx.car.app.NAVIGATION_TEMPLATES Navegação
MapWithContentTemplate androidx.car.app.NAVIGATION_TEMPLATES OU
androidx.car.app.MAP_TEMPLATES
Navegação, PDI
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 usado pelo app, o app precisa declarar a permissão androidx.car.app.ACCESS_SURFACE na AndroidManifest.xml para ter acesso à plataforma:

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

Acessar a superfície

Para acessar o Surface fornecido pelo host, implemente uma SurfaceCallback e forneça essa implementação ao AppManager manutenção do carro. O Surface atual é transmitido à sua SurfaceCallback no parâmetro SurfaceContainer da 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 com certeza desobstruída e totalmente visível para o usuário chamando 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á um tempo para fazer 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, apenas onVisibleAreaChanged é chamado com a área maior. Se o usuário interagir com a tela, novamente apenas onVisibleAreaChanged é chamado com do primeiro retângulo.

Oferecer suporte ao tema escuro

Os apps precisam redesenhar o mapa na instância do Surface com a escuridão adequada cores quando o host determinar que as condições as justificam, como 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, é possível adicionar suporte para que os usuários interajam com os mapas que você desenha, por exemplo, deixando-os ver partes diferentes de um mapa, aplicação de zoom e movimentação.

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

Interface SurfaceCallback tem vários métodos de callback que podem ser implementados para adicionar interatividade aos mapas criados com os modelos da 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 o fator de distância determinado pelo host do modelo) 2

Adicionar uma faixa de ações no mapa

Esses modelos podem ter uma faixa de ações no mapa para ações relacionadas ao mapa, como aumentar e diminuir o zoom, recentralizar, exibir uma bússola e outras ações que você decidir exibir. A faixa de ações no mapa pode ter até quatro botões somente com ícones que podem ser atualizados sem afetar a profundidade da tarefa. Ele fica oculto durante o estado inativo e reaparece no estado ativo.

Para receber callbacks de interatividade do mapa, precisa adicionar um botão Action.PAN à faixa de ações no mapa. Quando o usuário pressionar o botão de movimentação, o host entrará no modo de movimentação, conforme descrito no nesta seção.

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 de movimentação

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 aos de 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 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 a app de navegação padrão para carro usando o 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 aplicativos, incluindo o formato da ACTION_NAVIGATE, consulte Iniciar um app para carro com uma intent nesta seção.

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 estacionado, o anfitrião exibirá uma uma indicação ao usuário de que a ação não é permitida nesse caso. Se o carro está estacionado, o código é executado normalmente. O snippet a seguir mostra como use a propriedade 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ó vão aparecer na tela do carro se eles são estendidos com um CarAppExtender Alguns atributos de notificação, como título, texto, ícone e ações do conteúdo, podem ser definidas no CarAppExtender, substituindo os atributos da notificação. quando elas aparecerem na tela do carro.

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

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 exibida no widget de coluna como descritos em Notificações de navegação guiada.
.

Você pode escolher como configurar as notificações do seu aplicativo para afetar cada um esses elementos da interface do usuário utilizando a prioridade da notificação, conforme descrito no CarAppExtender na documentação do Google Cloud.

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

Para mais informações sobre como projetar as notificações do seu app para carro, consulte a Guia do Google Design para direção sobre Notificações.

Mostrar avisos

Seu app pode exibir 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

Caso o app precise de acesso a ações ou dados restritos, por exemplo, localização: as regras padrão do Android permissões se aplicam. 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 lançar seu próprio Activity criar a caixa de diálogo de permissões. Além disso, você pode usar o mesmo código em ambos com o Android Auto e o Android Automotive OS, em vez de criar que dependem 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 será exibida no smartphone. Por padrão, não haverá nenhum plano de fundo atrás da caixa de diálogo. Para definir um segundo plano, declare um tema de app para carros na sua 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 para carros:

<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 executar uma das seguintes ações:

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. Estenda a instância de notificação com uma intent de conteúdo que contenha uma PendingIntent unindo um objeto explícito 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());

O app também precisa declarar um BroadcastReceiver que é invocada para processar a intent quando o usuário seleciona a ação no 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 Session.onNewIntent no seu app processa essa intent enviando a tela de reserva de estacionamento na pilha, se ainda não estiver no topo:

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 Exibir notificações para saber mais informações sobre como processar notificações para o app para carro.

Restrições de modelos

O host limita o número de modelos a serem exibidos para uma determinada tarefa de cinco, sendo que o último modelo precisa ser de um destes tipos:

Observe que esse limite se aplica ao número de modelos e não ao número de Screen instâncias na pilha. Para exemplo, se um app envia dois modelos na tela A e depois envia para a tela B: ele agora pode enviar mais três modelos. Como alternativa, se cada tela for estruturada para enviar um único modelo, o aplicativo pode enviar cinco instâncias de tela para o pilha ScreenManager.

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

Atualizações de modelos

Algumas atualizações de conteúdo não são contabilizadas no limite de modelos. Em geral, se um aplicativo envia um novo modelo que é do mesmo tipo e contém o mesmo conteúdo principal do modelo anterior, o novo modelo não será contados 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 aplicativo está exibindo Screen da pilha ScreenManager e atualizações a cota restante com base no número de modelos que o aplicativo para trás.

Por exemplo, se o app envia dois modelos na tela A e depois envia tela B e enviar mais dois modelos, o aplicativo terá uma cota restante. Se o aplicativo volta para a tela A, o host redefine a cota para três, porque o app voltou em dois modelos.

Ao retornar para uma tela, um aplicativo deve enviar um modelo do mesmo tipo que o último enviado por aquela tela. Enviar 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. Para exemplo, os NavigationTemplate é uma visualização que deve permanecer na tela e ser atualizada com as novas instruções passo a passo para o consumo do usuário. Quando ela chega a um desses modelos, o host redefine a cota do modelo, tratando esse modelo como se é 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 aplicativo por uma ação de notificação ou no acesso rápido, a cota também é redefinida. Esse mecanismo permite que um app inicia um novo fluxo de tarefas a partir das notificações, e se mantém verdadeiro mesmo se um aplicativo for já vinculado e em primeiro plano.

Consulte a seção Exibir notificações para mais detalhes sobre como mostrar as notificações do app na tela do carro. Consulte a Seção Iniciar um app para carro com uma intent para informações sobre como para iniciar o app a partir de uma ação de notificação.

API Connection

Você pode determinar se o app está sendo executado no Android Auto ou no Android o Automotive OS usando o API CarConnection para recuperar informações de conexão no momento da execução.

Por exemplo, no Session do app para carros, inicialize um CarConnection e inscrever-se nas 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 à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 um número diferente de Item instâncias a serem exibidas para o usuário por vez. Use o ConstraintManager para verificar o limite de conteúdo no tempo de 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, é possível consultar o objeto ConstraintManager recuperado para limite de conteúdo. Por exemplo, para descobrir o número de itens que podem ser exibidos 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

Se o app oferece uma experiência de login aos usuários, é possível usar modelos como SignInTemplate e LongMessageTemplate com a API Car App de nível 2 ou mais recente para fazer login no app na unidade principal do carro.

Para criar um SignInTemplate, defina um SignInMethod. O carro Atualmente, a biblioteca de apps oferece suporte aos seguintes métodos de login:

  • InputSignInMethod para fazer login com nome de usuário/senha.
  • PinSignInMethod para o login com PIN, em que o usuário vincula a conta pelo smartphone usando um PIN exibido na unidade principal.
  • ProviderSignInMethod para o login do provedor, como Login do Google e um toque.
  • QRCodeSignInMethod para login com QR code, em que o usuário lê um QR code para concluir o login. o celular. Esse recurso está disponível no nível 4 da API Car e em 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 da conta facilitado: os usuários podem gerenciar todas as informações com facilidade as contas deles no menu de contas das configurações do sistema, incluindo informações e logout.
  • "Convidado" experiências: como os carros são dispositivos compartilhados, os OEMs podem permitir experiências de visitante no veículo, quando 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 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();

É possível 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 strings em ordem da mais para a menos preferida, por exemplo, da mais longa para mais curta. O host escolhe a string de tamanho apropriado dependendo do sobre a quantidade de espaço disponível na tela do carro.

Adicionar CarIcons inline para linhas.

É possível adicionar ícones inline com texto para enriquecer o apelo visual do app usando CarIconSpan Consulte a documentação CarIconSpan.create para mais informações sobre como criar esses períodos. Consulte Spantastic o estilo de texto com períodos para uma visão geral de como funciona o estilo de texto com períodos.

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 do carro

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

Requisitos

Para usar as APIs com o Android Auto, adicione uma dependência ao androidx.car.app:app-projected para o arquivo build.gradle para seu Android Módulo automático. No Android Automotive OS, adicione uma dependência androidx.car.app:app-automotive para o arquivo build.gradle para seu Android Módulo do Automotive OS.

Além disso, no arquivo AndroidManifest.xml, você precisa: declarar as permissões relevantes necessárias para solicitar os dados do carro que você quer usar. Observe que essas permissões também devem ser concedida pelo usuário. Você pode usar o mesmo código no Android Auto e no Android Automotive OS, em do que a necessidade de criar fluxos dependentes da plataforma. No entanto, as permissões necessárias são diferentes.

Informações do carro

Esta tabela descreve as propriedades mostradas pelo As APIs do CarInfo e as 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 só estão disponíveis em alguns veículos com o Android Automotive OS. com a API 30 ou mais recente

Dimensões externas N/A android.car.permission.CAR_INFO 7
addTollListener
removeTollListener
Estado do cartão de pedágio, tipo de 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 (mostrado 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
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 da Play Store. 3

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

Sensores do carro

A classe CarSensors dá acesso ao acelerômetro, giroscópio, bússola e dados de local. A disponibilidade desses valores pode depender OEM O formato dos dados do acelerômetro, giroscópio e bússola é da mesma forma que receberia 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 os android.permission.ACCESS_FINE_LOCATION.

Teste

Para simular dados do sensor ao testar no Android Auto, consulte a Sensores e Sensores de configuração do Guia da unidade principal da área de trabalho. Simular dados do sensor durante testes no Android o Automotive OS, consulte a seção Emulate hardware estado da interface do Android Guia do emulador do Automotive OS.

Ciclos de vida de CarAppService, Session e Screen

Os métodos Session e Screen implementam a LifecycleOwner. Conforme o usuário interage com o app, os objetos Session e Screen ciclo de vida 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 mais detalhes, consulte a documentação Session.getLifecycle .

O ciclo de vida de Screen

Figura 2. O ciclo de vida da Screen.

Para mais detalhes, consulte a documentação método Screen.getLifecycle.

Gravar do microfone do carro

Usar o CarAppService e as API CarAudioRecord, Você pode permitir que o app acesse o microfone do carro do usuário. Os usuários precisam dar a permissão do app para acessar o microfone do carro. Seu app pode gravar e e processar a entrada do usuário dentro do seu aplicativo.

Permissão para gravar

Antes de gravar qualquer áudio, é necessário declarar a permissão de gravação no seu 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 solicitação de permissões para detalhes sobre como solicitar uma permissão no app do carro.

Gravar áudio

Depois que o usuário der permissão para 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 do microfone do carro, primeiro colete o áudio focar para garantir que qualquer mídia em andamento seja interrompida. Se você perder a seleção de áudio, pare a gravação.

Veja um exemplo de como adquirir 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

Os testes do Android para carros Library fornece recursos 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 o servidor Screen e Template são criados e retornados.

Consulte a 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 solicitadas no modelo de problema.

Criar novo problema (link em inglês)

Antes de informar um novo problema, verifique se ele está listado na versão da biblioteca ou relatados 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.