Assistant Google et applications multimédias

L'Assistant Google vous permet d'utiliser des commandes vocales pour contrôler de nombreux appareils, comme Google Home, votre téléphone, etc. Il est doté d'une fonctionnalité intégrée pour comprendre les commandes multimédias ("Mets du contenu de Beyoncé") et prend en charge les commandes multimédias (pause, passer, avance rapide, pouce vers le haut, etc.).

L'Assistant communique avec les applications multimédias Android à l'aide d'une session multimédia. Elle peut utiliser des intents ou des services pour lancer votre application et lancer la lecture. Pour des résultats optimaux, votre application doit implémenter toutes les fonctionnalités décrites sur cette page.

Utiliser une session multimédia

Chaque application audio et vidéo doit implémenter une session multimédia afin que l'Assistant puisse utiliser les commandes de transport une fois la lecture commencée.

Bien que l'Assistant n'utilise que les actions listées dans cette section, il est recommandé d'implémenter toutes les API de préparation et de lecture pour assurer la compatibilité avec les autres applications. Pour toutes les actions non compatibles, les rappels de session multimédia peuvent simplement renvoyer une erreur à l'aide de ERROR_CODE_NOT_SUPPORTED.

Activez les commandes multimédias et de transport en définissant ces indicateurs dans l'objet MediaSession de votre application:

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 les rappels de session multimédia correspondants. Déclarez les actions compatibles dans setActions().

L'exemple de projet Universal Android Music Player constitue un bon exemple de configuration d'une session multimédia.

Actions de lecture

Pour lancer la lecture à partir d'un service, une session multimédia doit comporter les actions PLAY 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()

(*) Les actions basées sur les URI de l'Assistant Google ne fonctionnent que pour les entreprises qui fournissent des URI à Google. Pour savoir comment décrire votre contenu multimédia à Google, consultez Actions multimédias.

En implémentant les API de préparation, vous pouvez réduire la latence de lecture après une commande vocale. Les applications multimédias qui souhaitent améliorer la latence de lecture peuvent utiliser le temps supplémentaire pour commencer à mettre en cache le contenu et à préparer la lecture des contenus multimédias.

Analyser les requêtes de recherche

Lorsqu'un utilisateur recherche un élément multimédia spécifique, comme Mets du jazz sur [nom de votre application] ou Écouter [titre du titre], la méthode de rappel 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 procédant comme suit:

  1. Utilisez le bundle d'extras et la chaîne de requête de recherche renvoyés par la recherche vocale pour filtrer les résultats.
  2. Créez une file d'attente de lecture en fonction de ces résultats.
  3. Lire l'élément multimédia le plus pertinent parmi les résultats

La méthode onPlayFromSearch() utilise un paramètre extras avec des informations plus détaillées issues de la recherche vocale. Ces éléments supplémentaires vous aident à trouver le contenu audio dans votre application à lire. Si les résultats de recherche ne peuvent pas fournir ces données, vous pouvez mettre en œuvre une logique pour analyser la requête de recherche brute et lire les pistes appropriées en fonction de la requête.

Les extras suivants sont compatibles avec Android Automotive OS et Android Auto:

L'extrait de code suivant montre comment remplacer la méthode onPlayFromSearch() dans votre implémentation de 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 afin de lire du contenu audio dans votre application, consultez l'exemple Universal Android Music Player.

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 le contenu multimédia "actuel". Si aucun contenu multimédia n'est en cours de lecture, l'application doit essayer de lire quelque chose, comme un titre de la playlist la plus récente ou une file d'attente aléatoire. L'assistant utilise ces API lorsqu'un utilisateur demande "Mets de la musique sur [nom de votre application]" sans informations supplémentaires.

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 prononcé le nom de l'élément multimédia, la méthode onPlayFromSearch() reçoit un paramètre de requête vide. Dans ce cas, votre application doit répondre en lisant immédiatement du contenu audio, par exemple un titre de la playlist la plus récente ou une file d'attente aléatoire.

Déclarer l'ancienne compatibilité des commandes vocales

Dans la plupart des cas, la gestion des actions de lecture décrites ci-dessus donne à votre application toutes les fonctionnalités de lecture dont elle a besoin. Cependant, certains systèmes nécessitent que votre application contienne un filtre d'intent pour la recherche. Vous devez déclarer la prise en charge de ce filtre d'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

Une fois la session multimédia de votre application active, l'Assistant peut émettre des commandes vocales pour contrôler la lecture et mettre à jour les métadonnées multimédias. Pour que cela fonctionne, votre code doit activer les actions suivantes et implémenter les rappels correspondants:

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() Suspendre
ACTION_STOP onStop() Arrêter
ACTION_PLAY onPlay() Réactiver
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 avec state, position, playback speed, and update time. L'application doit appeler setPlaybackState() lorsque l'état change.
  • L'application multimédia doit également maintenir les métadonnées de la session multimédia à jour. Cela permet d'obtenir des réponses à des questions telles que "Quelle est la chanson qui est diffusée ?" 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 celle-ci doit implémenter onSetRating(). Si l'application ne prend pas en charge la classification, définissez le type de classification sur RATING_NONE.

Les commandes vocales acceptées varient probablement selon le type de contenu.

Type de contenu Actions requises
Musique

Compatibilité requise : "Lire", "Mettre en pause", "Arrêter", "Passer à l'élément suivant" et "Passer à la page précédente"

Recommander fortement l'assistance pour: Rechercher

Podcast

Compatibilité requise: Lire, Mettre en pause, Arrêter et Aller à

Recommander l'assistance pour : "Passer à l'élément suivant" et "Passer à l'élément précédent"

Livre audio Compatibilité requise: Lire, Mettre en pause, Arrêter et Aller à
Radio Compatibilité requise : "Lire", "Mettre en pause" et "Arrêter"
Actualités Compatibilité requise : "Lire", "Mettre en pause", "Arrêter", "Passer à l'élément suivant" et "Passer à la page précédente"
Vidéo

Compatibilité requise : "Lecture", "Pause", "Arrêt", "Avance rapide", "Retour rapide" et "Avance rapide"

Assistance vivement recommandée : "Passer à l'élément suivant" et "Passer à l'élément précédent"

Vous devez prendre en charge autant d'actions listées ci-dessus que votre offre de produits le permet, mais réagir de manière appropriée pour toute autre action. Par exemple, si seuls les utilisateurs Premium ont la possibilité de revenir à l'élément précédent, vous pouvez générer 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 tester votre implémentation:

Rappel MediaSession Commande "Hey Google" à utiliser
onPlay()

"Lance la lecture."

"Reprends la lecture".

onPlayFromSearch()
onPlayFromUri()
Musique

"Mets de la musique ou des chansons 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 "Mets (podcast) sur (nom de l'application)."
onPause() "Mets sur pause."
onStop() "Arrête la lecture."
onSkipToNext() "Suivant (titre | épisode | titre)."
onSkipToPrevious() "Précédent (titre | épisode | titre)."
onSeekTo()

"Recommencer".

"Avance de ## secondes."

"Reviens en arrière de ## minutes."

N/A (Maintenez à jour votre MediaMetadata) "Qu'est-ce qui passe actuellement ?"

Erreurs

L'Assistant gère les erreurs d'une session multimédia lorsqu'elles se produisent et les signale aux utilisateurs. Assurez-vous que votre session multimédia met correctement à jour l'état de transport et le code d'erreur dans son PlaybackState, comme décrit dans la section Utiliser une session multimédia. L'Assistant reconnaît tous les codes d'erreur renvoyés par getErrorCode().

Cas souvent mal gérés

Voici quelques exemples de cas d'erreur que vous devez vous assurer de les gérer correctement:

  • L'utilisateur doit se connecter.
    • Définissez le code d'erreur PlaybackState sur ERROR_CODE_AUTHENTICATION_EXPIRED.
    • Définissez le message d'erreur PlaybackState.
    • Si nécessaire pour la lecture, définissez l'état de PlaybackState sur STATE_ERROR. Sinon, conservez le reste de PlaybackState tel quel.
  • L'utilisateur demande une action indisponible.
    • Définissez correctement le code d'erreur PlaybackState. Par exemple, définissez PlaybackState sur ERROR_CODE_NOT_SUPPORTED si l'action n'est pas compatible ou sur ERROR_CODE_PREMIUM_ACCOUNT_REQUIRED si elle est protégée par une connexion.
    • Définissez le message d'erreur PlaybackState.
    • Conservez le reste des PlaybackState en l'état.
  • L'utilisateur demande à voir du contenu non disponible dans l'application.
    • Définissez correctement le code d'erreur PlaybackState. Par exemple, utilisez ERROR_CODE_NOT_AVAILABLE_IN_REGION.
    • Définissez le message d'erreur PlaybackState.
    • Définissez l'état PlaybackSate sur STATE_ERROR pour interrompre la lecture. Sinon, vous pouvez conserver le reste de PlaybackState tel quel.
  • L'utilisateur demande un contenu pour lequel aucune correspondance exacte n'est disponible. Par exemple, un utilisateur de la version sans frais demandant du contenu réservé aux utilisateurs du niveau Premium.
    • Nous vous recommandons de ne pas renvoyer d'erreur et de privilégier plutôt un jeu semblable à celui-ci. L'Assistant se chargera de prononcer la réponse vocale la plus pertinente avant le début de la lecture.

Lecture avec un intent

L'Assistant peut lancer une application audio ou vidéo et commencer la lecture en envoyant un intent avec un lien profond.

L'intent et son lien profond peuvent provenir de différentes sources:

  • Lorsque l'Assistant démarre une application mobile, il peut utiliser la recherche Google pour récupérer le contenu balisé qui fournit une action de visionnage avec un lien.
  • Lorsque l'Assistant démarre une application TV, celle-ci doit inclure un fournisseur de recherche TV pour exposer les URI du contenu multimédia. L'Assistant envoie une requête au fournisseur de contenu, qui doit renvoyer un intent contenant un URI pour le lien profond et 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 spécifié aucune action, l'Assistant ajoute ACTION_VIEW à l'intent.

L'Assistant ajoute le EXTRA_START_PLAYBACK supplémentaire 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 quelque chose pendant que votre application lit le contenu d'une requête précédente. Cela signifie que votre application peut recevoir de nouveaux intents pour lancer la lecture lorsque son activité de lecture est déjà lancée et active.

Les activités qui acceptent les intents avec des liens profonds doivent remplacer onNewIntent() pour gérer les nouvelles requêtes.

Lors du démarrage de la lecture, l'Assistant peut ajouter des indicateurs supplémentaires à l'intent qu'il envoie à votre application. En particulier, il peut ajouter FLAG_ACTIVITY_CLEAR_TOP, FLAG_ACTIVITY_NEW_TASK ou les deux. Bien que votre code n'ait 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 alors que l'URI précédent est toujours en cours de lecture. Nous vous recommandons de tester la réaction de votre application dans ce cas. Vous pouvez utiliser l'outil de ligne de commande adb pour simuler la situation (la constante 0x14000000 est la valeur booléenne OR au niveau du 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 autorise les connexions depuis l'Assistant, celui-ci peut démarrer l'application en communiquant avec l'media session du service. Le service de navigateur multimédia ne doit jamais lancer d'activité. L'Assistant lancera votre activité en fonction du PendingIntent que vous définissez avec setSessionActivity().

Veillez à définir le jeton MediaSession.Token lorsque vous initialisez le service de navigateur multimédia. N'oubliez pas de définir les actions de lecture compatibles à tout moment, y compris lors de l'initialisation. L'Assistant s'attend à ce que votre application multimédia définisse les actions de lecture avant qu'il n'envoie la première commande de lecture.

Pour commencer à partir d'un service, l'Assistant implémente les API clientes du navigateur multimédia. Elle effectue des appels TransportControls qui déclenchent des rappels d'action de lecture sur la session multimédia de votre application.

Le schéma suivant montre l'ordre des appels générés par l'Assistant et 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'attend pas de réponse de votre application.

Démarrer la lecture avec une session multimédia

Lorsqu'un utilisateur émet une commande vocale, l'Assistant répond par une brève 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 lancer l'annonce.

Se connecter à MediaBrowserService

Pour utiliser un service afin de démarrer votre application, l'Assistant doit pouvoir se connecter au MediaBrowserService de l'application et récupérer son MediaSession.Token. Les requêtes de connexion sont traitées dans la méthode onGetRoot() du service. Il existe deux façons de gérer les requêtes:

  • Accepter toutes les demandes de connexion
  • Accepter uniquement les demandes de connexion de l'application Assistant

Accepter toutes les demandes de connexion

Vous devez renvoyer un BrowserRoot pour autoriser l'Assistant à envoyer des commandes à votre session multimédia. Le moyen le plus simple consiste à autoriser toutes les applications MediaBrowser à se connecter à MediaBrowserService. Vous devez renvoyer un BrowserRoot non nul. Voici le code applicable provenant 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 recherchant le nom et la signature du package. Votre application recevra le nom du package dans la méthode onGetRoot de MediaBrowserService. Vous devez renvoyer un BrowserRoot pour autoriser l'Assistant à envoyer des commandes à votre session multimédia. L'exemple Universal Music Player gère une liste des noms de packages et des signatures connus. Vous trouverez ci-dessous les noms de 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>