Adaptez vos applications Android existantes de streaming vidéo, jeux et navigateurs web pour qu'elles s'exécutent en tant qu'applications stationnées sur Android Auto et Android Automotive OS. Suivez ces étapes pour respecter les directives de qualité des applications pour voitures en vue de leur distribution sur Google Play.
Prérequis
- Commencez avec une application Android adaptative existante dans l'une des catégories d'applications stationnées prises en charge : applications de streaming vidéo, jeux ou navigateurs web.
Glossaire
- Applications stationnées : Les applications stationnées s'exécutent sur Android Auto et Android Automotive OS uniquement lorsque le véhicule est à l'arrêt. Les trois catégories stationnées sont la vidéo, les jeux et les navigateurs ; les autres catégories ne peuvent pas être publiées en tant qu'applications stationnées.
- Android Auto : Android Auto est un système de projection alimenté par le téléphone. Les applications s'exécutent sur le téléphone de l'utilisateur et sont projetées sur l'écran de la voiture via une connexion filaire ou sans fil. Android Auto est une plateforme distincte d'Android Automotive OS.
- Android Automotive OS : Android Automotive OS, ou AAOS, est une variante d'Android qui s'exécute directement sur le véhicule lui-même. Android Automotive OS est une plateforme distincte d'Android Auto.
- Catégorie vidéo : Les applications vidéo diffusent des vidéos en streaming pendant que le véhicule est à l'arrêt. Généralement disponible sur Android Automotive OS ; non prise en charge sur Android Auto.
- Catégorie jeux : Les applications de jeux permettent de jouer pendant que le véhicule est à l'arrêt. Généralement disponibles sur Android Automotive OS et Android Auto.
- Catégorie navigateurs : Les navigateurs web permettent aux utilisateurs de naviguer sur le web pendant que le véhicule est à l'arrêt. Disponible en version bêta sur Android Automotive OS ; non prise en charge sur Android Auto.
- Catégorie média : Ce n'est pas une catégorie stationnée. Les applications média lisent du contenu audio (musique, radio, livres audio, à l'exclusion de la vidéo) pendant la conduite ou à l'arrêt.
- Plateforme Android for Cars : Android for Cars est un terme générique qui regroupe Android Auto et Android Automotive OS.
- Catégories généralement disponibles : Autorisées sur les pistes de test interne, test fermé, test ouvert et production de Google Play pour une plateforme prise en charge.
- Catégories bêta : Autorisées sur les pistes de test interne ou fermé ; les pistes de test ouvert et production nécessitent une acceptation dans un programme d'accès anticipé.
- Audio pendant la conduite : Une fonctionnalité bêta sur Android Automotive OS qui permet aux applications vidéo de lire de l'audio lorsque le véhicule est en mouvement.
- Directives de qualité des applications pour voitures : Lors de l'audit d'une application par rapport aux critères de niveau 3 (Prêt pour la voiture), niveau 2 (Optimisé pour la voiture) ou niveau 1 (Différencié pour la voiture), DOIT vérifier les directives de qualité des applications pour voitures.
- Mode de compatibilité Android Automotive OS : Le mode de compatibilité (
android.software.car.display_compatibility) est une fonctionnalité système sur certains véhicules fournissant une fonction de retour système, un rendu de zone sûre, une mise à l'échelle de la densité et un blocage opaque en mode conduite (déplacement des activités vers l'état Stopped). Le mode de compatibilité n'est pertinent que pour les applications qui prennent en charge Android Automotive OS ; il n'est pas pertinent pour les applications qui ne prennent en charge que Android Auto.
Exploration du code source
Recherchez dans le code source les fichiers AndroidManifest.xml (ou créez Assets/Plugins/Android/AndroidManifest.xml dans les projets Unity), res/xml/automotive_app_desc.xml, build.gradle, build.gradle.kts ou gradle/libs.versions.toml. Localisez les lecteurs vidéo existants, les classes MediaSession, les appels requestedOrientation et les gestionnaires WindowInsets. Sauf instruction contraire de l'utilisateur, demandez la permission avant d'ajouter des dépendances.
Étape 1 : Configurer le manifeste de l'application
Déclarez la catégorie de l'application, les plateformes cibles, les exigences de fonctionnalités matérielles et la configuration des activités dans AndroidManifest.xml.
1.1 : Identifier la catégorie de l'application
- Applications vidéo : Les applications vidéo DOIVENT définir
android:appCategory="video"sur<application>et NE DOIVENT PAS inclureapp/src/main/res/xml/automotive_app_desc.xmlavecuses name="video". - Jeux : Les jeux DOIVENT définir
android:appCategory="game"sur<application>. Déclarezandroid.hardware.gamepadavecandroid:required="false"si l'application prend en charge la saisie par manette. - Navigateurs : Les navigateurs DOIVENT inclure
<category android:name="android.intent.category.APP_BROWSER"/>dans le<intent-filter>MAINd'une<activity>exportée (android:exported="true").
1.2 : Identifier les plateformes cibles et les fonctionnalités matérielles
- Android Auto : Les applications ciblant Android Auto (Android 15+) DOIVENT ajouter
<category android:name="android.intent.category.CAR_LAUNCHER"/>à un<intent-filter>dans le fichierAndroidManifest.xml. Ajoutez-le au<intent-filter>MAINsauf si l'utilisateur spécifie un autre filtre d'intention. NE JAMAIS ajouter cette catégorie si l'application cible uniquement Android Automotive OS. - Android Automotive OS : Les applications ciblant Android Automotive OS DOIVENT déclarer
<uses-feature android:name="android.hardware.type.automotive" android:required="false"/>("false"pour les pistes mobiles ou multi-facteurs de forme ;"true"uniquement si l'application sera publiée sur la piste dédiée Automotive OS) dans le fichierAndroidManifest.xml. NE JAMAIS ajouter cette balise de fonctionnalité si l'application cible uniquement Android Auto. - Fonctionnalités matérielles non requises (
DO-1) : Pour satisfaire les exigences de fonctionnalités de Google Play etDO-1, déclarez les fonctionnalités non prises en charge (android.hardware.wifi,android.hardware.screen.portrait,android.hardware.screen.landscape,android.hardware.camera) avecandroid:required="false", et supprimezandroid:required="true"sur les fonctionnalités absentes dans les voitures. - Continuité d'affichage (
LS-C1) : Configurezandroid:configChangespourscreenSize,smallestScreenSize,orientation,screenLayoutetdensitysur<activity>afin que le passage vers ou depuis un écran distant ne recrée pas l'activité ou ne fasse pas hoqueter la lecture. - Mode de compatibilité et distraction : La déclaration du mode de compatibilité est facultative et n'est pertinente que pour les applications qui prennent en charge Android Automotive OS. NE JAMAIS ajouter le mode de compatibilité pour les applications qui ne prennent en charge que Android Auto. L'ajout de
android.hardware.type.automotivedésactive le mode de compatibilité à moins que<meta-data android:name="android.software.car.display_compatibility" android:value="true"/>ne soit ajouté à<application>. Lorsqu'une application déclareandroid.software.car.display_compatibilitydansAndroidManifest.xml, l'application DOIT vérifierPackageManager.hasSystemFeature("android.software.car.display_compatibility")au moment de l'exécution et masquer les boutons de retour ou de fermeture redondants dans l'application lorsque cela renvoietrue. Les applications stationnées NE DOIVENT PAS inclure<meta-data android:name="distractionOptimized" android:value="true"/>sur une<activity>(DD-2,DD-3).
Étape 2 : Fournir la navigation arrière et la prise en charge de l'orientation
2.1 : Ajouter des boutons de retour et de fermeture dans l'application
Les véhicules Android Automotive OS ne sont pas tenus d'avoir un bouton de retour matériel ou logiciel ni une navigation par gestes (AN-1). Fournissez des contrôles de retour et de fermeture explicites dans l'interface utilisateur sur chaque écran non racine, y compris les écrans de détail et les lecteurs plein écran :
- Si
AndroidManifest.xmlne déclare pasandroid.software.car.display_compatibility: Affichez les contrôles de retour et de fermeture dans l'application de manière inconditionnelle. - Si
AndroidManifest.xmldéclareandroid.software.car.display_compatibilityavecandroid:value="true": Affichez les contrôles de retour et de fermeture dans l'application uniquement lorsquePackageManager.hasSystemFeature("android.software.car.display_compatibility")renvoiefalse.
2.2 : Protéger les demandes d'orientation au moment de l'exécution
Les écrans des véhicules ont des orientations fixes. Demander le mode paysage sur un écran de voiture fixé en portrait provoque des boîtes aux lettres ou des boucles de scintillement (DO-1). Supprimez les attributs android:screenOrientation codés en dur du manifeste et vérifiez PackageManager.FEATURE_SCREEN_LANDSCAPE et PackageManager.FEATURE_SCREEN_PORTRAIT avant de définir requestedOrientation.
Étape 3 : S'adapter aux barres système et aux découpes d'écran irrégulières
3.1 : Suivre les encarts de barres système contrôlables
Sur Android Automotive OS, les OEM sont autorisés à verrouiller les barres système visibles afin que les commandes de climatisation et du véhicule restent accessibles (AR-1). Suivez les encarts contrôlables avec OnControllableInsetsChangedListener, masquez uniquement les barres contrôlables et appliquez windowInsetsPadding pour les encarts statusBars, navigationBars et displayCutout non contrôlables.
3.2 : Rendre les découpes d'écran irrégulières en toute sécurité
Pour répondre au critère de niveau 1 AR-2 sur les écrans incurvés ou non rectangulaires, NE JAMAIS utiliser LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT (0) ou LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES (1). Configurez LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS (3) ou LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER (2) en utilisant le qualificateur de ressource car dans res/values-car-v30/integers.xml :
<integer name="windowLayoutInDisplayCutoutMode">3</integer>
<!— saut de ligne intentionnel —>
Protégez l'interface utilisateur interactive, les barres d'application et les listes défilables avec WindowInsets.safeDrawing.
Étape 4 : Répondre aux exigences de distraction du conducteur et de lecture multimédia
4.1 : Mettre en pause la lecture lors des transitions du cycle de vie et bloquer la reprise en conduite
Lorsqu'un véhicule commence à se déplacer, le système d'exploitation recouvre les activités stationnées avec une superposition de blocage et déclenche onPause (et onStop sur les véhicules en mode de compatibilité). Pour satisfaire DD-2, DD-3 et MC-1 :
- Mettez en pause la lecture sur
ON_PAUSEouON_STOPen utilisantLifecycleEventEffectdeandroidx.lifecycle:lifecycle-runtime-compose. - Exposez un
MediaSession(androidx.media3:media3-session) avec les commandes de lecture, pause ou arrêt et les métadonnées des éléments (MC-1), et libérez-le dansonCleared. - Ajoutez
useLibrary("android.car")dansbuild.gradle.kts, enveloppezExoPlayerdansForwardingSimpleBasePlayer, écoutezCarUxRestrictionsManager, mettez en pause la lecture lorsqueisRequiresDistractionOptimizationdevienttrue, et supprimezCOMMAND_PLAY_PAUSEdansgetStateafin que les contrôlesMediaSessionexternes ne puissent pas reprendre la lecture pendant la conduite :
@UnstableApi
class CarAwarePlayer(context: Context) :
ForwardingSimpleBasePlayer(ExoPlayer.Builder(context).build()) {
private var shouldPreventPlay = false
private var pausedByUxRestrictions = false
private lateinit var carUxRestrictionsManager: CarUxRestrictionsManager
init {
if (context.packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
val car = Car.createCar(context)
carUxRestrictionsManager =
car.getCarManager(Car.CAR_UX_RESTRICTION_SERVICE) as CarUxRestrictionsManager
shouldPreventPlay =
carUxRestrictionsManager.currentCarUxRestrictions.isRequiresDistractionOptimization
invalidateState()
carUxRestrictionsManager.registerListener { restrictions: CarUxRestrictions ->
shouldPreventPlay = restrictions.isRequiresDistractionOptimization
if (!shouldPreventPlay && pausedByUxRestrictions) {
handleSetPlayWhenReady(true)
invalidateState()
} else if (shouldPreventPlay && isPlaying) {
pausedByUxRestrictions = true
handleSetPlayWhenReady(false)
invalidateState()
}
}
}
}
override fun getState(): State {
val state = super.getState()
return state.buildUpon()
.setAvailableCommands(
state.availableCommands.buildUpon()
.removeIf(COMMAND_PLAY_PAUSE, shouldPreventPlay)
.build()
)
.build()
}
override fun handleRelease(): ListenableFuture<*> {
if (::carUxRestrictionsManager.isInitialized) {
carUxRestrictionsManager.unregisterListener()
}
return super.handleRelease()
}
}
<!— saut de ligne intentionnel —>
4.2 : Prendre en charge l'audio en arrière-plan pendant la conduite (fonctionnalité bêta facultative)
Comme il s'agit d'une fonctionnalité bêta facultative, NE JAMAIS implémenter cette fonctionnalité à moins que l'utilisateur ne le demande. Pour implémenter la fonctionnalité :
- Déclarez
<uses-feature android:name="com.android.car.background_audio_while_driving" android:required="false" />dansAndroidManifest.xml, ainsi que les permissionsFOREGROUND_SERVICEetFOREGROUND_SERVICE_MEDIA_PLAYBACK. - Vérifiez la prise en charge de la fonctionnalité par le véhicule au moment de l'exécution avec
CarFeatures.isFeatureEnabled(context, CarFeatures.FEATURE_BACKGROUND_AUDIO_WHILE_DRIVING)deandroidx.car.app:app. Ne définissezshouldPreventPlayque lorsque!isBackgroundAudioWhileDrivingSupported && restrictions.isRequiresDistractionOptimization. - Hébergez le
MediaSessionet le lecteur dans unMediaSessionServiceexporté avecandroid:foregroundServiceType="mediaPlayback", et connectez-vous depuis l'interface utilisateur en utilisantMediaController.BuilderetbuildAsync.
Étape 5 : Protéger les données sensibles dans les applications de navigateur
Comme les véhicules sont des appareils partagés, les navigateurs DOIVENT bloquer l'accès aux mots de passe enregistrés et aux méthodes de paiement, sauf s'ils sont protégés par un verrouillage de profil (SD-1). Avant de synchroniser les mots de passe ou les données de paiement vers le véhicule, les navigateurs DOIVENT inviter l'utilisateur à s'authentifier et afficher un avis sur l'écran de la voiture indiquant que ses données seront synchronisées avec la voiture (SD-2).
Vérifiez si un code PIN, un schéma ou un mot de passe est défini à l'aide de KeyguardManager, ouvrez Settings.ACTION_SECURITY_SETTINGS s'il n'est pas défini, et authentifiez-vous avec BiometricPrompt en utilisant BiometricManager.Authenticators.DEVICE_CREDENTIAL uniquement (les véhicules n'ont pas de matériel biométrique) :
val keyguardManager = context.getSystemService<KeyguardManager>()
val isDeviceSecure = keyguardManager?.isDeviceSecure == true
lateinit var biometricPrompt: BiometricPrompt
if (!isDeviceSecure) {
context.startActivity(
Intent(Settings.ACTION_SECURITY_SETTINGS).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
)
} else {
val promptInfo = BiometricPrompt.PromptInfo.Builder()
.setTitle(context.getString(R.string.auth_title))
.setSubtitle(context.getString(R.string.sync_data_to_car_notice))
.setAllowedAuthenticators(BiometricManager.Authenticators.DEVICE_CREDENTIAL)
.build()
biometricPrompt.authenticate(promptInfo)
}
<!— saut de ligne intentionnel —>
Étape 6 : Vérifier l'implémentation
Construisez l'application en utilisant ./gradlew assembleDebug. Trouvez et corrigez toute erreur de construction.
Post-implémentation
…
[tronqué pour la traduction]