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() |
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:
- Utilizza il bundle extra e la stringa della query di ricerca restituiti dalla ricerca vocale. per filtrare i risultati.
- Crea una coda di riproduzione basata su questi risultati.
- Riproduci l'elemento multimediale più pertinente dai risultati.
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 constate, position, playback speed, and update time
. L'app deve chiamaresetPlaybackState()
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 implementareonSetRating()
. Se l'app non supporta la valutazione, il tipo di classificazione deve essere impostato suRATING_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
suERROR_CODE_AUTHENTICATION_EXPIRED
. - Imposta il messaggio di errore
PlaybackState
. - Se necessario per la riproduzione, imposta lo stato
PlaybackState
suSTATE_ERROR
, altrimenti mantengono il resto diPlaybackState
così com'è.
- Imposta il codice di errore di
- L'utente richiede un'azione non disponibile
- Imposta correttamente il codice di errore
PlaybackState
. Ad esempio, imposta il valore DaPlaybackState
aERROR_CODE_NOT_SUPPORTED
se l'azione non è supportata oERROR_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.
- Imposta correttamente il codice di errore
- L'utente richiede contenuti non disponibili nell'app
- Imposta correttamente il codice di errore
PlaybackState
. Ad esempio, utilizzaERROR_CODE_NOT_AVAILABLE_IN_REGION
- Imposta il messaggio di errore
PlaybackState
. - Imposta lo stato di
PlaybackSate
suSTATE_ERROR
per interrompere la riproduzione. altrimenti mantengono il resto diPlaybackState
così com'è.
- Imposta correttamente il codice di errore
- 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.
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
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>