L'Assistant Google vous permet d'utiliser des commandes vocales pour contrôler de nombreux appareils, comme Google Home, votre téléphone et plus encore. Il dispose d'une fonctionnalité intégrée comprendre les commandes multimédias ("joue quelque chose de Beyoncé") et prend en charge les commandes multimédias (pause, saut, avance rapide, pouce levé, etc.) ;
L'Assistant communique avec les applications multimédias Android à l'aide d'un contenu multimédia session. Elle peut utiliser des intents ou des services pour lancer votre application et lancer la lecture. Pour des résultats optimaux, votre application doit : mettre en œuvre toutes les fonctionnalités décrites sur cette page.
Utiliser une session multimédia
Chaque application audio et vidéo doit implémenter un session multimédia pour que l'Assistant puisse fonctionner les commandes de transport une fois la lecture lancée.
Notez que même si l'Assistant n'utilise que les actions indiquées dans cette section, les
il est recommandé d'implémenter toutes les API de préparation et de lecture pour garantir
la compatibilité avec d'autres applications. Pour toutes les actions
que vous n'acceptez pas,
les rappels de session multimédia
peuvent simplement renvoyer une erreur en utilisant
ERROR_CODE_NOT_SUPPORTED
Activez les commandes multimédias et de transport en définissant ces indicateurs dans le menu
Objet MediaSession
:
Kotlin
session.setFlags( MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS or MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS )
Java
session.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
La session multimédia de votre application doit déclarer les actions compatibles et implémenter la
les rappels de session multimédia correspondants. Déclarez vos actions acceptées dans
setActions()
La Lecteur de musique universel Android exemple de projet est un bon exemple de la façon d'organiser une session multimédia.
Actions de lecture
Pour lancer la lecture à partir d'un service, une session multimédia doit comporter les actions PLAY
suivantes et leurs rappels:
Action | Rappel |
---|---|
ACTION_PLAY |
onPlay() |
ACTION_PLAY_FROM_SEARCH |
onPlayFromSearch() |
ACTION_PLAY_FROM_URI (*) |
onPlayFromUri() |
Votre session doit également implémenter ces actions PREPARE
et leurs rappels:
Action | Rappel |
---|---|
ACTION_PREPARE |
onPrepare() |
ACTION_PREPARE_FROM_SEARCH |
onPrepareFromSearch() |
ACTION_PREPARE_FROM_URI (*) |
onPrepareFromUri() |
Grâce à la mise en œuvre des API de préparation, la latence de lecture après une commande vocale peut être réduit. Les applications multimédias qui souhaitent améliorer la latence de lecture peuvent utiliser plus de temps pour commencer la mise en cache du contenu et la préparation de la lecture du contenu multimédia.
Analyser les requêtes de recherche
Lorsqu'un utilisateur recherche un élément multimédia spécifique, par exemple "Mets du jazz sur
[nom de votre application]" ou "Écouter [titre de la chanson]", le
onPrepareFromSearch()
ou
onPlayFromSearch()
reçoit un paramètre de requête et un bundle d'extras.
Votre application doit analyser la requête de recherche vocale et lancer la lecture en suivant ces étapes:
- Utiliser le bundle d'extras et la chaîne de requête de recherche renvoyés par la recherche vocale pour filtrer les résultats.
- Créez une file d'attente de lecture en fonction de ces résultats.
- Lire l'élément multimédia le plus pertinent parmi les résultats.
onPlayFromSearch()
utilise un paramètre extras avec des informations plus détaillées provenant de la voix.
recherche. Ces extras vous aident à trouver le contenu audio dans votre application pour la lecture.
Si les résultats de recherche ne sont pas en mesure de fournir ces données, vous pouvez implémenter une logique
pour analyser la requête de recherche brute et lire les titres appropriés en fonction
requête.
Les extras suivants sont compatibles avec Android Automotive OS et Android Auto:
L'extrait de code suivant montre comment remplacer onPlayFromSearch()
dans votre MediaSession.Callback
pour analyser la requête de recherche vocale et lancer la lecture:
Kotlin
override fun onPlayFromSearch(query: String?, extras: Bundle?) { if (query.isNullOrEmpty()) { // The user provided generic string e.g. 'Play music' // Build appropriate playlist queue } else { // Build a queue based on songs that match "query" or "extras" param val mediaFocus: String? = extras?.getString(MediaStore.EXTRA_MEDIA_FOCUS) if (mediaFocus == MediaStore.Audio.Artists.ENTRY_CONTENT_TYPE) { isArtistFocus = true artist = extras.getString(MediaStore.EXTRA_MEDIA_ARTIST) } else if (mediaFocus == MediaStore.Audio.Albums.ENTRY_CONTENT_TYPE) { isAlbumFocus = true album = extras.getString(MediaStore.EXTRA_MEDIA_ALBUM) } // Implement additional "extras" param filtering } // Implement your logic to retrieve the queue var result: String? = when { isArtistFocus -> artist?.also { searchMusicByArtist(it) } isAlbumFocus -> album?.also { searchMusicByAlbum(it) } else -> null } result = result ?: run { // No focus found, search by query for song title query?.also { searchMusicBySongTitle(it) } } if (result?.isNotEmpty() == true) { // Immediately start playing from the beginning of the search results // Implement your logic to start playing music playMusic(result) } else { // Handle no queue found. Stop playing if the app // is currently playing a song } }
Java
@Override public void onPlayFromSearch(String query, Bundle extras) { if (TextUtils.isEmpty(query)) { // The user provided generic string e.g. 'Play music' // Build appropriate playlist queue } else { // Build a queue based on songs that match "query" or "extras" param String mediaFocus = extras.getString(MediaStore.EXTRA_MEDIA_FOCUS); if (TextUtils.equals(mediaFocus, MediaStore.Audio.Artists.ENTRY_CONTENT_TYPE)) { isArtistFocus = true; artist = extras.getString(MediaStore.EXTRA_MEDIA_ARTIST); } else if (TextUtils.equals(mediaFocus, MediaStore.Audio.Albums.ENTRY_CONTENT_TYPE)) { isAlbumFocus = true; album = extras.getString(MediaStore.EXTRA_MEDIA_ALBUM); } // Implement additional "extras" param filtering } // Implement your logic to retrieve the queue if (isArtistFocus) { result = searchMusicByArtist(artist); } else if (isAlbumFocus) { result = searchMusicByAlbum(album); } if (result == null) { // No focus found, search by query for song title result = searchMusicBySongTitle(query); } if (result != null && !result.isEmpty()) { // Immediately start playing from the beginning of the search results // Implement your logic to start playing music playMusic(result); } else { // Handle no queue found. Stop playing if the app // is currently playing a song } }
Pour obtenir un exemple plus détaillé d'implémentation de la recherche vocale pour lire du contenu audio contenu dans votre application, consultez la page consacrée au lecteur de musique Universal Android. échantillon.
Gérer les requêtes vides
Si onPrepare()
, onPlay()
, onPrepareFromSearch()
ou onPlayFromSearch()
sont appelés sans requête de recherche, votre application multimédia doit lire la chaîne "actuelle"
médias. Si aucun contenu multimédia n'est actuellement disponible, l'application doit essayer de lire un contenu, par exemple
comme un titre de la playlist la plus récente ou d'une file d'attente aléatoire. L'assistant utilise
ces API lorsqu'un utilisateur demande à mettre de la musique sur [nom de votre application] sans
plus d'informations.
Lorsqu'un utilisateur dit Mets de la musique sur [nom de votre application], Android Automotive OS ou
Android Auto tente de lancer votre application et de lire du contenu audio en appelant la méthode onPlayFromSearch()
de votre application
. Toutefois, comme l'utilisateur n'a pas dit le nom de l'élément multimédia, onPlayFromSearch()
reçoit un paramètre de requête vide. Dans ce cas, votre application doit
réagissent en diffusant immédiatement un contenu audio, comme un titre de l'un des derniers
ou dans une file d'attente aléatoire.
Déclarer une ancienne compatibilité des commandes vocales
Dans la plupart des cas, la gestion des actions de lecture décrites ci-dessus permet à votre application la fonctionnalité de lecture dont elle a besoin. Cependant, certains systèmes exigent que votre application contiennent un filtre d'intent pour la recherche. Vous devez déclarer la prise en charge de cet intent dans les fichiers manifestes de votre application.
Incluez ce code dans le fichier manifeste d'une application pour téléphone:
<activity>
<intent-filter>
<action android:name=
"android.media.action.MEDIA_PLAY_FROM_SEARCH" />
<category android:name=
"android.intent.category.DEFAULT" />
</intent-filter>
</activity>
Commandes de transport
Lorsque la session multimédia de votre appli est active, l'Assistant peut émettre des commandes vocales pour contrôler la lecture et mettre à jour les métadonnées des contenus multimédias. Pour que cela fonctionne, doit permettre les actions suivantes et implémenter Rappels:
Action | Rappel | Description |
---|---|---|
ACTION_SKIP_TO_NEXT |
onSkipToNext() |
Vidéo suivante |
ACTION_SKIP_TO_PREVIOUS |
onSkipToPrevious() |
Titre précédent |
ACTION_PAUSE, ACTION_PLAY_PAUSE |
onPause() |
Mettre en pause |
ACTION_STOP |
onStop() |
Arrêter |
ACTION_PLAY |
onPlay() |
Reprendre |
ACTION_SEEK_TO |
onSeekTo() |
Revenir en arrière de 30 secondes |
ACTION_SET_RATING |
onSetRating(android.support.v4.media.RatingCompat) |
J'aime/Je n'aime pas. |
ACTION_SET_CAPTIONING_ENABLED |
onSetCaptioningEnabled(boolean) |
activer ou désactiver les sous-titres ; |
Remarque :
- Pour que les commandes de recherche fonctionnent,
PlaybackState
doit être à jour avecstate, position, playback speed, and update time
. L'application doit appelersetPlaybackState()
lorsque l'état change. - L'application multimédia doit également maintenir les métadonnées de la session multimédia à jour. Cela permet de répondre à des questions telles que "Quel est le titre en cours de lecture ?". L'application doit appeler
setMetadata()
lorsque les champs applicables (comme le titre, l'artiste et le nom du morceau) changent. MediaSession.setRatingType()
doit être défini pour indiquer le type de classification accepté par l'application, et l'application doit implémenteronSetRating()
. Si l'application n'est pas compatible avec la classification, le type de classification doit êtreRATING_NONE
.
Les commandes vocales que vous acceptez peuvent varier en fonction du type de contenu.
Type de contenu | Actions requises |
---|---|
Musique |
Fonctionnalités obligatoires: Lecture, Pause, Arrêt, Passer à l'élément suivant et Passer à l'élément précédent Recommander fortement l'assistance pour: Aller à |
Podcast |
Doit être compatible: Lire, Mettre en pause, Arrêter et Aller à Recommander la prise en charge de: Passer à "Suivant" et "Précédent" |
Livre audio | Doit être compatible: Lire, Mettre en pause, Arrêter et Aller à |
Radio | Doit être compatible: Lecture, Pause et Arrêt |
Actualités | Fonctionnalités obligatoires: Lecture, Pause, Arrêt, Passer à l'élément suivant et Passer à l'élément précédent |
Vidéo |
Fonctionnalités obligatoires: lecture, mise en pause, arrêt, fonctionnalité de recherche, retour en arrière et avance rapide Recommander fortement l'assistance pour: Passer à l'élément suivant et Passer à l'élément précédent |
Vous devez accepter autant d'actions listées ci-dessus que vos offres de produits. autoriser, mais toujours de répondre convivialement à toute autre action. Par exemple, si seulement les utilisateurs premium ont la possibilité de revenir à l'élément précédent, vous pouvez augmenter une erreur si un utilisateur du niveau sans frais demande à l'Assistant de revenir à l'élément précédent. Pour en savoir plus, consultez la section Gestion des erreurs.
Exemples de requêtes vocales à essayer
Le tableau suivant présente quelques exemples de requêtes à utiliser pour testez votre implémentation:
Rappel MediaSession | l'expression "Hey Google" à utiliser | |
---|---|---|
onPlay() |
"Lance la lecture." "Reprends la lecture". |
|
onPlayFromSearch()
onPlayFromUri() |
Musique |
"Joue de la musique ou des titres sur (nom de l'application)." Cette requête est vide. "Mets (titre | artiste | album | genre | playlist) sur (nom de l'application)" |
Radio | "Mets (fréquence | station) sur (nom de l'application)" | |
Audiobook |
"Lis mon livre audio sur (nom de l'application)." "Lis (livre audio) sur (nom de l'application)." |
|
Podcasts | "Lis (podcast) sur (nom de l'application)." | |
onPause() |
"Mets sur pause." | |
onStop() |
"Arrête la lecture." | |
onSkipToNext() |
"Next (titre | épisode | titre)." | |
onSkipToPrevious() |
"Précédent (titre | épisode | titre)." | |
onSeekTo() |
"Reprends la lecture." "Avance de ## secondes." "Reviens en arrière de ## minutes." |
|
N/A (Gardez votre
MediaMetadata
mis à jour) |
"Qu'est-ce qui passe actuellement ?" |
Erreurs
L'Assistant traite les erreurs d'une session multimédia lorsqu'elles se produisent et signale
aux utilisateurs. Assurez-vous que votre session multimédia met à jour l'état de transport et
code d'erreur dans son PlaybackState
, comme décrit dans la section Utiliser un
session multimédia. Assistant
reconnaît tous les codes d'erreur renvoyés par
getErrorCode()
Cas fréquemment mal gérés
Voici quelques exemples de cas d'erreur à gérer correctement:
- L'utilisateur doit se connecter
<ph type="x-smartling-placeholder">
- </ph>
- Définissez le code d'erreur
PlaybackState
surERROR_CODE_AUTHENTICATION_EXPIRED
. - Définissez le message d'erreur
PlaybackState
. - Si nécessaire pour la lecture, définissez l'état
PlaybackState
surSTATE_ERROR
. Sinon, conservez le reste dePlaybackState
tel quel.
- Définissez le code d'erreur
- L'utilisateur demande une action non disponible
<ph type="x-smartling-placeholder">
- </ph>
- Définissez le code d'erreur
PlaybackState
de manière appropriée. Par exemple, définissez le paramètrePlaybackState
àERROR_CODE_NOT_SUPPORTED
si l'action n'est pas acceptée ouERROR_CODE_PREMIUM_ACCOUNT_REQUIRED
si l'action est protégée par une procédure de connexion. - Définissez le message d'erreur
PlaybackState
. - Conservez le reste de
PlaybackState
tel quel.
- Définissez le code d'erreur
- L'utilisateur demande un contenu non disponible dans l'application
<ph type="x-smartling-placeholder">
- </ph>
- Définissez le code d'erreur
PlaybackState
de manière appropriée. Par exemple, utilisezERROR_CODE_NOT_AVAILABLE_IN_REGION
- Définissez le message d'erreur
PlaybackState
. - Définissez l'état
PlaybackSate
surSTATE_ERROR
pour interrompre la lecture. sinon conserver le reste dePlaybackState
tel quel.
- Définissez le code d'erreur
- L'utilisateur demande un contenu lorsqu'aucune correspondance exacte n'est disponible. Par exemple, un
utilisateur de la version sans frais demandant des contenus réservés aux utilisateurs de la version premium
- Nous vous recommandons de ne pas renvoyer d'erreur, mais plutôt de donner la priorité de trouver quelque chose de similaire à jouer. L'Assistant se chargera de parler le plus souvent une réponse vocale pertinente avant le début de la lecture.
Lecture avec un intent
L'Assistant peut lancer une application audio ou vidéo, et lancer la lecture en envoyant une avec un lien profond.
L'intent et son lien profond peuvent provenir de différentes sources:
- Lorsque l'Assistant est au démarrage d'une application mobile, il peut utiliser la recherche Google pour récupérer du contenu balisé fournit une action de visionnage avec un lien.
- Lorsque l'Assistant lance une appli TV, celle-ci doit inclure un
Moteur de recherche TV
pour exposer les URI du contenu multimédia. L'Assistant envoie une requête à
Le fournisseur de contenu qui doit renvoyer un intent contenant un URI pour le lien profond
une action facultative.
Si la requête renvoie une action dans l'intent,
l'Assistant renvoie cette action et l'URI à votre application.
Si le fournisseur n'a pas précisé
une action, l'Assistant ajoutera
ACTION_VIEW
à l'intent.
L'Assistant ajoute EXTRA_START_PLAYBACK
avec la valeur true
à l'intent qu'il envoie à votre application. Votre application doit lancer la lecture lorsqu'elle
reçoit un intent avec EXTRA_START_PLAYBACK
.
Gérer les intents actifs
Les utilisateurs peuvent demander à l'Assistant de lire un contenu pendant que votre appli est en cours de lecture d'une requête précédente. Cela signifie que votre application peut recevoir de nouveaux intents lancer la lecture alors que l'activité de lecture est déjà lancée et active ;
Les activités compatibles avec les intents comportant des liens profonds doivent remplacer
onNewIntent()
pour gérer les nouvelles requêtes.
Au démarrage de la lecture, l'Assistant peut ajouter des
indicateurs
à l'intent qu'il envoie à votre application. En particulier, elle peut ajouter
FLAG_ACTIVITY_CLEAR_TOP
ou
FLAG_ACTIVITY_NEW_TASK
ou les deux. Bien que votre code
n'a pas besoin de gérer ces indicateurs, le système Android y répond.
Cela peut affecter le comportement de votre application lorsqu'une deuxième requête de lecture avec un nouvel URI arrive
pendant que l'URI précédent est toujours en cours de lecture. Dans ce cas, nous vous recommandons de tester la réponse de votre application. Vous pouvez utiliser la commande adb
outil de ligne de commande pour simuler la situation (la constante 0x14000000
est l'opérateur OR booléen bit à bit des deux indicateurs):
adb shell 'am start -a android.intent.action.VIEW --ez android.intent.extra.START_PLAYBACK true -d "<first_uri>"' -f 0x14000000
adb shell 'am start -a android.intent.action.VIEW --ez android.intent.extra.START_PLAYBACK true -d "<second_uri>"' -f 0x14000000
Lecture depuis un service
Si votre application dispose d'un
media browser service
qui permet d'établir des connexions depuis l'Assistant,
l'Assistant peut démarrer l'application en communiquant avec le service
media session
Le service de navigateur multimédia ne doit jamais lancer d'activité.
L'Assistant lancera votre activité en fonction des PendingIntent
que vous aurez définies
avec setSessionActivity().
Veillez à définir MediaSession.Token lorsque vous initialisez le service de navigateur multimédia. N'oubliez pas de définir les actions de lecture acceptées à tout moment, y compris pendant l'initialisation. L'Assistant attend que votre contenu multimédia l'application pour définir les actions de lecture avant que l'Assistant n'envoie la première lecture .
Pour démarrer à partir d'un service, l'Assistant implémente les API clientes du navigateur multimédia. Il effectue des appels TransportControls qui déclenchent des rappels d'action de LECTURE sur votre la session multimédia de l'application.
Le schéma suivant illustre l'ordre des appels générés par l'Assistant et les les rappels de session multimédia correspondants. (Les rappels de préparation ne sont envoyés que si votre application les prend en charge). Tous les appels sont asynchrones. L'Assistant n'effectue pas attendre la réponse de votre application.
Lorsqu'un utilisateur énonce une commande vocale pour écouter de la musique, l'Assistant répond par une courte annonce. Dès que l'annonce est terminée, l'Assistant émet une action de LECTURE. Il n'attend pas d'état de lecture spécifique.
Si votre application est compatible avec les actions ACTION_PREPARE_*
, l'Assistant appelle l'action PREPARE
avant de commencer l'annonce.
Connexion à MediaBrowserService
Pour utiliser un service afin de démarrer votre application, l'Assistant doit pouvoir se connecter au MediaBrowserService de l'application et
pour récupérer son MediaSession.Token. Les requêtes de connexion sont gérées
onGetRoot()
. Il existe deux façons de gérer les requêtes:
- Accepter toutes les demandes de connexion
- Accepter uniquement les demandes de connexion provenant de l'application Assistant
Accepter toutes les demandes de connexion
Vous devez renvoyer un BrowserRoot pour permettre à l'Assistant d'envoyer des commandes à votre session multimédia. La méthode la plus simple consiste à autoriser toutes les applications MediaBrowser à se connecter à votre service MediaBrowserService. Vous devez renvoyer une valeur BrowserRoot non nulle. Voici le code applicable issu du lecteur de musique Universal:
Kotlin
override fun onGetRoot( clientPackageName: String, clientUid: Int, rootHints: Bundle? ): BrowserRoot? { // To ensure you are not allowing any arbitrary app to browse your app's contents, you // need to check the origin: if (!packageValidator.isCallerAllowed(this, clientPackageName, clientUid)) { // If the request comes from an untrusted package, return an empty browser root. // If you return null, then the media browser will not be able to connect and // no further calls will be made to other media browsing methods. Log.i(TAG, "OnGetRoot: Browsing NOT ALLOWED for unknown caller. Returning empty " + "browser root so all apps can use MediaController. $clientPackageName") return MediaBrowserServiceCompat.BrowserRoot(MEDIA_ID_EMPTY_ROOT, null) } // Return browser roots for browsing... }
Java
@Override public BrowserRoot onGetRoot(@NonNull String clientPackageName, int clientUid, Bundle rootHints) { // To ensure you are not allowing any arbitrary app to browse your app's contents, you // need to check the origin: if (!packageValidator.isCallerAllowed(this, clientPackageName, clientUid)) { // If the request comes from an untrusted package, return an empty browser root. // If you return null, then the media browser will not be able to connect and // no further calls will be made to other media browsing methods. LogHelper.i(TAG, "OnGetRoot: Browsing NOT ALLOWED for unknown caller. " + "Returning empty browser root so all apps can use MediaController." + clientPackageName); return new MediaBrowserServiceCompat.BrowserRoot(MEDIA_ID_EMPTY_ROOT, null); } // Return browser roots for browsing... }
Accepter le package et la signature de l'application Assistant
Vous pouvez autoriser explicitement l'Assistant à se connecter à votre service de navigateur multimédia en vérifiant le nom et la signature de son package. Votre application recevra le nom du package dans la méthode onGetRoot de votre MediaBrowserService. Vous devez renvoyer un BrowserRoot pour permettre à l'Assistant d'envoyer des commandes à votre session multimédia. La Lecteur de musique universel gère une liste de signatures et de noms de packages connus. Vous trouverez ci-dessous les noms des packages et les signatures utilisés par l'Assistant Google.
<signature name="Google" package="com.google.android.googlequicksearchbox">
<key release="false">19:75:b2:f1:71:77:bc:89:a5:df:f3:1f:9e:64:a6:ca:e2:81:a5:3d:c1:d1:d5:9b:1d:14:7f:e1:c8:2a:fa:00</key>
<key release="true">f0:fd:6c:5b:41:0f:25:cb:25:c3:b5:33:46:c8:97:2f:ae:30:f8:ee:74:11:df:91:04:80:ad:6b:2d:60:db:83</key>
</signature>
<signature name="Google Assistant on Android Automotive OS" package="com.google.android.carassistant">
<key release="false">17:E2:81:11:06:2F:97:A8:60:79:7A:83:70:5B:F8:2C:7C:C0:29:35:56:6D:46:22:BC:4E:CF:EE:1B:EB:F8:15</key>
<key release="true">74:B6:FB:F7:10:E8:D9:0D:44:D3:40:12:58:89:B4:23:06:A6:2C:43:79:D0:E5:A6:62:20:E3:A6:8A:BF:90:E2</key>
</signature>