Criar e testar um app do Android Automotive OS para carros estacionados: compatibilidade com o recurso Áudio ao Dirigir

1. Antes de começar

O que este codelab não é

  • Um guia sobre como criar apps de música (áudio, por exemplo, músicas, rádios, podcasts) para o Android Auto e o Android Automotive OS. Consulte Criar apps de música para carros para saber como criar esses apps.

O que é necessário

O que você vai criar

Neste codelab, você vai aprender a adicionar compatibilidade com o recurso Áudio ao Dirigir no app Road Reels, que já existe e pode ser usado em dispositivos móveis e Android Automotive OS.

A versão inicial da reprodução pausada enquanto as restrições de experiência do usuário estão ativas.

A versão concluída do app continua a reprodução enquanto as restrições de experiência do usuário estão ativas.

O que você aprenderá

  • Como ativar a reprodução de áudio em segundo plano em um app de vídeo no Android Automotive OS

2. Começar a configuração

Acessar o código

  1. O código deste codelab pode ser encontrado no diretório build-a-parked-app do repositório car-codelabs do GitHub. Para clonar, execute o seguinte comando:
git clone https://github.com/android/car-codelabs.git
  1. Se preferir, baixe o repositório como um arquivo ZIP:

Abrir o projeto

  • Depois de iniciar o Android Studio, importe o projeto, selecionando apenas o diretório build-a-parked-app/end. O diretório build-a-parked-app/audio-while-driving contém o código da solução, que você pode consultar a qualquer momento se tiver dificuldades ou apenas quiser conferir o projeto completo.

Conhecer o código

  • Depois de abrir o projeto no Android Studio, analise o código inicial.

3. Aprender sobre apps do Android Automotive OS para carros estacionados

Os apps para carros estacionados são um subconjunto das categorias de apps compatíveis com o Android Automotive OS. No momento, eles consistem em apps de streaming de vídeo, navegadores da Web e jogos. Esses apps são ideais para uso em carros, considerando o hardware presente em veículos com o Google integrado e a crescente prevalência de veículos elétricos, em que o tempo de carregamento é uma ótima oportunidade para motoristas e passageiros interagirem com esses tipos de apps.

De muitas maneiras, os carros são semelhantes a outros dispositivos de tela grande, como tablets e dobráveis. Eles têm telas touch com tamanhos, resoluções e proporções semelhantes e que podem estar na orientação retrato ou paisagem (embora, diferentemente dos tablets, a orientação seja fixa). Também são dispositivos conectados que podem entrar e sair da conexão de rede. Com tudo isso em mente, não é de surpreender que os apps já otimizados para telas grandes geralmente exijam uma quantidade mínima de trabalho para oferecer uma ótima experiência do usuário em carros.

Assim como em telas grandes, também há níveis de qualidade de apps em carros:

  • Nível 3, pronto para carros: o app é compatível com telas grandes e pode ser usado enquanto o carro está estacionado. Embora o app não tenha recursos otimizados para carros, os usuários podem usar o app da mesma forma que faria em qualquer outro dispositivo Android de tela grande. Os apps para dispositivos móveis que satisfazem esses requisitos podem ser distribuídos para carros sem alterações pelo programa de apps para dispositivos móveis criados para carros.
  • Nível 2, otimizado para carros: o app oferece uma ótima experiência na tela central do carro. Para isso, o app precisa ter uma engenharia específica para incluir recursos que possam ser usados nos modos de direção ou estacionado, dependendo da categoria do app.
  • Nível 1, diferenciado para carros: o app é criado para funcionar com diversos tipos de hardware em carros e pode adaptar a experiência nos modos de direção e estacionado. Ele oferece a melhor experiência do usuário projetada para as diferentes telas dos carros, como o console central, o painel de instrumentos e outras telas, como telas panorâmicas encontradas em muitos carros premium.

Neste codelab, você vai implementar o Áudio ao Dirigir, que é um recurso de Nível 1, tornando o app diferenciado para carros.

4. Executar o app no emulador do Android Automotive OS

Instalar as imagens do sistema do Automotive com a Play Store

  1. Primeiro, abra o SDK Manager no Android Studio e selecione a guia SDK Platforms, se ela ainda não estiver selecionada. No canto inferior direito da janela do SDK Manager, verifique se a caixa Show package details está marcada.
  2. Instale uma das imagens do emulador de Android Automotive da API 34 listadas em Adicionar imagens genéricas do sistema. As imagens só podem ser executadas em máquinas com a mesma arquitetura (x86/ARM) que elas.

Criar um dispositivo virtual Android para o Android Automotive OS

  1. Depois de abrir o Gerenciador de dispositivos, selecione Automotive na coluna Category no lado esquerdo da janela. Em seguida, selecione o perfil de hardware do pacote Automotive (1408p landscape) na lista e clique em Next.

6a32a01404a7729f.png

  1. Na próxima página, selecione a imagem do sistema da etapa anterior. Clique em Next e selecione qualquer opção avançada que você queira antes de criar o AVD. Para isso, clique em Finish.

Executar o app

Execute o app no emulador recém-criado usando a configuração de execução do app. Navegue até a tela do player e simule que está dirigindo para testar o comportamento do app.

5. Detectar a compatibilidade com o recurso Áudio ao Dirigir

Como o Áudio ao Dirigir não é compatível com todos os veículos, você precisa detectar se o dispositivo atual permite usar o recurso e adaptar o comportamento do app de acordo. Para isso, use a classe CarFeatures da biblioteca androidx.car.app:app.

  1. Adicione uma dependência ao artefato androidx.car.app:app.

libs.version.toml

[versions]
...
carApp = "1.7.0-rc01"


[libraries]
...
androidx-car-app = { group = "androidx.car.app", name = "app", version.ref = "carApp" }

build.gradle.kts (módulo :app)

implementation(libs.androidx.car.app)
  1. No RoadReelsPlayer, você pode determinar se o recurso é compatível e atualizar a lógica usada para calcular o valor de shouldPreventPlay.

RoadReelsPlayer.kt

if (packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
    ...

    val isBackgroundAudioWhileDrivingSupported = CarFeatures.isFeatureEnabled(
        context,
        CarFeatures.FEATURE_BACKGROUND_AUDIO_WHILE_DRIVING
    )

    shouldPreventPlay = !isBackgroundAudioWhileDrivingSupported &&
            carUxRestrictionsManager.currentCarUxRestrictions.isRequiresDistractionOptimization
    invalidateState()

    carUxRestrictionsManager.registerListener { carUxRestrictions: CarUxRestrictions ->
        shouldPreventPlay = !isBackgroundAudioWhileDrivingSupported &&
            carUxRestrictions.isRequiresDistractionOptimization
        ...
    }
}

Execute o app de novo com essas mudanças e simule que está dirigindo. Observe que a reprodução de áudio agora continua quando a interface do app é ocultada pelo sistema. Mas ainda não terminamos: para atender a todos os requisitos, é necessário fazer mais algumas mudanças.

6. Oferecer compatibilidade com a reprodução em segundo plano

No momento, a reprodução de mídia do app é processada por uma atividade. Por isso, a reprodução pode continuar por um curto período depois que as restrições de experiência do usuário forem ativadas e a atividade ficar oculta. Com o tempo, o app será armazenado em cache pelo sistema, fazendo com que a reprodução seja encerrada.

Se quiser oferecer compatibilidade com a reprodução de longa duração, o app precisará ser atualizado para usar um serviço que processe a reprodução. Isso pode ser feito usando o MediaSessionService da Media3.

Criar um MediaSessionService

  1. Clique com o botão direito do mouse no pacote com.example.android.cars.roadreels na janela "Project" e selecione New > Kotlin Class/File. Insira PlaybackService como o nome do arquivo e clique no tipo Class.
  2. Adicione a seguinte implementação do PlaybackService. Consulte Reprodução em segundo plano com uma MediaSessionService para mais informações sobre MediaSessionService.

PlaybackService.kt

import androidx.media3.common.util.UnstableApi
import androidx.media3.session.MediaSession
import androidx.media3.session.MediaSessionService

@UnstableApi
class PlaybackService : MediaSessionService() {
    private var mediaSession: MediaSession? = null

    override fun onCreate() {
        super.onCreate()
        val player = RoadReelsPlayer(this)
        mediaSession = MediaSession.Builder(this, player).build()
    }

    override fun onDestroy() {
        mediaSession?.run {
            player.release()
            release()
            mediaSession = null
        }
        super.onDestroy()
    }

    override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaSession? {
        if (controllerInfo.isTrusted || controllerInfo.packageName == packageName) {
            return mediaSession
        }
        return null
    }
}
  1. Adicione os seguintes elementos <uses-permission> no arquivo de manifesto. A reprodução de mídia é processada usando um serviço em primeiro plano.

AndroidManifest.xml

<manifest ...>
    ...
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
    ...
</manifest>
  1. Declare o PlaybackService no manifesto também:

AndroidManifest.xml

<application ...>
    ...
    <service
        android:name=".PlaybackService"
        android:foregroundServiceType="mediaPlayback"
        android:exported="true">
        <intent-filter>
            <action android:name="androidx.media3.session.MediaSessionService"/>
        </intent-filter>
    </service>
</application>

Atualizar o PlayerViewModel para usar o PlaybackService

  1. Como o PlaybackService cria e expõe uma MediaSession, não é mais necessário que o PlayerViewModel crie uma. Encontre e exclua a linha a seguir e todas as referências à variável:

PlayerViewModel.kt

private var mediaSession: MediaSession? = null
  1. Em seguida, substitua a seção do bloco init que instancia um RoadReelsPlayer pelo código abaixo, que conecta o app ao PlaybackService usando um MediaController.

PlayerViewModel.kt

import android.content.ComponentName
import androidx.media3.session.MediaController
import androidx.media3.session.SessionToken
import com.example.android.cars.roadreels.PlaybackService
import com.google.common.util.concurrent.MoreExecutors

...

init {
        viewModelScope.launch {
            ...
        }
        
        val sessionToken = SessionToken(
            application,
            ComponentName(application, PlaybackService::class.java)
        )

        val mediaControllerFuture =
            MediaController.Builder(getApplication(), sessionToken).buildAsync()

        mediaControllerFuture.addListener(
            { _player.update { mediaControllerFuture.get() } },
            MoreExecutors.directExecutor()
        )
    }

Teste o app de novo. Uma interface diferente vai aparecer quando ele for bloqueado pelo sistema. Agora, o usuário pode controlar a reprodução enquanto está em movimento. Quando ele parar novamente, poderá clicar no botão de saída para voltar à experiência completa do app.

33eb8ff3d4035978.gif

Remover mudanças na reprodução do ciclo de vida

Como agora há compatibilidade com a reprodução em segundo plano, você pode remover os dois blocos LifecycleEventEffect no PlayerScreen.kt para que a reprodução continue quando o usuário sair do app, o que inclui quando os controles de mídia mostrados na tela anterior aparecem.

Pausar a reprodução ao sair da tela do player

Como o player (que agora está contido no PlaybackService) não é liberado ao sair da tela dele, é necessário adicionar uma chamada para pausar a reprodução ao sair dele para manter o comportamento anterior. Faça isso atualizando a implementação do método onCleared do PlayerViewModel:

PlayerViewModel.kt

override fun onCleared() {
    super.onCleared()
    _player.value?.apply {
        pause()
        release()
    }
}

7. Atualizar o manifesto

Por fim, para indicar que o app é compatível com o recurso Áudio ao Dirigir, adicione o seguinte elemento <uses-feature> ao manifesto do app.

AndroidManifest.xml

<application>
    <uses-feature android:name="com.android.car.background_audio_while_driving" android:required="false">
</application>

8. Parabéns

Você incluiu a compatibilidade com o Áudio ao Dirigir a um app de vídeo. Agora é hora de aplicar o que você aprendeu ao seu próprio app.

Leia mais