1. Avant de commencer
Dans cet atelier de programmation, vous allez apprendre à créer des applications optimisées contre la distraction pour Android Auto et Android Automotive OS à l'aide de la bibliothèque d'applications Android for Cars. Vous commencerez par ajouter la compatibilité avec Android Auto puis créerez facilement une variante de l'application pouvant s'exécuter sur Android Automotive OS. Après avoir configuré l'application pour qu'elle fonctionne sur les deux plates-formes, vous ajouterez un écran supplémentaire et quelques interactivités de base !
Prérequis
- La dernière version d'Android Studio
- Expérience avec le langage Kotlin de base.
- Connaissances de base sur les Services Android.
- Expérience avec la création d'appareils virtuels Android et leur exécution dans Android Emulator.
- Connaissances de base en modularisation des applications Android.
- Connaissances de base sur le schéma de conception Builder.
Objectifs de l'atelier
Android Auto | Android Automotive OS |
|
|
Points abordés
- Fonctionnement de l'architecture client-hôte de la bibliothèque Car App.
- Création de vos propres classes
CarAppService,SessionetScreen. - Partage de votre implémentation sur Android Auto et Android Automotive OS.
- Exécution d'Android Auto sur votre ordinateur de développement à l'aide de l'unité principale pour ordinateur.
- Utilisation de l'émulateur Android Automotive OS.
2. Configuration
Obtenir le code
- Le code pour cet atelier de programmation se trouve dans le répertoire
car-app-library-fundamentalsau sein du dépôt GitHubcar-codelabs. Pour le cloner, exécutez la commande suivante :
git clone https://github.com/android/car-codelabs.git
- Vous pouvez aussi télécharger le dépôt sous la forme d'un fichier ZIP :
Ouvrir le projet
- Après avoir démarré Android Studio, importez le projet en sélectionnant uniquement le répertoire
car-app-library-fundamentals/start. Le répertoirecar-app-library-fundamentals/endcontient le code de solution, que vous pouvez consulter à tout moment en cas de difficulté ou pour avoir un aperçu du projet dans son ensemble.
Vous familiariser avec le code
- Après avoir ouvert le projet dans Android Studio, prenez le temps d'examiner le code de démarrage.
Notez que le code de démarrage pour l'application est divisé en deux modules, :app et :common:data.

Le module :app contient l'interface utilisateur et la logique de l'application mobile, tandis que le module :common:data contient la classe de données et le dépôt du modèle Place, utilisé pour lire les modèles Place. En résumé, le dépôt lit une liste codée en dur, mais il pourrait facilement lire une base de données ou un serveur backend dans une application réelle.
Le module :app comprend une dépendance au module :common:data lui permettant de lire et de présenter la liste des modèles Place.
3. En savoir plus sur la bibliothèque d'applications Android for Cars
La bibliothèque d'applications Android for Cars est un ensemble de bibliothèques Jetpack qui permettent aux développeurs de créer des applications pour les voitures. Elle offre un modèle de framework qui fournit des interfaces utilisateur optimisées pour la conduite tout en s'adaptant aux différentes configurations matérielles présentes dans les voitures (modes de saisie, tailles d'écran, formats, etc.). Cela vous permet, en tant que développeur, de créer facilement une application et d'avoir la certitude qu'elle fonctionnera correctement sur plusieurs véhicules équipés d'Android Auto ou d'Android Automotive OS.
En savoir plus sur le fonctionnement
Les applications créées à l'aide de la bibliothèque Car App ne s'exécutent pas directement sur Android Auto ou Android Automotive OS. Elles s'appuient plutôt sur une application hôte qui communique avec les applications clientes (votre application) et affiche les interfaces utilisateur de ces dernières en leur nom. Android Auto est, elle-même, une hôte et Google Automotive App Host est l'hôte utilisé sur les véhicules équipés d'Android Automotive OS avec Google intégré.
Voici les principales classes de la bibliothèque Car App que vous devez étendre lors de la création de votre application :
CarAppService
CarAppService est une sous-classe de la classe Service d'Android et sert de point d'entrée pour la communication entre les applications hôtes et les applications clientes (comme celle que vous allez créer dans cet atelier). Son rôle principal est de créer des instances Session avec lesquelles l'application hôte interagit.
Session
Une instance Session est une instance d'une application cliente s'exécutant sur l'écran d'un véhicule. Comme d'autres composants Android, elle a son propre cycle de vie qui peut être utilisé pour initialiser et supprimer des ressources utilisées par l'instance Session. Il existe une relation d'un à plusieurs entre CarAppService et Session. Par exemple, un CarAppService peut avoir deux instances Session, une pour un premier écran et l'autre pour un écran de cluster pour les applications de navigation compatibles avec les écrans de cluster.
Écran
Les instances Screen sont responsables de la génération d'interfaces utilisateur affichées par les applications hôtes. Ces interfaces utilisateur sont représentées par des classes Template et chacune de ces classes modélise une certaine mise en page, comme une grille ou une liste. Chaque Session gère une pile d'instances Screen qui gèrent les parcours utilisateurs à travers les différentes parties de votre application. Comme pour une instance Session, Screen a son propre cycle de vie auquel vous pouvez vous connecter.

Vous découvrirez tout ce que vous avez besoin de savoir sur ces classes lorsque vous écrirez un CarAppService, un Session et un Screen dans la section Write your CarAppService (Créer votre CarAppService) de cet atelier de programmation.
4. Configuration initiale
Pour commencer, configurez le module contenant le CarAppService et déclarez ses dépendances.
Créer le module car-app-service
- Sélectionnez le module
:commondans la fenêtre Project (Projet), faites un clic droit et sélectionnez l'option New > Module (Nouveau > Module). - L'assistant du module s'ouvre. Dans la liste se trouvant dans le volet de gauche, sélectionnez le modèle Android Library (bibliothèque Android), de façon à ce que ce module puisse être utilisé comme une dépendance par d'autres modules. Définissez ensuite les valeurs suivantes :
- Module name (nom du module) :
:common:car-app-service - Package name (nom du package) :
com.example.places.carappservice - Minimum SDK (SDK minimum) :
API 23: Android 6.0 (Marshmallow)

Configurer des dépendances
- Dans le fichier
libs.version.toml, ajoutez des entrées pour l'artefactandroidx.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" }
- Ensuite, ajoutez deux dépendances au fichier
build.gradle.ktsdu module:common:car-app-service.
androidx.car.app:app. Il s'agit de l'artefact principal de la bibliothèque Car App, qui comprend toutes les classes de base utilisées lors de la création d'applications. La bibliothèque se compose de trois autres artefacts :androidx.car.app:app-projectedpour les fonctionnalités propres à Android Auto,androidx.car.app:app-automotivepour le code des fonctionnalités Android Automotive OS, etandroidx.car.app:app-testingpour certains assistants utiles lors des tests unitaires. Vous utiliserezapp-projectedetapp-automotiveplus tard dans cet atelier.:common:data. Il s'agit du même module de données que celui utilisé par l'application mobile existante et il permet d'utiliser les mêmes sources de données pour chaque version de l'expérience dans l'application.
build.gradle.kts (Module :common:car-app-service)
dependencies {
...
implementation(libs.androidx.car.app)
implementation(project(":common:data"))
...
}
Avec ce changement, le graphique de dépendance pour les modules propres à l'application est le suivant :

Maintenant que vous avez configuré les dépendances, passons à l'écriture de CarAppService.
5. Écrire votre CarAppService
- Commencez par créer un fichier intitulé
PlacesCarAppService.ktdans le packagecarappserviceà l'intérieur du module:common:car-app-service. - Dans ce fichier, créez une classe intitulée
PlacesCarAppService, qui étendCarAppService.
PlacesCarAppService.kt
import androidx.car.app.CarAppService
import androidx.car.app.Session
import androidx.car.app.SessionInfo
import androidx.car.app.validation.HostValidator
...
class PlacesCarAppService : CarAppService() {
override fun createHostValidator(): HostValidator {
return HostValidator.ALLOW_ALL_HOSTS_VALIDATOR
}
override fun onCreateSession(sessionInfo: SessionInfo): Session {
// PlacesSession will be an unresolved reference until the next step
return PlacesSession()
}
}
La classe abstraite CarAppService implémente les méthodes Service telles que onBind et onUnbind pour vous et empêche les futurs forçages de ces méthodes afin d'assurer une bonne interopérabilité avec les applications hôte. Il vous suffit d'implémenter createHostValidator et onCreateSession.
Le HostValidator que vous renvoyez à partir de createHostValidator est référencé lors de la liaison de votre CarAppService afin de s'assurer que l'hôte est fiable et que la liaison échoue si l'hôte ne correspond pas aux paramètres que vous avez définis. Pour les besoins de cet atelier de programmation (et des tests en général), l'ALLOW_ALL_HOSTS_VALIDATOR permet de s'assurer facilement que votre application se connecte, mais il ne doit pas être utilisé en production. Consultez la documentation sur createHostValidator afin d'en savoir plus sur sa configuration pour une application de productivité.
Pour une application aussi simple que celle-ci, onCreateSession peut simplement renvoyer une instance d'une Session. Dans une application plus complexe, nous aurions pu initialiser les ressources à long terme telles que les métriques et les clients de journalisation qui sont utilisés pendant que votre application s'exécute sur le véhicule.
- Enfin, vous devez ajouter l'élément
<service>qui correspond auPlacesCarAppServicedans le fichierAndroidManifest.xmldu module:common:car-app-serviceafin que le système d'exploitation (et, par extension, d'autres applications telles que les applications hôtes) sache que cet élément existe.
AndroidManifest.xml (:common:car-app-service)
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!--
This AndroidManifest.xml will contain all of the elements that should be shared across the
Android Auto and Automotive OS versions of the app, such as the CarAppService <service> element
-->
<application>
<service
android:name="com.example.places.carappservice.PlacesCarAppService"
android:exported="true">
<intent-filter>
<action android:name="androidx.car.app.CarAppService" />
<category android:name="androidx.car.app.category.POI" />
</intent-filter>
</service>
</application>
</manifest>
Il y a deux choses importantes à retenir ici :
- L'élément
<action>permet aux applications hôtes (et au lanceur d'applications) de trouver l'application. - L'élément
<category>déclare la catégorie de l'application (point d'intérêt dans ce cas), qui détermine les critères de qualité auxquels l'application doit répondre (nous y reviendrons plus tard). Les autres valeurs possibles sont détaillées dans Supported app categories (Catégories d'applications compatibles).
Créer la classe PlacesSession
- Dans
PlacesCarAppService.kt, ajoutez le code suivant :
PlacesCarAppService.kt
import android.content.Intent
import androidx.car.app.Screen
...
class PlacesSession : Session() {
override fun onCreateScreen(intent: Intent): Screen {
// MainScreen will be an unresolved reference until the next step
return MainScreen(carContext)
}
}
Pour une application simple comme celle-ci, vous pouvez renvoyer l'écran principal dans onCreateScreen. Cependant, comme cette méthode prend un Intent comme paramètre, une application avec plus de fonctionnalités pourrait également le lire et insérer une pile "Retour" d'écrans ou utiliser d'autres logiques conditionnelles.
Créer la classe MainScreen
Ensuite, créez un nouveau package intitulé screen.
- Effectuez un clic droit sur le package
com.example.places.carappserviceet sélectionnez New > Package (Nouveau > Package), le nom complet du package sera alorscom.example.places.carappservice.screen. C'est à cet endroit que vous mettez toutes les sous-classesScreende l'application. - Dans le package
screen, créez un fichier intituléMainScreen.ktpour contenir la classeMainScreen, qui étendScreen. Pour le moment, un simple message Hello, world! grâce auPaneTemplate.
MainScreen.kt
import androidx.car.app.CarContext
import androidx.car.app.Screen
import androidx.car.app.model.Action
import androidx.car.app.model.Header
import androidx.car.app.model.Pane
import androidx.car.app.model.PaneTemplate
import androidx.car.app.model.Row
import androidx.car.app.model.Template
...
class MainScreen(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)
.setHeader(
Header.Builder()
.setStartHeaderAction(Action.APP_ICON)
.build()
).build()
}
}
6. Ajouter la prise en charge d'Android Auto
Bien que vous ayez maintenant implémenté toute la logique nécessaire pour que l'application soit opérationnelle, il vous reste deux éléments de configuration à mettre en place avant de pouvoir l'exécuter sur Android Auto.
Ajouter une dépendance au module car-app-service
Dans le fichier build.gradle.kts du module :app, ajoutez ceci :
build.gradle.kts (Module :app)
dependencies {
...
implementation(project(":common:car-app-service"))
...
}
Avec ce changement, le graphique de dépendance pour les modules propres à l'application est le suivant :

Cela regroupe le code que vous venez d'écrire dans le module :common:car-app-service avec d'autres composants inclus dans la bibliothèque Car App, tels que l'autorisation accordée autorisant l'activité.
Déclarer les métadonnées com.google.android.gms.car.application
- Effectuez un clic droit sur le module
:common:car-app-serviceet sélectionnez l'option New > Android Resource File (Nouveau > Fichier de ressources Android), puis sélectionnez les valeurs suivantes :
- File name (Nom du fichier) :
automotive_app_desc.xml - Resource type (Type de ressource) :
XML - Root element (Élément racine) :
automotiveApp

- Dans ce fichier, ajoutez l'élément
<uses>afin de déclarer que votre application utilise les modèles fournis par la bibliothèque Car App.
automotive_app_desc.xml
<?xml version="1.0" encoding="utf-8"?>
<automotiveApp>
<uses name="template"/>
</automotiveApp>
- Dans le fichier
AndroidManifest.xmldu module:app, ajoutez l'élément suivant<meta-data>qui référence le fichierautomotive_app_desc.xmlque vous venez de créer.
AndroidManifest.xml (:app)
<application ...>
<meta-data
android:name="com.google.android.gms.car.application"
android:resource="@xml/automotive_app_desc" />
...
</application>
Ce fichier est lu par Android Auto et lui indique les capacités prises en charge par votre application. Dans notre cas, il indique qu'elle utilise le système de modèles de la bibliothèque Car App. Cette information est ensuite utilisée pour gérer différents comportements, comme l'ajout de l'application au lanceur d'Android Auto et son ouverture à partir des notifications.
Facultatif : écouter les modifications de projection
Vous souhaitez parfois savoir si l'appareil d'un utilisateur est connecté ou non à un véhicule. Pour ce faire, utilisez l'API CarConnection, qui fournit des LiveData vous permettant d'observer l'état de connexion de l'appareil.
- Pour utiliser l'API
CarConnection, commencez par ajouter une dépendance au module:appdans l'artefactandroidx.car.app:app.
build.gradle.kts (Module :app)
dependencies {
...
implementation(libs.androidx.car.app)
...
}
- À des fins de démonstration, vous pouvez ensuite créer un composable simple, comme le suivant, qui affiche l'état de connexion actuel. Dans une application réelle, cet état peut être enregistré dans un journal, utilisé pour désactiver une fonctionnalité sur l'écran du téléphone pendant la projection, ou autre chose.
MainActivity.kt
import androidx.car.app.connection.CarConnection
...
@Composable
fun ProjectionState(carConnectionType: Int, modifier: Modifier = Modifier) {
val text = when (carConnectionType) {
CarConnection.CONNECTION_TYPE_NOT_CONNECTED -> "Not projecting"
CarConnection.CONNECTION_TYPE_NATIVE -> "Running on Android Automotive OS"
CarConnection.CONNECTION_TYPE_PROJECTION -> "Projecting"
else -> "Unknown connection type"
}
Text(
text = text,
style = MaterialTheme.typography.bodyMedium,
modifier = modifier
)
}
- Maintenant qu'il existe un moyen d'afficher les données, lisez-les et transmettez-les au composable, comme le montre l'extrait de code suivant.
MainActivity.kt
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
...
setContent {
val carConnectionType by CarConnection(this).type.observeAsState(initial = -1)
PlacesTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
Column {
Text(
text = "Places",
style = MaterialTheme.typography.displayLarge,
modifier = Modifier.padding(8.dp)
)
ProjectionState(
carConnectionType = carConnectionType,
modifier = Modifier.padding(8.dp)
)
PlaceList(places = PlacesRepository().getPlaces())
}
}
}
}
- Si vous exécutez l'application, le texte Not projecting (Aucune projection) devrait s'afficher.

7. Tester avec l'unité principale pour ordinateur (DHU)
Une fois CarAppService implémenté et Android Auto configuré, il est temps d'exécuter l'application et de voir le résultat.
- Installez l'application sur votre téléphone et suivez les instructions pour installer et exécuter la DHU.
La DHU étant opérationnelle, vous pouvez voir l'icône de l'application dans le lanceur d'applications (si tel n'est pas le cas, vérifiez que vous avez suivi toutes les étapes de la section précédente, puis quittez et relancez la DHU depuis le terminal).

Oups, ça a planté !

- Pour savoir pourquoi l'application a planté, vous pouvez consulter le Logcat dans Android Studio (vous devrez peut-être supprimer le filtre Logcat par défaut pour
package:mineet le remplacer paris:error).
Error: [type: null, cause: null, debug msg: java.lang.IllegalArgumentException: Min API level not declared in manifest (androidx.car.app.minCarApiLevel)
at androidx.car.app.AppInfo.retrieveMinCarAppApiLevel(AppInfo.java:143)
at androidx.car.app.AppInfo.create(AppInfo.java:91)
at androidx.car.app.CarAppService.getAppInfo(CarAppService.java:380)
at androidx.car.app.CarAppBinder.getAppInfo(CarAppBinder.java:255)
at androidx.car.app.ICarApp$Stub.onTransact(ICarApp.java:182)
at android.os.Binder.execTransactInternal(Binder.java:1285)
at android.os.Binder.execTransact(Binder.java:1244)
]
Le journal montre qu'il manque une déclaration dans le fichier manifeste pour le niveau minimum d'API pris en charge par l'application. Avant d'ajouter cette entrée, il est préférable de comprendre pourquoi elle est nécessaire.
Comme Android, la bibliothèque Car App possède également un concept de niveaux d'API, puisqu'il doit exister un contrat entre les applications hôtes et clientes pour qu'elles puissent communiquer. Les applications hôtes prennent en charge un niveau d'API donné et les fonctionnalités qui y sont associées (et, pour des raisons de rétrocompatibilité, celles des niveaux antérieurs également). Par exemple, le SignInTemplate peut être utilisé sur les applications hôtes exécutant un niveau d'API 2 ou supérieur. Mais si vous essayez de l'utiliser sur une application hôte qui ne prend en charge que le niveau d'API 1, cette appli ne connaîtra pas le type de modèle et ne pourra rien faire de significatif avec lui.
Au cours du processus de liaison entre les applications hôte et cliente, il doit y avoir un certain chevauchement des niveaux d'API pris en charge pour que la liaison réussisse. Par exemple, si une application hôte ne prend en charge que le niveau d'API 1, mais qu'une application cliente ne peut pas fonctionner sans les fonctionnalités du niveau d'API 2 (comme indiqué par cette déclaration du fichier manifeste), les applications ne devraient pas se connecter, car l'application cliente ne pourrait pas s'exécuter correctement sur l'application hôte. Ainsi, le niveau minimum d'API requis doit être déclaré par l'application cliente dans son fichier manifeste afin de s'assurer qu'elle sera liée uniquement à une application hôte capable de le prendre en charge.
- Pour configurer le niveau minimum d'API requis, ajoutez l'élément
<meta-data>dans le fichierAndroidManfiest.xmldu module:common:car-app-service:
AndroidManifest.xml (:common:car-app-service)
<application>
<meta-data
android:name="androidx.car.app.minCarApiLevel"
android:value="1" />
<service android:name="com.example.places.carappservice.PlacesCarAppService" ...>
...
</service>
</application>
- Réinstallez l'application et exécutez-la sur la DHU, l'écran suivant devrait s'afficher :

Par souci d'exhaustivité, vous pouvez également essayer de configurer le minCarApiLevel sur une valeur élevée (par exemple, 100) pour voir ce qui se passe lorsque vous essayez de lancer l'application si les applications hôte et cliente ne sont pas compatibles (indice : l'application plante, comme lorsqu'aucune valeur n'est renseignée).
Notez également que, tout comme Android, vous pouvez utiliser des fonctionnalités d'une API d'un niveau supérieur au niveau minimum déclaré si vous vérifiez lors de l'exécution que l'application hôte prend en charge ce niveau d'API.
Facultatif : écouter les modifications de projection
- Si vous avez ajouté l'écouteur
CarConnectionà l'étape précédente, l'état de la projection devrait s'être mis à jour sur votre téléphone lors de l'exécution de la DHU, comme indiqué ci-dessous :

8. Ajouter la prise en charge d'Android Automotive OS
La version pour Android Auto étant opérationnelle, il est maintenant temps d'ajouter la prise en charge d'Android Automotive OS.
Créer le module :automotive
- Pour créer un module contenant le code spécifique à la version Android Automotive OS de l'application, ouvrez File > New > New Module… (Fichier > Nouveau > Nouveau module…) dans Android Studio. Dans la liste des types de modèles, sélectionnez Automotive (Automobile), puis utilisez les valeurs suivantes :
- Application/Library name (nom de l'application/de la bibliothèque) :
Places(le même que celui de l'application principale, mais vous pourriez le modifier si vous le souhaitiez) - Module name (nom du module) :
automotive - Package name (nom du package) :
com.example.places.automotive - Langue :
Kotlin - SDK minimum :
API 29: Android 10.0 (Q). Tous les véhicules équipés d'Android Automotive OS prenant en charge les applications de la bibliothèque Car App exécutent un niveau d'API 29 au minimum.

- Cliquez sur Next (Suivant), puis sélectionnez No Activity (Aucune activité) sur l'écran suivant et cliquez sur Finish (Terminer).

Ajouter des dépendances
Tout comme pour Android Auto, vous devez déclarer une dépendance au module :common:car-app-service. Vous pourrez ainsi partager votre implémentation sur les deux plates-formes.
Vous devez également ajouter une dépendance à l'artefact androidx.car.app:app-automotive. Contrairement à l'artefact androidx.car.app:app-projected, facultatif pour Android Auto, cette dépendance est obligatoire pour Android Automotive OS, car elle inclut la CarAppActivity utilisées pour exécuter l'application.
- Commencez par ajouter une entrée pour l'artefact
androidx.car.app:app-automotivedanslibs.versions.toml.
libs.version.toml
[libraries]
...
androidx-car-app-automotive = { group = "androidx.car.app", name = "app-automotive", version.ref = "carApp"}
- Pour ajouter des dépendances, ouvrez le fichier
build.gradle.ktset insérez le code suivant :
build.gradle.kts (Module :automotive)
dependencies {
...
implementation(project(":common:car-app-service"))
implementation(libs.androidx.car.app.automotive)
...
}
Avec ce changement, le graphique de dépendance pour les modules propres à l'application est le suivant :

Configurer le fichier manifeste
- Commencez par déclarer deux fonctionnalités obligatoires,
android.hardware.type.automotiveetandroid.software.car.templates_host.
android.hardware.type.automotive est une fonctionnalité système qui indique que l'appareil est un véhicule (pour plus d'informations, consultez la section FEATURE_AUTOMOTIVE). Seules les applications qui marquent cette fonctionnalité comme obligatoire peuvent être soumises à un canal Automotive OS sur la Play Console (les applications soumises à d'autres canaux ne peuvent pas exiger cette fonctionnalité). android.software.car.templates_host est une fonctionnalité système présente uniquement dans les véhicules pour lesquels l'hôte du modèle est exigé pour exécuter les applications modèles. Pour cet atelier de programmation, ces modifications sont suffisantes. Lorsque vous créez votre propre application, assurez-vous qu'elle répond à toutes les exigences concernant les fonctionnalités de Google Play pour Android Automotive OS.
AndroidManifest.xml (:automotive)
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-feature
android:name="android.hardware.type.automotive"
android:required="true" />
<uses-feature
android:name="android.software.car.templates_host"
android:required="true" />
...
</manifest>
- Ajoutez ensuite la référence au fichier
automotive_app_desc.xml, comme vous l'aviez fait pour Android Auto.
Notez que cette fois l'attribut android:name est différent, il ne s'agit plus de com.google.android.gms.car.application, mais de com.android.automotive. Comme précédemment, cela permet de référencer le fichier automotive_app_desc.xml dans le module :common:car-app-service, ce qui signifie que la même ressource est utilisée pour Android Auto et Android Automotive OS. De plus, l'élément <meta-data> se trouve dans l'élément <application> (vous devez donc modifier la balise application pour qu'elle ne soit plus autofermante).
AndroidManifest.xml (:automotive)
<application>
...
<meta-data android:name="com.android.automotive"
android:resource="@xml/automotive_app_desc"/>
...
</application>
- Enfin, vous devez ajouter un élément
<activity>pour laCarAppActivityde la bibliothèque.
AndroidManifest.xml (:automotive)
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
...
<application ...>
...
<activity
android:name="androidx.car.app.activity.CarAppActivity"
android:exported="true"
android:launchMode="singleTask"
android:theme="@android:style/Theme.DeviceDefault.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android:name="distractionOptimized"
android:value="true" />
</activity>
</application>
</manifest>
Et voici à quoi servent toutes ces configurations :
android:nameliste le nom de classe complet de la classeCarAppActivitydepuis le packageapp-automotive.android:exportedest défini surtruepuisque cetteActivitydoit être exécutable par une autre application (le lanceur d'applications).android:launchModeest défini sursingleTaskafin qu'il ne puisse y avoir qu'une instance deCarAppActivityà la fois.android:themeest défini sur@android:style/Theme.DeviceDefault.NoActionBarafin que l'application occupe tout l'espace sur l'écran.- Le fichier d'intent indique qu'il s'agit du lanceur d'applications
Activitypour l'application. - Un élément
<meta-data>indique au système que l'application peut être utilisée lorsque les restrictions de l'expérience utilisateur sont en place, lorsque le véhicule roule par exemple.
Facultatif : copier les icônes de lanceur depuis le module :app
Puisque vous venez de créer le module :automotive, il affiche les icônes vertes par défaut du logo Android.
- Si vous le souhaitez, vous pouvez copier et coller le répertoire de ressources
mipmapdepuis le module:appdans le module:automotivepour utiliser les mêmes icônes de lanceur que l'application mobile.
9. Effectuer des tests avec l'émulateur d'Android Automotive OS
Créer un appareil virtuel Android pour Android Automotive OS
- Après avoir ouvert le gestionnaire d'appareils, sélectionnezAutomotive (Automobile) dans la colonne Category (Catégorie) à gauche de la fenêtre. Sélectionnez ensuite la définition de l'appareil Automobile (1408p landscape) with Google Play (Automobile [paysage 1408 p] avec Google Play) dans la liste, puis cliquez sur Next (Suivant).
- Sur la page suivante, sélectionnez l'image système de l'API 34 (elle sera téléchargée si elle ne l'est pas déjà), puis créez l'AVD en cliquant sur Finish (Terminer).

Exécuter l'application
Exécutez l'application sur l'émulateur que vous venez de créer en utilisant la configuration d'exécution automotive.


Dans la prochaine étape, vous apporterez des changements au module :common:car-app-service afin d'afficher une liste de lieux et de permettre à l'utilisateur de lancer la navigation vers une destination donnée depuis une autre application.
10. Ajouter une carte et un écran d'informations
Ajouter une carte à l'écran principal
- Pour commencer, remplacez le code dans la méthode
onGetTemplatede la classeMainScreenpar ce qui suit :
MainScreen.kt
import androidx.car.app.model.CarLocation
import androidx.car.app.model.Distance
import androidx.car.app.model.DistanceSpan
import androidx.car.app.model.ItemList
import androidx.car.app.model.Metadata
import androidx.car.app.model.Place
import androidx.car.app.model.PlaceListMapTemplate
import androidx.car.app.model.PlaceMarker
import com.example.places.data.PlacesRepository
...
override fun onGetTemplate(): Template {
val placesRepository = PlacesRepository()
val itemListBuilder = ItemList.Builder()
.setNoItemsMessage("No places to show")
placesRepository.getPlaces()
.forEach {
itemListBuilder.addItem(
Row.Builder()
.setTitle(it.name)
// Each item in the list *must* have a DistanceSpan applied to either the title
// or one of the its lines of text (to help drivers make decisions)
.addText(SpannableString(" ").apply {
setSpan(
DistanceSpan.create(
Distance.create(Math.random() * 100, Distance.UNIT_KILOMETERS)
), 0, 1, Spannable.SPAN_INCLUSIVE_INCLUSIVE
)
})
.setOnClickListener { TODO() }
// Setting Metadata is optional, but is required to automatically show the
// item's location on the provided map
.setMetadata(
Metadata.Builder()
.setPlace(Place.Builder(CarLocation.create(it.latitude, it.longitude))
// Using the default PlaceMarker indicates that the host should
// decide how to style the pins it shows on the map/in the list
.setMarker(PlaceMarker.Builder().build())
.build())
.build()
).build()
)
}
return PlaceListMapTemplate.Builder()
.setTitle("Places")
.setItemList(itemListBuilder.build())
.build()
}
Ce code lit la liste des instances Place depuis le PlacesRepository et les convertit en une Row à ajouter à la ItemList affichée dans le PlaceListMapTemplate.
- Exécutez à nouveau l'application (sur l'une des plates-formes ou les deux) pour voir le résultat :
Android Auto | Android Automotive OS |
|
|
Oups, une nouvelle erreur. Une autorisation semble manquante.
java.lang.SecurityException: The car app does not have a required permission: androidx.car.app.MAP_TEMPLATES
at android.os.Parcel.createExceptionOrNull(Parcel.java:2373)
at android.os.Parcel.createException(Parcel.java:2357)
at android.os.Parcel.readException(Parcel.java:2340)
at android.os.Parcel.readException(Parcel.java:2282)
...
- Pour corriger l'erreur, ajoutez l'élément
<uses-permission>suivant dans le fichier manifeste du module:common:car-app-service.
L'autorisation doit être déclarée par n'importe quelle application qui utilise le PlaceListMapTemplate. Autrement l'application plante, comme nous venons de le voir. Notez que seules les applications qui déclarent leur catégorie comme androidx.car.app.category.POI peuvent utiliser ce modèle et, par extension, cette autorisation.
AndroidManifest.xml (:common:car-app-service)
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="androidx.car.app.MAP_TEMPLATES" />
...
</manifest>
Si vous exécutez l'application après avoir ajouté l'autorisation, vous devriez obtenir le résultat suivant sur chaque plate-forme :
Android Auto | Android Automotive OS |
|
|
L'affichage de la carte est pris en charge par l'hôte de l'application lorsque vous fournissez les Metadata nécessaires.
Ajouter un écran d'informations
Maintenant, il est temps d'ajouter un écran d'informations pour permettre aux utilisateurs d'en savoir plus sur un lieu spécifique et d'avoir la possibilité de s'y rendre en utilisant leur application de navigation préférée ou de retourner à la liste des lieux. Pour ce faire, vous pouvez utiliser PaneTemplate, qui vous permet d'afficher jusqu'à quatre lignes d'informations à côté des boutons d'action facultatifs.
- Tout d'abord, faites un clic droit sur le répertoire
resdans le module:common:car-app-service, puis sur New > Vector Asset (Nouveau > Élément vectoriel), et utilisez la configuration suivante pour créer une icône de navigation :
- Asset type (type d'élément) :
Clip art - Clip art (Image clipart) :
navigation - Nom :
baseline_navigation_24 - Size (Taille) :
24dp by24dp (24dp par24dp) - Couleur :
#000000 - Opacity (Opacité) :
100%

- Ensuite, créez un fichier intitulé
DetailScreen.ktdans le packagescreen(à côté du fichierMainScreen.ktexistant) et ajoutez le code suivant :
DetailScreen.kt
import android.graphics.Color
import androidx.car.app.CarContext
import androidx.car.app.Screen
import androidx.car.app.model.Action
import androidx.car.app.model.CarColor
import androidx.car.app.model.CarIcon
import androidx.car.app.model.Header
import androidx.car.app.model.MessageTemplate
import androidx.car.app.model.Pane
import androidx.car.app.model.PaneTemplate
import androidx.car.app.model.Row
import androidx.car.app.model.Template
import androidx.core.graphics.drawable.IconCompat
import com.example.android.cars.carappservice.R
import com.example.places.data.PlacesRepository
import com.example.places.data.model.toIntent
class DetailScreen(carContext: CarContext, private val placeId: Int) : Screen(carContext) {
private var isBookmarked = false
override fun onGetTemplate(): Template {
val place = PlacesRepository().getPlace(placeId)
?: return MessageTemplate.Builder("Place not found")
.setHeader(
Header.Builder()
.setStartHeaderAction(Action.BACK)
.build()
)
.build()
val navigateAction = Action.Builder()
.setTitle("Navigate")
.setIcon(
CarIcon.Builder(
IconCompat.createWithResource(
carContext,
R.drawable.baseline_navigation_24
)
).build()
)
// Only certain Intent actions are supported by `startCarApp`. Check its documentation
// for all of the details. To open another app that can handle navigating to a location
// you must use the CarContext.ACTION_NAVIGATE action and not Intent.ACTION_VIEW like
// you might on a phone.
.setOnClickListener { carContext.startCarApp(place.toIntent(CarContext.ACTION_NAVIGATE)) }
.build()
return PaneTemplate.Builder(
Pane.Builder()
.addAction(navigateAction)
.addRow(
Row.Builder()
.setTitle("Coordinates")
.addText("${place.latitude}, ${place.longitude}")
.build()
).addRow(
Row.Builder()
.setTitle("Description")
.addText(place.description)
.build()
).build()
).setHeader(
Header.Builder()
.setStartHeaderAction(Action.BACK)
.setTitle(place.name)
.build()
).build()
}
}
Faites particulièrement attention à la construction de navigateAction. L'appel à startCarApp dans son OnClickListener est essentiel pour interagir avec d'autres applications sur Android Auto et Android Automotive OS.
Naviguer entre les écrans
Maintenant qu'il y a deux types d'écrans, il est temps d'ajouter la navigation entre les deux. La navigation dans la bibliothèque Car App Library utilise un modèle de pile avec empilement et dépilement, idéal pour les flux de tâches simples pendant la conduite.

- Pour que l'un des éléments de la liste passe vers
MainScreenouDetailScreen, ajoutez le code suivant :
MainScreen.kt
Row.Builder()
...
.setOnClickListener { screenManager.push(DetailScreen(carContext, it.id)) }
...
Le retour depuis un DetailScreen vers le MainScreen est déjà configuré puisque setHeaderAction(Action.BACK) est appelé lors de la création du PaneTemplate affiché sur le DetailScreen. Lorsque l'utilisateur clique sur l'action d'en-tête, l'hôte se charge de dépiler l'écran actuel de la pile, mais ce comportement peut être ignoré par votre application, si vous le souhaitez.
- Exécutez votre application pour voir le fonctionnement du
DetailScreenet de la navigation dans votre application.
11. Mettre à jour le contenu sur un écran
Vous souhaitez sans doute permettre à un utilisateur d'interagir avec un écran et de modifier l'état des éléments qui s'y trouvent. Pour ce faire, créez une fonctionnalité permettant aux utilisateurs d'ajouter un lieu à leurs favoris, ou de l'en supprimer, à partir du DetailScreen.
- Dans un premier temps, ajoutez une variable locale,
isBookmarkedqui retient l'état. Dans une application réelle, cette variable devrait être stockée dans la couche de données, mais une variable locale suffit à des fins de démonstration.
DetailScreen.kt
class DetailScreen(carContext: CarContext, private val placeId: Int) : Screen(carContext) {
private var isBookmarked = false
...
}
- Ensuite, effectuez un clic droit sur le répertoire
resdans le module:common:car-app-service, puis sur New > Vector Asset (Nouveau > Élément vectoriel), et utilisez la configuration suivante pour créer deux icônes de favoris :
- Asset type (type d'élément) :
Clip art - Nom :
outline_bookmark_add_24,outline_bookmark_added_24 - Images clipart :
bookmark,bookmark_added(de l'ensemble de sources "Material Symbols Outlined" [Symboles Material Design en traits pleins]) - Size (Taille) :
24dp by24dp (24dp par24dp) - Couleur :
#000000 - Opacity (Opacité) :
100%
- Ensuite, dans
DetailsScreen.kt, créez unActionpour la fonctionnalité d'ajout aux favoris.
DetailScreen.kt
val navigateAction = ...
val bookmarkAction = Action.Builder()
.setIcon(
CarIcon.Builder(
IconCompat.createWithResource(
carContext,
if (isBookmarked) R.drawable.outline_bookmark_added_24 else R.drawable.outline_bookmark_add_24
)
).build()
)
.setOnClickListener {
isBookmarked = !isBookmarked
}.build()
...
Deux éléments sont intéressants ici :
- La couleur de
CarIconchange en fonction de l'état de l'élément. setOnClickListenerest utilisé pour réagir aux entrées de l'utilisateur et modifier l'état de favori.
- N'oubliez pas d'appeler l'
addEndHeaderActionsur lePaneTemplate.Builderpour utiliser réellement lebookmarkAction.
DetailScreen.kt
Header.Builder()
...
.addEndHeaderAction(bookmarkAction)
.build()
- Exécutez maintenant l'application et observez :

Les clics ont bien été reçus, mais l'icône ne change pas.
Ceci est dû au fait que la bibliothèque Car App Library possède un concept d'actualisation. Pour limiter la distraction du conducteur, l'actualisation du contenu sur l'écran est limitée (cette limitation varie en fonction du modèle affiché) et chaque actualisation doit être explicitement demandée par votre propre code en appelant la méthode invalidate de la classe Screen. La simple mise à jour de certains états référencés dans onGetTemplate ne suffit pas pour mettre à jour l'interface utilisateur.
- Pour corriger ce problème, mettez à jour le
OnClickListenerpour lebookmarkActioncomme suit :
DetailScreen.kt
.setOnClickListener {
isBookmarked = !isBookmarked
// Request that `onGetTemplate` be called again so that updates to the
// screen's state can be picked up
invalidate()
}
- Exécutez à nouveau l'application pour voir que l'icône se met à jour lorsque vous cliquez dessus.

Vous disposez maintenant d'une application de base, bien intégrée à Android Auto et à Android Automotive OS !
12. Félicitations
Vous avez réussi à créer votre première application avec la bibliothèque Car App. Il est maintenant temps d'appliquer ce que vous avez appris à votre propre application !
Comme indiqué précédemment, seules certaines catégories créées à l'aide de la bibliothèque Car App peuvent être proposées au Play Store à l'heure actuelle. Si votre application appartient à l'une des catégories compatibles, vous pouvez commencer à la créer dès aujourd'hui !
De nouvelles catégories d'applications sont ajoutées chaque année, alors même si vous ne pouvez pas appliquer immédiatement ce que vous avez appris, revenez plus tard et vous pourrez peut-être étendre votre application aux véhicules !
À essayer
- Installez un émulateur d'OEM et observez comment la personnalisation de l'OEM peut modifier l'apparence des applications de la bibliothèque Car App sur Android Automotive OS. Notez que tous les émulateurs OEM ne sont pas compatibles avec les applications de la bibliothèque Car App.
- Utilisez différentes configurations DHU et différents profils matériels de l'émulateur Android Automotive OS pour voir comment les applications hôtes gèrent l'adaptation de l'application à différentes tailles d'affichage.
- Consultez l'exemple d'application Showcase qui présente toutes les fonctionnalités de la bibliothèque Car App.
Complément d'informations
- L'atelier de programmation Utiliser la bibliothèque d'applications Android for Cars couvre le contenu de cet atelier de programmation et bien plus encore.
- Les Consignes de conception de la bibliothèque d'applications Android for Cars fournit une description détaillée des différents modèles et des bonnes pratiques à suivre lors de la création de votre application.
- La page Qualité des applis Android pour les voitures décrit les critères auxquels votre application doit répondre pour créer une expérience utilisateur optimale et réussir l'examen du Play Store.






