Assistente Google e app multimediali

L'Assistente Google ti consente di usare i comandi vocali per controllare molti dispositivi, ad esempio Google Home, il tuo smartphone e altro. Ha una funzionalità integrata comprendere i comandi multimediali ("riproduci qualcosa di Beyoncé") e supporta controlli multimediali (come pausa, salto, avanti veloce, Mi piace).

L'assistente comunica con le app multimediali Android tramite contenuti multimediali sessione. Può utilizzare intent o servizi per avvia l'app e avvia la riproduzione. Per ottenere risultati ottimali, la tua app dovrebbe a implementare tutte le funzionalità descritte in questa pagina.

Utilizzare una sessione multimediale

Ogni app audio e video deve implementare un sessione multimediale per consentire all'assistente di funzionare i controlli di trasporto una volta avviata la riproduzione.

Tieni presente che, anche se l'assistente utilizza solo le azioni elencate in questa sezione, è implementare tutte le API di preparazione e riproduzione per garantire compatibilità con altre applicazioni. Per le azioni non supportate, i callback delle sessioni multimediali possono semplicemente restituire un errore ERROR_CODE_NOT_SUPPORTED

Attiva i controlli per contenuti multimediali e trasporto impostando questi flag nel Oggetto 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 sessione multimediale della tua app deve dichiarare le azioni supportate e implementare il parametro i callback delle sessioni multimediali corrispondenti. Dichiara le azioni supportate in setActions()

La Lettore di musica universale Android progetto di esempio è un buon esempio di come impostare una sessione multimediale.

Azioni di riproduzione

Per avviare la riproduzione da un servizio, una sessione multimediale deve avere queste azioni PLAY e i relativi callback:

Azione Richiamata
ACTION_PLAY onPlay()
ACTION_PLAY_FROM_SEARCH onPlayFromSearch()
ACTION_PLAY_FROM_URI(*) onPlayFromUri()

La sessione dovrebbe anche implementare queste azioni PREPARE e i relativi callback:

Azione Richiamata
ACTION_PREPARE onPrepare()
ACTION_PREPARE_FROM_SEARCH onPrepareFromSearch()
ACTION_PREPARE_FROM_URI(*) onPrepareFromUri()

(*) Le azioni basate su URI dell'Assistente Google funzionano solo per le aziende che forniscono URI a Google. Per scoprire di più sulla descrizione dei tuoi contenuti multimediali a Google consulta Azioni multimediali.

Implementando le API di preparazione, la latenza di riproduzione dopo un comando vocale possono essere ridotti. Le app multimediali che vogliono migliorare la latenza di riproduzione possono utilizzare per avviare la memorizzazione nella cache dei contenuti e la preparazione della riproduzione dei contenuti multimediali.

Analizzare le query di ricerca

Quando un utente cerca un elemento multimediale specifico, ad esempio "Fammi ascoltare jazz su [nome dell'app]" o "Ascolta [titolo del brano]", la onPrepareFromSearch() oppure onPlayFromSearch() riceve un parametro di query e un bundle di extra.

L'app deve analizzare la query di ricerca vocale e avviare la riproduzione seguendo questi passaggi: passaggi:

  1. Utilizza il bundle extra e la stringa della query di ricerca restituiti dalla ricerca vocale. per filtrare i risultati.
  2. Crea una coda di riproduzione basata su questi risultati.
  3. Riproduci l'elemento multimediale più pertinente dai risultati.
di Gemini Advanced.

La onPlayFromSearch() usa un parametro extras con informazioni più dettagliate provenienti dalla voce eseguire una ricerca. Questi extra ti aiutano a trovare i contenuti audio nella tua app da riprodurre. Se i risultati di ricerca non sono in grado di fornire questi dati, puoi implementare la logica di analizzare la query di ricerca non elaborata e riprodurre le tracce appropriate in base al query.

In Android Automotive OS e Android Auto sono supportati i seguenti extra:

Il seguente snippet di codice mostra come eseguire l'override di onPlayFromSearch(). in MediaSession.Callback implementazione per analizzare la query di ricerca vocale e avviare la riproduzione:

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
    }
}

Per un esempio più dettagliato su come implementare la ricerca vocale per riprodurre l'audio nella tua app, visita la pagina Universal Android Music Player campione.

Gestire le query vuote

Se onPrepare(), onPlay(), onPrepareFromSearch() o onPlayFromSearch() vengono chiamati senza una query di ricerca, la tua app multimediale deve riprodurre il video contenuti multimediali. Se al momento non sono presenti contenuti multimediali, l'app dovrebbe provare a riprodurre qualcosa, ad esempio come brano dalla playlist più recente o in una coda casuale. L'assistente utilizza queste API quando un utente chiede di "Fammi ascoltare musica su [nome della tua app]" senza ulteriori informazioni.

Quando un utente dice "Fammi ascoltare musica su [nome della tua app]", il sistema operativo Android Automotive oppure Android Auto tenta di avviare l'app e riprodurre l'audio chiamando il numero onPlayFromSearch() dell'app . Tuttavia, poiché l'utente non ha pronunciato il nome dell'elemento multimediale, la onPlayFromSearch() riceve un parametro di query vuoto. In questi casi, l'app dovrebbe rispondere riproducendo subito l'audio, ad esempio un brano playlist o una coda casuale.

Dichiara il supporto precedente per le azioni vocali

Nella maggior parte dei casi, la gestione delle azioni di riproduzione descritte sopra offre alla tua app tutte la funzionalità di riproduzione di cui ha bisogno. Tuttavia, alcuni sistemi richiedono all'app contengono un filtro per intent per la ricerca. Devi dichiarare il supporto per questo intent nei file manifest dell'app.

Includi questo codice nel file manifest per un'app per smartphone:

<activity>
    <intent-filter>
        <action android:name=
             "android.media.action.MEDIA_PLAY_FROM_SEARCH" />
        <category android:name=
             "android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

Controlli per il trasporto

Dopo aver attivato la sessione multimediale dell'app, l'assistente può emettere comandi vocali per controllare la riproduzione e aggiornare i metadati dei contenuti multimediali. Affinché questo comando funzioni, attivare le azioni seguenti e implementare il codice corrispondente di callback:

Azione Richiamata Descrizione
ACTION_SKIP_TO_NEXT onSkipToNext() Video successivo
ACTION_SKIP_TO_PREVIOUS onSkipToPrevious() Brano precedente
ACTION_PAUSE, ACTION_PLAY_PAUSE onPause() Metti in pausa
ACTION_STOP onStop() Interrompi
ACTION_PLAY onPlay() Riprendi
ACTION_SEEK_TO onSeekTo() Torna indietro di 30 secondi
ACTION_SET_RATING onSetRating(android.support.v4.media.RatingCompat) Mi piace/Non mi piace.
ACTION_SET_CAPTIONING_ENABLED onSetCaptioningEnabled(boolean) Attiva/disattiva i sottotitoli codificati.

Nota:

  • Affinché i comandi di ricerca funzionino, PlaybackState deve essere aggiornato con state, position, playback speed, and update time. L'app deve chiamare setPlaybackState() quando lo stato cambia.
  • L'app multimediale deve inoltre mantenere aggiornati i metadati della sessione multimediale. Questo supporta domande come "che canzone è in riproduzione?" L'app deve richiamare setMetadata() quando i campi applicabili (ad esempio titolo della traccia, artista e nome) cambiano.
  • MediaSession.setRatingType() deve essere impostato per indicare il tipo di classificazione supportato dall'app e l'app deve implementare onSetRating(). Se l'app non supporta la valutazione, il tipo di classificazione deve essere impostato su RATING_NONE.

Le azioni vocali supportate variano probabilmente in base al tipo di contenuti.

Tipo di contenuti Azioni richieste
Musica

Sono supportati: Play, Metti in pausa, Interrompi, Vai a successivo e Vai al precedente

Consigliamo vivamente di ricevere assistenza per: Seek To

Podcast

Sono supportati: Riproduci, Metti in pausa, Interrompi e Cerca

Consigliare l'assistenza per: Vai al successivo e Vai al precedente

Audiolibro Sono supportati: Riproduci, Metti in pausa, Interrompi e Cerca
Radio Supporto richiesto: riproduzione, pausa e interruzione
Notizie Sono supportati: Play, Metti in pausa, Interrompi, Vai a successivo e Vai al precedente
Video

Supporto richiesto: riproduzione, pausa, interruzione, ricerca, riavvolgimento e avanzamento veloce

Consiglio vivamente il supporto per: per passare al successivo e al precedente

Devi supportare un numero di azioni sopra elencate uguale al numero di prodotti offerti da te permettono, ma allo stesso tempo di rispondere con grazia a qualsiasi altra azione. Ad esempio, se gli utenti premium hanno la possibilità di tornare all'articolo precedente, puoi aumentare un errore se un utente del livello senza costi chiede all'assistente di tornare all'elemento precedente. Per ulteriori indicazioni, consulta la sezione sulla gestione degli errori.

Query vocali di esempio da provare

La seguente tabella illustra alcuni esempi di query da utilizzare durante testa l'implementazione:

Callback MediaSession Frase "Hey Google" da usare
onPlay()

"Riproduci."

"Riprendi".

onPlayFromSearch()
onPlayFromUri()
Musica

"Fammi ascoltare musica o brani su (nome dell'app)." Questa è una query vuota.

"Fammi ascoltare (brano | artista | album | genere | playlist) su (nome dell'app)."

Radio "Riproduci (frequency | stazione) su (app name)."
Audiolibro

"Leggi il mio audiolibro su (nome dell'app)."

"Leggi (audiolibro) su (nome dell'app)."

Podcast "Fammi ascoltare (podcast) su (nome dell'app)."
onPause() "Metti in pausa".
onStop() "Interrompi".
onSkipToNext() "Next (brano | episodio | traccia)."
onSkipToPrevious() "Precedente (brano | puntata | traccia)."
onSeekTo()

"Riavvia."

"Vai avanti di ## secondi."

"Torna indietro di ## minuti."

N/A (tieni MediaMetadata aggiornato) "Cosa c'è in riproduzione?"

Errori

L'assistente gestisce gli errori di una sessione multimediale quando si verificano e segnala per gli utenti. Assicurati che la sessione multimediale aggiorni lo stato di trasporto e codice di errore in PlaybackState correttamente, come descritto in Utilizzo di sessione multimediale. L'assistente riconosce tutti i codici di errore restituiti getErrorCode()

Casi comunemente gestiti in modo non corretto

Di seguito sono riportati alcuni esempi di casi di errore che dovresti assicurarti di gestire. correttamente:

  • L'utente deve eseguire l'accesso
    • Imposta il codice di errore di PlaybackState su ERROR_CODE_AUTHENTICATION_EXPIRED.
    • Imposta il messaggio di errore PlaybackState.
    • Se necessario per la riproduzione, imposta lo stato PlaybackState su STATE_ERROR, altrimenti mantengono il resto di PlaybackState così com'è.
  • L'utente richiede un'azione non disponibile
    • Imposta correttamente il codice di errore PlaybackState. Ad esempio, imposta il valore Da PlaybackState a ERROR_CODE_NOT_SUPPORTED se l'azione non è supportata o ERROR_CODE_PREMIUM_ACCOUNT_REQUIRED se l'azione è protetta da accesso all'account.
    • Imposta il messaggio di errore PlaybackState.
    • Conserva il resto di PlaybackState così come sono.
  • L'utente richiede contenuti non disponibili nell'app
    • Imposta correttamente il codice di errore PlaybackState. Ad esempio, utilizza ERROR_CODE_NOT_AVAILABLE_IN_REGION
    • Imposta il messaggio di errore PlaybackState.
    • Imposta lo stato di PlaybackSate su STATE_ERROR per interrompere la riproduzione. altrimenti mantengono il resto di PlaybackState così com'è.
  • L'utente richiede contenuti in cui non è disponibile una corrispondenza esatta. Ad esempio, un un utente di livello senza costi che richiede contenuti disponibili solo per gli utenti di livello premium.
    • Ti consigliamo di non restituire un errore e di dare la priorità trovare qualcosa di simile da suonare. Sarà l'assistente a occuparsi del parlato più spesso una risposta vocale pertinente prima dell'avvio della riproduzione.

Riproduzione con un intento

L'assistente può avviare un'app audio o video e avviare la riproduzione inviando un con un link diretto.

L'intento e il link diretto possono provenire da diverse fonti:

  • Quando l'assistente è avviando un'app mobile, può utilizzare la Ricerca Google per recuperare contenuti sottoposti a markup che fornisce un'azione di visualizzazione con un link.
  • Quando l'assistente avvia un'app TV, questa deve includere un'app Provider di ricerca TV per esporre gli URI dei contenuti multimediali. L'assistente invia una query a il fornitore di contenuti, che dovrebbe restituire un intent contenente un URI per il link diretto un'azione facoltativa. Se la query restituisce un'azione nell'intent, l'assistente invia l'azione e l'URI alla tua app. Se il fornitore non ha specificato un'azione, l'assistente aggiungerà ACTION_VIEW all'intent.

L'assistente aggiunge EXTRA_START_PLAYBACK extra con un valore di true all'intent che invia alla tua app. L'app dovrebbe avviare la riproduzione quando riceve un intent con EXTRA_START_PLAYBACK.

Gestione degli intent durante l'attività

Gli utenti possono chiedere all'assistente di riprodurre qualcosa mentre l'app è ancora in riproduzione contenuti di una richiesta precedente. Ciò significa che la tua app può ricevere nuovi intent avvia la riproduzione mentre la relativa attività di riproduzione è già avviata e attiva.

Le attività che supportano gli intent con i link diretti dovrebbero sostituire onNewIntent() per gestire le nuove richieste.

Quando avvii la riproduzione, l'assistente potrebbe aggiungere flag all'intent che invia alla tua app. In particolare, può aggiungere FLAG_ACTIVITY_CLEAR_TOP o FLAG_ACTIVITY_NEW_TASK o entrambi. Anche se il codice non ha bisogno di gestire questi flag, il sistema Android risponde. Ciò potrebbe influire sul comportamento della tua app quando arriva una seconda richiesta di riproduzione con un nuovo URI mentre l'URI precedente è ancora in riproduzione. È consigliabile verificare come risponde l'app in questo caso. Puoi usare il comando adb lo strumento a linee per simulare la situazione (la costante 0x14000000 è l'OPPURE booleano a livello di bit dei due flag):

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

Riproduzione da un servizio

Se la tua app include media browser service che consenta connessioni dall'assistente, l'assistente può avviare l'app comunicando con il servizio media session. Il servizio di browser multimediale non deve mai avviare un'attività. L'assistente avvierà la tua Attività in base ai PendingIntent che hai definito con setSessionActivity().

Assicurati di impostare MediaSession.Token quando inizializzare il servizio di browser multimediale. Ricordati di impostare le azioni di riproduzione supportate in qualsiasi momento, anche durante l'inizializzazione. L'assistente si aspetta che i tuoi contenuti multimediali per impostare le azioni di riproduzione prima che l'assistente invii la prima riproduzione .

Per iniziare da un servizio, l'assistente implementa le API client del browser multimediale. Esegue chiamate TransportControls che attivano i callback delle azioni PLAY sul tuo sessione multimediale dell'app.

Il seguente diagramma mostra l'ordine delle chiamate generate dall'assistente e i callback delle sessioni multimediali corrispondenti. I callback di preparazione vengono inviati solo se la tua app le supporta). Tutte le chiamate sono asincrone. L'assistente non attenderemo una risposta dall'app.

Avviare la riproduzione con una sessione multimediale

Quando un utente invia un comando vocale per avviare la riproduzione, l'assistente risponde con un breve annuncio. Non appena l'annuncio è terminato, l'assistente emette un'azione RIPRODUCI. Non attende lo stato di riproduzione specifico.

Se la tua app supporta le azioni ACTION_PREPARE_*, l'assistente chiama l'azione PREPARE prima di iniziare l'annuncio.

Connessione a MediaBrowserService

Per poter usare un servizio per avviare l'app, l'assistente deve poter connettersi al servizio MediaBrowserService dell'app e recupera il relativo MediaSession.Token. Le richieste di connessione vengono gestite onGetRoot() . Esistono due modi per gestire le richieste:

  • Accetta tutte le richieste di connessione
  • Accetta le richieste di connessione solo dall'app dell'assistente
di Gemini Advanced.

Accetta tutte le richieste di connessione

Devi restituire un valore BrowserRoot per consentire all'assistente di inviare comandi alla tua sessione multimediale. Il modo più semplice è consentire a tutte le app MediaBrowser di connettersi a MediaBrowserService. Devi restituire un valore BrowserRoot diverso da null. Ecco il codice applicabile dallo Universal Music Player:

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...
}

Accetta il pacchetto e la firma dell'app dell'assistente

Puoi consentire esplicitamente all'assistente di connettersi al tuo servizio di browser multimediale verificando il nome e la firma del pacchetto. L'app riceverà il nome del pacchetto nel metodo onGetRoot di MediaBrowserService. Devi restituire un valore BrowserRoot per consentire all'assistente di inviare comandi alla tua sessione multimediale. La Lettore musicale universale gestisce un elenco di firme e nomi di pacchetti noti. Di seguito sono riportati i nomi e le firme dei pacchetti utilizzati dall'Assistente 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>