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 !
Ce que cet atelier n'est pas
- Un guide pour créer des applications multimédias (audio) pour Android Auto et Android Automotive OS. Consultez la page Créer des applications multimédias pour voitures pour découvrir comment créer de telles applis.
- Un guide pour créer des applications de chat pour Android Auto. Consultez la page Créer des applications de chat pour Android Auto pour découvrir comment créer de telles applis.
Ce dont vous avez besoin
- Version Preview d'Android Studio : les émulateurs Android Automotive OS sont uniquement disponibles avec la version Preview d'Android Studio. Si vous ne possédez pas la version Preview d'Android Studio, vous pouvez commencer l'atelier de programmation avec la version stable le temps de télécharger la version Preview.
- 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
,Session
etScreen
. - 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-fundamentals
au 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/end
contient 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 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.
Screen
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 créerez CarAppService
, Session
et Screen
dans la section Créer votre CarAppService de cet atelier. Ne vous inquiétez pas si tout ne fonctionne pas correctement pour le moment.
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
:common
dans 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
build.gradle
au niveau du projet, ajoutez une déclaration de variable pour la version de la bibliothèque Car App, comme indiqué ci-dessous. Vous pourrez ainsi facilement utiliser la même version sur chacun des modules dans l'application.
build.gradle (Project: Places)
buildscript {
ext {
// All versions can be found at https://developer.android.com/jetpack/androidx/releases/car-app
car_app_library_version = '1.3.0-rc01'
...
}
}
- Ensuite, ajoutez deux dépendances au fichier
build.gradle
du 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-projected
pour les fonctionnalités propres à Android Auto,androidx.car.app:app-automotive
pour le code des fonctionnalités Android Automotive OS, etandroidx.car.app:app-testing
pour certains assistants utiles lors des tests unitaires. Vous utiliserezapp-projected
etapp-automotive
plus 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 (Module :common:car-app-service)
dependencies {
...
implementation "androidx.car.app:app:$car_app_library_version"
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.kt
dans 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
class PlacesCarAppService : CarAppService() {
override fun createHostValidator(): HostValidator {
return HostValidator.ALLOW_ALL_HOSTS_VALIDATOR
}
override fun onCreateSession(): 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 auPlacesCarAppService
dans le fichierAndroidManifest.xml
du module:common:car-app-service
afin 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, qui détermine les critères de qualité auxquels l'application doit répondre (nous y reviendrons plus tard).androidx.car.app.category.NAVIGATION
etandroidx.car.app.category.IOT
sont d'autres valeurs possibles.
Créer la classe PlacesSession
- Créez un fichier
PlacesCarAppService.kt
et ajoutez le code suivant :
PlacesCarAppService.kt
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.carappservice
et 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-classesScreen
de l'application. - Dans le package
screen
, créez un fichier intituléMainScreen.kt
pour contenir la classeMainScreen
, qui étendScreen
. Pour le moment, un simple message Hello, world! grâce auPaneTemplate
.
MainScreen.kt
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)
.setHeaderAction(Action.APP_ICON)
.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
du module :app
, ajoutez ceci :
build.gradle (Module :app)
dependencies {
...
implementation project(path: ':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-service
et 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.xml
du module:app
, ajoutez l'élément suivant<meta-data>
qui référence le fichierautomotive_app_desc.xml
que 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:app
dans l'artefactandroidx.car.app:app
.
build.gradle (Module :app)
dependencies {
...
implementation "androidx.car.app:app:$car_app_library_version"
...
}
- À 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
@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
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 soit activer le bouton de débogage en bas à droite (visible uniquement lorsque l'application est exécutée sur la DHU), soit vérifier le Logcat dans Android Studio.
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.xml
du 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
- Language (langue) :
Kotlin
- SDK minimum :
API 29: Android 10.0 (Q)
. Comme mentionné précédemment lors de la création du module:common:car-app-service
, 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.
- Pour ajouter des dépendances, ouvrez le fichier
build.gradle
et insérez le code suivant :
build.gradle (Module :automotive)
dependencies {
...
implementation project(':common:car-app-service')
implementation "androidx.car.app:app-automotive:$car_app_library_version"
...
}
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.automotive
etandroid.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.
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>
- Vous devez ensuite déclarer certaines fonctionnalités comme n'étant pas obligatoires.
Cela permet de s'assurer que votre application est compatible avec la gamme de matériel disponible dans les voitures avec Google intégré. Par exemple, si votre application nécessite la fonctionnalité android.hardware.screen.portrait
, elle n'est pas compatible avec les véhicules équipés d'écrans en orientation paysage, l'orientation étant fixe dans la plupart des véhicules. C'est pourquoi l'attribut android:required
est défini sur false
pour ces fonctionnalités.
AndroidManifest.xml (:automotive)
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
...
<uses-feature
android:name="android.hardware.wifi"
android:required="false" />
<uses-feature
android:name="android.hardware.screen.portrait"
android:required="false" />
<uses-feature
android:name="android.hardware.screen.landscape"
android:required="false" />
<uses-feature
android:name="android.hardware.camera"
android:required="false" />
...
</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. Notez que 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 laCarAppActivity
de 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:name
liste le nom de classe complet de la classeCarAppActivity
depuis le packageapp-automotive
.android:exported
est défini surtrue
puisque cetteActivity
doit être exécutable par une autre application (le lanceur d'applications).android:launchMode
est défini sursingleTask
afin qu'il ne puisse y avoir qu'une instance deCarAppActivity
à la fois.android:theme
est défini sur@android:style/Theme.DeviceDefault.NoActionBar
afin que l'application occupe tout l'espace sur l'écran.- Le fichier d'intent indique qu'il s'agit du lanceur d'applications
Activity
pour 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
mipmap
depuis le module:app
dans le module:automotive
pour utiliser les mêmes icônes de lanceur que l'application mobile !
9. Effectuer des tests avec l'émulateur d'Android Automotive OS
Installer les images système Automotive avec Play Store
- Pour commencer, ouvrez SDK Manager dans Android Studio et sélectionnez l'onglet SDK Platforms (Plates-formes de SDK) si ce n'est pas déjà fait. En bas à droite de la fenêtre SDK Manager, assurez-vous que la case Show package details (Afficher les détails du package) est cochée.
- Installez une ou plusieurs des images d'émulateur suivantes. Les images ne s'exécutent que sur les machines disposant de la même architecture (x86/ARM).
- Android 12L > Image système Automotive avec Play Store Intel x86 Atom_64
- Android 12L > Image système Automotive avec Play Store ARM 64 v8a
- Android 11 > Image système Automotive avec Play Store Intel x86 Atom_64
- Android 10 > Automotive avec Play Store Intel x86 Atom_64
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. Puis, sélectionnez la définition de l'appareil Automotive (1024p landscape) (Automobile [paysage 1024 p]) dans la liste et cliquez sur Next (Suivant).
- Sur la page suivante, sélectionnez une image système de l'étape précédente (si vous choisissez l'image Android 11/API 30, il se peut qu'elle se trouve dans l'onglet x86 Images (Images x86) et non dans l'onglet Recommended (Recommandé) par défaut). Cliquez sur Next (Suivant), sélectionnez les options avancées de votre choix, puis créez l'AVD (appareil Android virtuel) 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
.
Lorsque vous exécutez l'application pour la première fois, un écran semblable à celui-ci peut s'afficher :
Si tel est le cas, cliquez sur le bouton Check for updates (Rechercher les mises à jour), qui vous redirige vers la page de l'application Google Automotive App Host du Play Store, puis cliquez sur le bouton Installer. Si vous n'êtes pas connecté lorsque vous cliquez sur le bouton Check for updates (Rechercher les mises à jour), vous serez redirigé vers la page de connexion. Une fois connecté, vous pouvez rouvrir l'application, cliquer sur le bouton et retourner sur le page du Play Store.
- Enfin, une fois l'hôte installé, ouvrez à nouveau l'application à partir du lanceur d'applications (l'icône de grille à neuf points dans la ligne en bas de l'écran) et vous devriez voir ceci :
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
onGetTemplate
de la classeMainScreen
par ce qui suit :
MainScreen.kt
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
res
dans 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
- Name (Nom) :
baseline_navigation_24
- Size (Taille) :
24
dp by24
dp (24
dp par24
dp) - Color (Couleur) :
#000000
- Opacity (Opacité) :
100%
- Ensuite, créez un fichier intitulé
DetailScreen.kt
dans le packagescreen
(à côté du fichierMainScreen.kt
existant) et ajoutez le code suivant :
DetailScreen.kt
class DetailScreen(carContext: CarContext, private val placeId: Int) : Screen(carContext) {
override fun onGetTemplate(): Template {
val place = PlacesRepository().getPlace(placeId)
?: return MessageTemplate.Builder("Place not found")
.setHeaderAction(Action.BACK)
.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()
)
.setTitle(place.name)
.setHeaderAction(Action.BACK)
.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
MainScreen
ouDetailScreen
, 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
DetailScreen
et 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,
isFavorite
qui 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 isFavorite = false
...
}
- Ensuite, effectuez un clic droit sur le répertoire
res
dans 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 favoris :
- Asset type (type d'élément) :
Clip art
- Name (Nom) :
baseline_favorite_24
- Clip art (Image clipart) :
favorite
- Size (Taille) :
24
dp by24
dp (24
dp par24
dp) - Color (Couleur) :
#000000
- Opacity (Opacité) :
100%
- Enfin, dans
DetailsScreen.kt
, créez uneActionStrip
pour lePaneTemplate
.
Les composants de l'interface utilisateur ActionStrip
sont placés dans la ligne d'en-tête, à l'opposé du titre, et sont idéaux pour les actions secondaires et tertiaires. Étant donné que la navigation est la principale action requise sur le DetailScreen
, placer l'Action
de sorte d'ajouter ou de supprimer des favoris dans une ActionStrip
est un excellent moyen de structurer l'écran.
DetailScreen.kt
val navigateAction = ...
val actionStrip = ActionStrip.Builder()
.addAction(
Action.Builder()
.setIcon(
CarIcon.Builder(
IconCompat.createWithResource(
carContext,
R.drawable.baseline_favorite_24
)
).setTint(
if (isFavorite) CarColor.RED else CarColor.createCustom(
Color.LTGRAY,
Color.DKGRAY
)
).build()
)
.setOnClickListener {
isFavorite = !isFavorite
}.build()
)
.build()
...
Deux éléments sont intéressants ici :
- La couleur de
CarIcon
change en fonction de l'état de l'élément. setOnClickListener
est utilisé pour réagir aux entrées de l'utilisateur et modifier l'état de favori.
- N'oubliez pas d'appeler la
setActionStrip
sur lePaneTemplate.Builder
pour l'utiliser !
DetailScreen.kt
return PaneTemplate.Builder(...)
...
.setActionStrip(actionStrip)
.build()
- Exécutez maintenant l'application et observez :
Intéressant… Il semblerait que les clics se produisent, mais que l'interface utilisateur ne se met pas à jour.
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
OnClickListener
comme suit :
DetailScreen.kt
.setOnClickListener {
isFavorite = !isFavorite
// Request that `onGetTemplate` be called again so that updates to the
// screen's state can be picked up
invalidate()
}
- Exécutez à nouveau l'application. La couleur de l'icône cœur devrait changer à chaque clic !
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 est une application de navigation, une application de point d'intérêt (POI) (comme celle sur laquelle vous avez travaillé dans cet atelier de programmation), ou une application Internet des objets (IoT), vous pouvez commencer à la créer aujourd'hui et la publier pour qu'elle soit en production sur les deux plates-formes.
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 (par exemple, l'émulateur Polestar 2) 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.
- 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.