Android Auto e Android Automotive OS ti aiutano a portare i contenuti della tua app multimediale agli utenti nella loro auto.
Esistono due modi per creare app multimediali per le auto:
Questa guida spiega come utilizzare un
MediaBrowserService
e unMediaSession
per creare un'app a cui Android Auto e Android Automotive OS possono connettersi per eseguire il rendering delle visualizzazioni di navigazione e riproduzione dei contenuti multimediali ottimizzate per l'uso in auto.Le app multimediali possono essere create anche utilizzando i modelli della libreria di app per auto per formattazione personalizzabile, funzionalità di navigazione e azioni personalizzate ampliate. Per informazioni dettagliate sull'implementazione, consulta Creare un'app multimediale basata su modelli. Le app multimediali basate su modelli sono attualmente supportate solo su Android Auto.
Questa guida descrive i componenti richiesti di un MediaBrowserService
e di un
MediaSession
necessari alla tua app per funzionare su Android Auto o Android
Automotive OS. Dopo aver completato l'infrastruttura multimediale di base, puoi aggiungere il supporto di Android Auto e aggiungere il supporto di Android Automotive OS alla tua app multimediale.
Questa guida presuppone che tu abbia già un'app multimediale che riproduce audio su uno smartphone e che la tua app multimediale sia conforme all'architettura delle app multimediali di Android.
Prima di iniziare
- Consulta la documentazione dell'API Android Media.
- Consulta l'articolo Creare app multimediali per indicazioni sulla progettazione.
- Esamina i termini e i concetti chiave elencati in questa sezione.
Termini e concetti chiave
- Servizio di browser multimediale
- Un servizio Android implementato dalla tua app multimediale conforme all'API
MediaBrowserServiceCompat
. La tua app utilizza questo servizio per esporre i suoi contenuti. - Browser dei contenuti multimediali
- Un'API utilizzata dalle app multimediali per scoprire i servizi del browser multimediale e visualizzare i relativi contenuti. Android Auto e Android Automotive OS utilizzano un browser multimediale per trovare il servizio di browser multimediale della tua app.
- Elemento multimediale
Il browser dei contenuti multimediali organizza i contenuti in una struttura ad albero di oggetti
MediaItem
. Un elemento multimediale può avere uno o entrambi i seguenti flag:FLAG_PLAYABLE
: indica che l'elemento è una foglia dell'albero dei contenuti. L'elemento rappresenta un singolo stream audio, ad esempio un brano di un album, un capitolo di un audiolibro o una puntata di un podcast.FLAG_BROWSABLE
: indica che l'elemento è un nodo dell'albero dei contenuti e che ha elementi secondari. Ad esempio, l'elemento rappresenta un album e i relativi elementi secondari sono i brani dell'album.
Un elemento multimediale sia sfogliabile che riproducibile si comporta come una playlist. Puoi selezionare l'elemento stesso per riprodurre tutti i relativi elementi secondari oppure puoi sfogliarli.
- Ottimizzato per i veicoli
Un'attività per un'app Android Automotive OS che rispetta le linee guida per la progettazione di Android Automotive OS. L'interfaccia di queste attività non è disegnata da Android Automotive OS, quindi devi assicurarti che la tua app rispetti le linee guida di progettazione. In genere, ciò include target dei tocchi e dimensioni dei caratteri più grandi, supporto delle modalità giorno e notte e rapporti di contrasto più elevati.
Le interfacce utente ottimizzate per i veicoli possono essere visualizzate solo quando le limitazioni dell'esperienza utente in auto (CUXR) non sono attive, perché queste interfacce possono richiedere maggiore attenzione o interazione da parte dell'utente. Le CUXR non sono attive quando l'auto è ferma o parcheggiata, ma sono sempre attive quando l'auto è in movimento.
Non è necessario progettare attività per Android Auto, perché Android Auto disegna la propria interfaccia ottimizzata per il veicolo utilizzando le informazioni del servizio di navigazione multimediale.
Configurare i file manifest dell'app
Prima di poter creare il servizio di esplorazione dei contenuti multimediali, devi configurare i file manifest della tua app.
Dichiarare il servizio di esplorazione dei contenuti multimediali
Sia Android Auto che Android Automotive OS si connettono alla tua app tramite il servizio di esplorazione dei contenuti multimediali per sfogliare gli elementi multimediali. Dichiara il tuo servizio di esplorazione dei contenuti multimediali nel file manifest per consentire ad Android Auto e Android Automotive OS di rilevare il servizio e connettersi alla tua app.
Il seguente snippet di codice mostra come dichiarare il servizio di esplorazione dei contenuti multimediali nel manifest. Includi questo codice nel file manifest per il modulo Android Automotive OS e nel file manifest per l'app per smartphone.
<application>
...
<service android:name=".MyMediaBrowserService"
android:exported="true">
<intent-filter>
<action android:name="android.media.browse.MediaBrowserService"/>
</intent-filter>
</service>
...
</application>
Specifica le icone delle app
Devi specificare le icone dell'app che Android Auto e Android Automotive OS possono utilizzare per rappresentare la tua app nell'interfaccia utente del sistema. Sono necessari due tipi di icone:
- Icona in Avvio applicazioni
- Icona di attribuzione
Icona in Avvio applicazioni
L'icona di Avvio app rappresenta la tua app nell'interfaccia utente del sistema, ad esempio in Avvio app e nella barra delle icone. Puoi specificare che vuoi utilizzare l'icona della tua app mobile per rappresentare la tua app multimediale per auto utilizzando la seguente dichiarazione del manifest:
<application
...
android:icon="@mipmap/ic_launcher"
...
/>
Per utilizzare un'icona diversa da quella della tua app mobile, imposta la proprietà android:icon
sull'elemento <service>
del servizio di esplorazione dei contenuti multimediali nel file manifest:
<application>
...
<service
...
android:icon="@mipmap/auto_launcher"
...
/>
</application>
Icona di attribuzione

Figura 1. Icona dell'attribuzione sulla scheda dei contenuti multimediali.
L'icona di attribuzione viene utilizzata nei punti in cui i contenuti multimediali hanno la precedenza, ad esempio nelle schede multimediali. Valuta la possibilità di riutilizzare l'icona piccola utilizzata per le notifiche. Questa icona deve essere monocromatica. Puoi specificare un'icona utilizzata per rappresentare la tua app utilizzando la seguente dichiarazione del manifest:
<application>
...
<meta-data
android:name="androidx.car.app.TintableAttributionIcon"
android:resource="@drawable/ic_status_icon" />
...
</application>
Crea il tuo servizio di esplorazione dei contenuti multimediali
Per creare un servizio di esplorazione dei contenuti multimediali, estendi la classe MediaBrowserServiceCompat
. Android Auto e Android Automotive OS possono quindi utilizzare il tuo servizio
per svolgere le seguenti attività:
- Sfoglia la gerarchia dei contenuti dell'app per presentare un menu all'utente.
- Ottieni il token per l'oggetto
MediaSessionCompat
della tua app per controllare la riproduzione audio.
Puoi anche utilizzare il servizio di esplorazione dei contenuti multimediali per consentire ad altri client di accedere ai contenuti multimediali dalla tua app. Questi client multimediali potrebbero essere altre app sullo smartphone di un utente o altri client remoti.
Flusso di lavoro del servizio di esplorazione dei contenuti multimediali
Questa sezione descrive come Android Automotive OS e Android Auto interagiscono con il servizio di navigazione multimediale durante un tipico flusso di lavoro dell'utente.
- L'utente avvia la tua app su Android Automotive OS o Android Auto.
- Android Automotive OS o Android Auto contattano il servizio di esplorazione dei contenuti multimediali della tua app utilizzando il metodo
onCreate()
. Nell'implementazione del metodoonCreate()
, devi creare e registrare un oggettoMediaSessionCompat
e il relativo oggetto callback. - Android Automotive OS o Android Auto chiamano il metodo
onGetRoot()
del tuo servizio per ottenere l'elemento multimediale radice nella gerarchia dei contenuti. L'elemento multimediale principale non viene visualizzato, ma viene utilizzato per recuperare altri contenuti dalla tua app. - Android Automotive OS o Android Auto chiamano il metodo
onLoadChildren()
del servizio per ottenere gli elementi multimediali secondari dell'elemento multimediale principale. Android Automotive OS e Android Auto mostrano questi elementi multimediali come livello superiore degli elementi di contenuti. Consulta la sezione Strutturare il menu principale in questa pagina per ulteriori informazioni su cosa si aspetta il sistema a questo livello. - Se l'utente seleziona un elemento multimediale sfogliabile, viene chiamato di nuovo il metodo
onLoadChildren()
del tuo servizio per recuperare gli elementi secondari della voce di menu selezionata. - Se l'utente seleziona un elemento multimediale riproducibile, Android Automotive OS o Android Auto chiama il metodo di callback della sessione multimediale appropriato per eseguire l'azione.
- Se supportata dalla tua app, l'utente può anche cercare i tuoi contenuti. In questo
caso, Android Automotive OS o Android Auto chiamano il metodo
onSearch()
del tuo servizio.
Creare la gerarchia dei contenuti
Android Auto e Android Automotive OS chiamano il servizio di esplorazione dei contenuti multimediali della tua app per
scoprire quali contenuti sono disponibili. Per supportare questa funzionalità, devi implementare due metodi nel servizio di esplorazione dei contenuti multimediali: onGetRoot()
e onLoadChildren()
.
Implementare onGetRoot
Il metodo onGetRoot()
del servizio restituisce informazioni sul nodo radice della gerarchia dei contenuti.
Android Auto e Android Automotive OS utilizzano questo nodo radice per richiedere il resto dei contenuti utilizzando il metodo onLoadChildren()
.
Il seguente snippet di codice mostra una semplice implementazione del metodo
onGetRoot()
:
Kotlin
override fun onGetRoot( clientPackageName: String, clientUid: Int, rootHints: Bundle? ): BrowserRoot? = // Verify that the specified package is allowed to access your // content. You'll need to write your own logic to do this. if (!isValid(clientPackageName, clientUid)) { // If the request comes from an untrusted package, return null. // No further calls will be made to other media browsing methods. null } else MediaBrowserServiceCompat.BrowserRoot(MY_MEDIA_ROOT_ID, null)
Java
@Override public BrowserRoot onGetRoot(String clientPackageName, int clientUid, Bundle rootHints) { // Verify that the specified package is allowed to access your // content. You'll need to write your own logic to do this. if (!isValid(clientPackageName, clientUid)) { // If the request comes from an untrusted package, return null. // No further calls will be made to other media browsing methods. return null; } return new MediaBrowserServiceCompat.BrowserRoot(MY_MEDIA_ROOT_ID, null); }
Per un esempio più dettagliato di questo metodo, consulta il metodo onGetRoot()
nell'app di esempio Universal Android Music Player su GitHub.
Aggiungi la convalida del pacchetto per onGetRoot()
Quando viene effettuata una chiamata al metodo onGetRoot()
del tuo servizio, il pacchetto chiamante trasmette informazioni identificative al tuo servizio. Il tuo
servizio può utilizzare queste informazioni per decidere se il pacchetto può accedere ai tuoi
contenuti. Ad esempio, puoi limitare l'accesso ai contenuti della tua app a un elenco di
pacchetti approvati confrontando clientPackageName
con la tua lista consentita e
verificando il certificato utilizzato per firmare l'APK del pacchetto. Se il pacchetto non può essere verificato, torna a null
per negare l'accesso ai tuoi contenuti.
Per fornire alle app di sistema, come Android Auto e Android Automotive OS, l'accesso ai tuoi contenuti, il tuo servizio deve sempre restituire un valore BrowserRoot
non nullo quando queste app di sistema chiamano il metodo onGetRoot()
. La firma dell'app di sistema Android Automotive OS può variare a seconda
della marca e del modello dell'auto, pertanto devi consentire le connessioni da tutte
le app di sistema per supportare Android Automotive OS in modo affidabile.
Il seguente snippet di codice mostra come il tuo servizio può verificare che il pacchetto chiamante sia un'app di sistema:
fun isKnownCaller(
callingPackage: String,
callingUid: Int
): Boolean {
...
val isCallerKnown = when {
// If the system is making the call, allow it.
callingUid == Process.SYSTEM_UID -> true
// If the app was signed by the same certificate as the platform
// itself, also allow it.
callerSignature == platformSignature -> true
// ... more cases
}
return isCallerKnown
}
Questo snippet di codice è un estratto della classe PackageValidator
nell'app di esempio Universal Android Music Player su GitHub. Consulta questa classe
per un esempio più dettagliato di come implementare la convalida dei pacchetti per il metodo
onGetRoot()
del tuo servizio.
Oltre a consentire le app di sistema, devi consentire all'Assistente Google
di connettersi al tuo MediaBrowserService
. Tieni presente che l'Assistente Google ha
nomi di pacchetti separati
per lo smartphone, che include Android Auto, e per Android Automotive OS.
Implementare onLoadChildren()
Dopo aver ricevuto l'oggetto nodo radice, Android Auto e Android Automotive OS
creano un menu di primo livello chiamando onLoadChildren()
sull'oggetto nodo radice per ottenere i relativi elementi secondari. Le app client creano i sottomenu
chiamando lo stesso metodo utilizzando gli oggetti nodo secondario.
Ogni nodo della gerarchia dei contenuti è rappresentato da un oggetto MediaBrowserCompat.MediaItem
. Ciascuno di questi elementi multimediali è identificato da una stringa ID univoca. Le app
client trattano queste stringhe ID come token opachi. Quando un'app client vuole sfogliare
un sottomenu o riprodurre un elemento multimediale, passa il token. La tua app è responsabile
dell'associazione del token all'elemento multimediale appropriato.
Il seguente snippet di codice mostra una semplice implementazione del metodo onLoadChildren()
:
Kotlin
override fun onLoadChildren( parentMediaId: String, result: Result<List<MediaBrowserCompat.MediaItem>> ) { // Assume for example that the music catalog is already loaded/cached. val mediaItems: MutableList<MediaBrowserCompat.MediaItem> = mutableListOf() // Check whether this is the root menu: if (MY_MEDIA_ROOT_ID == parentMediaId) { // Build the MediaItem objects for the top level // and put them in the mediaItems list. } else { // Examine the passed parentMediaId to see which submenu we're at // and put the children of that menu in the mediaItems list. } result.sendResult(mediaItems) }
Java
@Override public void onLoadChildren(final String parentMediaId, final Result<List<MediaBrowserCompat.MediaItem>> result) { // Assume for example that the music catalog is already loaded/cached. List<MediaBrowserCompat.MediaItem> mediaItems = new ArrayList<>(); // Check whether this is the root menu: if (MY_MEDIA_ROOT_ID.equals(parentMediaId)) { // Build the MediaItem objects for the top level // and put them in the mediaItems list. } else { // Examine the passed parentMediaId to see which submenu we're at // and put the children of that menu in the mediaItems list. } result.sendResult(mediaItems); }
Per un esempio completo di questo metodo, consulta il metodo
onLoadChildren()
nell'app di esempio Universal Android Music Player su GitHub.
Strutturare il menu principale

Figura 2. Contenuti di primo livello visualizzati come schede di navigazione.
Android Auto e Android Automotive OS hanno vincoli specifici sulla
struttura del menu principale. Questi vengono comunicati a MediaBrowserService
tramite suggerimenti radice, che possono essere letti tramite l'argomento Bundle
passato a
onGetRoot()
.
Se segui questi suggerimenti, il sistema visualizzerà i contenuti principali in modo ottimale
come schede di navigazione. Se non segui questi suggerimenti, alcuni contenuti principali potrebbero
essere eliminati o resi meno rilevabili dal sistema. Vengono inviati due suggerimenti:
- Un limite al numero di figli della radice: nella maggior parte dei casi, questo numero è quattro. Ciò significa che non è possibile visualizzare più di quattro schede.
- Flag supportati per i figli della radice:
puoi aspettarti che questo valore sia
MediaItem#FLAG_BROWSABLE
. Ciò significa che solo gli elementi sfogliabili, non riproducibili, possono essere visualizzati come schede.
Utilizza il seguente codice per leggere i suggerimenti per la radice pertinenti:
Kotlin
import androidx.media.utils.MediaConstants // Later, in your MediaBrowserServiceCompat. override fun onGetRoot( clientPackageName: String, clientUid: Int, rootHints: Bundle ): BrowserRoot { val maximumRootChildLimit = rootHints.getInt( MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_LIMIT, /* defaultValue= */ 4) val supportedRootChildFlags = rootHints.getInt( MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS, /* defaultValue= */ MediaItem.FLAG_BROWSABLE) // Rest of method... }
Java
import androidx.media.utils.MediaConstants; // Later, in your MediaBrowserServiceCompat. @Override public BrowserRoot onGetRoot( String clientPackageName, int clientUid, Bundle rootHints) { int maximumRootChildLimit = rootHints.getInt( MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_LIMIT, /* defaultValue= */ 4); int supportedRootChildFlags = rootHints.getInt( MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS, /* defaultValue= */ MediaItem.FLAG_BROWSABLE); // Rest of method... }
Puoi scegliere di ramificare la logica per la struttura della gerarchia dei contenuti
in base ai valori di questi suggerimenti, in particolare se la gerarchia varia tra
le integrazioni MediaBrowser
al di fuori di Android Auto e Android Automotive OS.
Ad esempio, se di solito mostri un elemento riproducibile principale, potresti volerlo nidificare
in un elemento sfogliabile principale a causa del valore del suggerimento
dei flag supportati.
Oltre ai suggerimenti principali, ci sono un paio di linee guida aggiuntive da seguire per garantire che le schede vengano visualizzate in modo ottimale:
- Fornisci icone monocromatiche, preferibilmente bianche, per ogni elemento della scheda.
- Fornisci etichette brevi ma significative per ogni elemento della scheda. Mantenere le etichette brevi riduce la possibilità che le stringhe vengano troncate.
Visualizzare la copertina dei contenuti multimediali
Le copertine degli elementi multimediali devono essere trasmesse come URI locale utilizzando
ContentResolver.SCHEME_CONTENT
o ContentResolver.SCHEME_ANDROID_RESOURCE
.
Questo URI locale deve essere risolto in una bitmap o in un elemento disegnabile vettoriale nelle risorse dell'applicazione. Per gli oggetti MediaDescriptionCompat
che rappresentano gli elementi nella
gerarchia dei contenuti, trasmetti l'URI tramite setIconUri()
.
Per gli oggetti MediaMetadataCompat
che rappresentano l'elemento in riproduzione, passa l'URI tramite putString()
, utilizzando uno dei seguenti tasti:
MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON_URI
MediaMetadataCompat.METADATA_KEY_ART_URI
MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI
I passaggi riportati di seguito descrivono come scaricare la copertina da un URI web ed esporla tramite un URI locale. Per un esempio più completo, consulta l'implementazione di openFile()
e i metodi circostanti nell'app di esempio Universal Android Music Player.
Crea un URI
content://
corrispondente all'URI web. Il servizio di esplorazione dei contenuti multimediali e la sessione multimediale passano questo URI dei contenuti ad Android Auto e Android Automotive OS.Kotlin
fun Uri.asAlbumArtContentURI(): Uri { return Uri.Builder() .scheme(ContentResolver.SCHEME_CONTENT) .authority(CONTENT_PROVIDER_AUTHORITY) .appendPath(this.getPath()) // Make sure you trust the URI .build() }
Java
public static Uri asAlbumArtContentURI(Uri webUri) { return new Uri.Builder() .scheme(ContentResolver.SCHEME_CONTENT) .authority(CONTENT_PROVIDER_AUTHORITY) .appendPath(webUri.getPath()) // Make sure you trust the URI! .build(); }
Nell'implementazione di
ContentProvider.openFile()
, controlla se esiste un file per l'URI corrispondente. In caso contrario, scarica e memorizza nella cache il file immagine. Lo snippet di codice seguente utilizza Glide.Kotlin
override fun openFile(uri: Uri, mode: String): ParcelFileDescriptor? { val context = this.context ?: return null val file = File(context.cacheDir, uri.path) if (!file.exists()) { val remoteUri = Uri.Builder() .scheme("https") .authority("my-image-site") .appendPath(uri.path) .build() val cacheFile = Glide.with(context) .asFile() .load(remoteUri) .submit() .get(DOWNLOAD_TIMEOUT_SECONDS, TimeUnit.SECONDS) cacheFile.renameTo(file) file = cacheFile } return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY) }
Java
@Nullable @Override public ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode) throws FileNotFoundException { Context context = this.getContext(); File file = new File(context.getCacheDir(), uri.getPath()); if (!file.exists()) { Uri remoteUri = new Uri.Builder() .scheme("https") .authority("my-image-site") .appendPath(uri.getPath()) .build(); File cacheFile = Glide.with(context) .asFile() .load(remoteUri) .submit() .get(DOWNLOAD_TIMEOUT_SECONDS, TimeUnit.SECONDS); cacheFile.renameTo(file); file = cacheFile; } return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY); }
Per maggiori dettagli sui fornitori di contenuti, consulta la sezione Creazione di un fornitore di contenuti.
Applicare stili ai contenuti
Dopo aver creato la gerarchia dei contenuti utilizzando elementi sfogliabili o riproducibili, puoi applicare stili di contenuti che determinano la modalità di visualizzazione di questi elementi nell'auto.
Puoi utilizzare i seguenti stili di contenuti:
- Voci dell'elenco
-
Questo stile di contenuti dà la priorità a titoli e metadati rispetto alle immagini.
- Elementi della griglia
-
Questo stile di contenuti dà la priorità alle immagini rispetto ai titoli e ai metadati.
Impostare gli stili dei contenuti predefiniti
Puoi impostare valori predefiniti globali per la modalità di visualizzazione degli elementi multimediali includendo
determinate costanti nel bundle di extra BrowserRoot
del metodo
onGetRoot()
del servizio. Android Auto e Android Automotive OS leggono questo bundle e cercano
queste costanti per determinare lo stile appropriato.
I seguenti componenti aggiuntivi possono essere utilizzati come chiavi nel bundle:
DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE
: indica un suggerimento di presentazione per tutti gli elementi sfogliabili all'interno dell'albero di navigazione.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE
: indica un suggerimento di presentazione per tutti gli elementi riproducibili all'interno dell'albero di navigazione.
Le chiavi possono essere mappate ai seguenti valori costanti interi per influenzare la presentazione di questi elementi:
DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM
: gli elementi corrispondenti vengono presentati come elementi di elenco.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM
: gli elementi corrispondenti vengono presentati come elementi della griglia.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_CATEGORY_LIST_ITEM
: gli elementi corrispondenti vengono presentati come elementi di elenco "categoria". Questi sono uguali ai normali elementi di elenco, tranne per il fatto che i margini vengono applicati intorno alle icone degli elementi, poiché le icone hanno un aspetto migliore quando sono piccole. Le icone devono essere risorse drawable vettoriali colorabili. Questo suggerimento dovrebbe essere fornito solo per gli elementi sfogliabili.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_CATEGORY_GRID_ITEM
: gli elementi corrispondenti vengono presentati come elementi della griglia "categoria". Questi sono uguali ai normali elementi della griglia, tranne per il fatto che i margini vengono applicati intorno alle icone degli elementi, poiché le icone hanno un aspetto migliore quando sono piccole. Le icone devono essere risorse drawable vettoriali colorabili. Questo suggerimento dovrebbe essere fornito solo per gli elementi sfogliabili.
Il seguente snippet di codice mostra come impostare lo stile dei contenuti predefinito per gli elementi sfogliabili in griglie e gli elementi riproducibili in elenchi:
Kotlin
import androidx.media.utils.MediaConstants @Nullable override fun onGetRoot( @NonNull clientPackageName: String, clientUid: Int, @Nullable rootHints: Bundle ): BrowserRoot { val extras = Bundle() extras.putInt( MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE, MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM) extras.putInt( MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE, MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM) return BrowserRoot(ROOT_ID, extras) }
Java
import androidx.media.utils.MediaConstants; @Nullable @Override public BrowserRoot onGetRoot( @NonNull String clientPackageName, int clientUid, @Nullable Bundle rootHints) { Bundle extras = new Bundle(); extras.putInt( MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE, MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM); extras.putInt( MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE, MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM); return new BrowserRoot(ROOT_ID, extras); }
Impostare gli stili dei contenuti per elemento
L'API Content Style ti consente di ignorare lo stile dei contenuti predefinito per i figli di qualsiasi elemento multimediale sfogliabile, nonché per qualsiasi elemento multimediale stesso.
Per ignorare il valore predefinito per gli elementi secondari di un elemento multimediale sfogliabile, crea un
bundle di contenuti extra in MediaDescription
dell'elemento multimediale e aggiungi gli stessi
suggerimenti menzionati in precedenza. DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE
si applica agli elementi secondari riproducibili dell'elemento, mentre
DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE
si applica agli elementi secondari
navigabili dell'elemento.
Per ignorare il valore predefinito per un determinato elemento multimediale stesso, non per i relativi
elementi secondari, crea un bundle di contenuti aggiuntivi in MediaDescription
dell'elemento multimediale
e aggiungi un suggerimento con la chiave
DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_SINGLE_ITEM
.
Utilizza gli stessi valori descritti in precedenza per specificare la presentazione dell'elemento.
Il seguente snippet di codice mostra come creare un MediaItem
sfogliabile che
override lo stile dei contenuti predefinito per se stesso e per i relativi elementi secondari. Si presenta come una voce di elenco di categoria, i suoi elementi secondari sfogliabili come voci di elenco e i suoi elementi secondari riproducibili come elementi della griglia:
Kotlin
import androidx.media.utils.MediaConstants private fun createBrowsableMediaItem( mediaId: String, folderName: String, iconUri: Uri ): MediaBrowser.MediaItem { val mediaDescriptionBuilder = MediaDescription.Builder() mediaDescriptionBuilder.setMediaId(mediaId) mediaDescriptionBuilder.setTitle(folderName) mediaDescriptionBuilder.setIconUri(iconUri) val extras = Bundle() extras.putInt( MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_SINGLE_ITEM, MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_CATEGORY_LIST_ITEM) extras.putInt( MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE, MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM) extras.putInt( MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE, MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM) mediaDescriptionBuilder.setExtras(extras) return MediaBrowser.MediaItem( mediaDescriptionBuilder.build(), MediaBrowser.MediaItem.FLAG_BROWSABLE) }
Java
import androidx.media.utils.MediaConstants; private MediaBrowser.MediaItem createBrowsableMediaItem( String mediaId, String folderName, Uri iconUri) { MediaDescription.Builder mediaDescriptionBuilder = new MediaDescription.Builder(); mediaDescriptionBuilder.setMediaId(mediaId); mediaDescriptionBuilder.setTitle(folderName); mediaDescriptionBuilder.setIconUri(iconUri); Bundle extras = new Bundle(); extras.putInt( MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_SINGLE_ITEM, MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_CATEGORY_LIST_ITEM); extras.putInt( MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE, MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM); extras.putInt( MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE, MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM); mediaDescriptionBuilder.setExtras(extras); return new MediaBrowser.MediaItem( mediaDescriptionBuilder.build(), MediaBrowser.MediaItem.FLAG_BROWSABLE); }
Raggruppare gli elementi utilizzando i suggerimenti per i titoli
Per raggruppare gli elementi multimediali correlati, utilizza un suggerimento per elemento. Ogni elemento multimediale
di un gruppo deve dichiarare un bundle di componenti aggiuntivi nel relativo MediaDescription
che
include una mappatura con la chiave
DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE
e un valore stringa identico. Localizza questa stringa, che viene utilizzata come
titolo del gruppo.
Il seguente snippet di codice mostra come creare un MediaItem
con un'intestazione
di sottogruppo "Songs"
:
Kotlin
import androidx.media.utils.MediaConstants private fun createMediaItem( mediaId: String, folderName: String, iconUri: Uri ): MediaBrowser.MediaItem { val mediaDescriptionBuilder = MediaDescription.Builder() mediaDescriptionBuilder.setMediaId(mediaId) mediaDescriptionBuilder.setTitle(folderName) mediaDescriptionBuilder.setIconUri(iconUri) val extras = Bundle() extras.putString( MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs") mediaDescriptionBuilder.setExtras(extras) return MediaBrowser.MediaItem( mediaDescriptionBuilder.build(), /* playable or browsable flag*/) }
Java
import androidx.media.utils.MediaConstants; private MediaBrowser.MediaItem createMediaItem(String mediaId, String folderName, Uri iconUri) { MediaDescription.Builder mediaDescriptionBuilder = new MediaDescription.Builder(); mediaDescriptionBuilder.setMediaId(mediaId); mediaDescriptionBuilder.setTitle(folderName); mediaDescriptionBuilder.setIconUri(iconUri); Bundle extras = new Bundle(); extras.putString( MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs"); mediaDescriptionBuilder.setExtras(extras); return new MediaBrowser.MediaItem( mediaDescriptionBuilder.build(), /* playable or browsable flag*/); }
La tua app deve superare tutti gli elementi multimediali che vuoi raggruppare come blocco contiguo. Ad esempio, supponi di voler visualizzare due gruppi di elementi multimediali, "Canzoni" e "Album", in quest'ordine e che la tua app trasmetta cinque elementi multimediali nel seguente ordine:
- Elemento multimediale A con
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
- Elemento multimediale B con
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums")
- Elemento multimediale C con
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
- Elemento multimediale D con
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
- Elemento multimediale E con
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums")
Poiché gli elementi multimediali per i gruppi "Brani" e "Album" non vengono mantenuti insieme in blocchi contigui, Android Auto e Android Automotive OS interpretano questa situazione come i quattro gruppi seguenti:
- Gruppo 1 denominato "Canzoni" contenente l'elemento multimediale A
- Gruppo 2 denominato "Album" contenente l'elemento multimediale B
- Gruppo 3 denominato "Canzoni" contenente gli elementi multimediali C e D
- Gruppo 4 denominato "Album" contenente l'elemento multimediale E
Per visualizzare questi elementi in due gruppi, l'app deve passare gli elementi multimediali nel seguente ordine:
- Elemento multimediale A con
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
- Elemento multimediale C con
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
- Elemento multimediale D con
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
- Elemento multimediale B con
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums")
- Elemento multimediale E con
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums")
Visualizzare indicatori di metadati aggiuntivi
Puoi includere indicatori di metadati aggiuntivi per fornire informazioni a colpo d'occhio per i contenuti nell'albero del browser multimediale e durante la riproduzione. All'interno dell'albero di navigazione, Android Auto e Android Automotive OS leggono gli extra associati a un elemento e cercano determinate costanti per determinare quali indicatori visualizzare. Durante la riproduzione dei contenuti multimediali, Android Auto e Android Automotive OS leggono i metadati della sessione multimediale e cercano determinate costanti per determinare gli indicatori da visualizzare.

Figura 3. Visualizzazione della riproduzione con i metadati che identificano il brano e l'artista, nonché un'icona che indica i contenuti espliciti.

Figura 4. Visualizzazione Sfoglia con un punto per i contenuti non riprodotti nel primo elemento e una barra di avanzamento per i contenuti riprodotti parzialmente nel secondo elemento.
Le seguenti costanti possono essere utilizzate sia negli extra di descrizione MediaItem
sia negli extra MediaMetadata
:
EXTRA_DOWNLOAD_STATUS
: indica lo stato del download di un elemento. Utilizza questa costante come chiave; le seguenti costanti lunghe sono i valori possibili:STATUS_DOWNLOADED
: l'elemento è stato scaricato completamente.STATUS_DOWNLOADING
: l'elemento è in fase di download.STATUS_NOT_DOWNLOADED
: l'elemento non viene scaricato.
METADATA_KEY_IS_EXPLICIT
: indica se l'elemento contiene contenuti espliciti. Per indicare che un elemento è esplicito, utilizza questa costante come chiave e il valoreMETADATA_VALUE_ATTRIBUTE_PRESENT
.
Le seguenti costanti possono essere utilizzate solo negli extra di descrizione MediaItem
:
DESCRIPTION_EXTRAS_KEY_COMPLETION_STATUS
: indica lo stato di completamento dei contenuti nel formato lungo, come le puntate dei podcast o gli audiolibri. Utilizza questa costante come chiave; le seguenti costanti integer sono i valori possibili:DESCRIPTION_EXTRAS_VALUE_COMPLETION_STATUS_NOT_PLAYED
: l'articolo non è stato riprodotto.DESCRIPTION_EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED
: l'elemento è stato riprodotto parzialmente e la posizione attuale si trova a metà.DESCRIPTION_EXTRAS_VALUE_COMPLETION_STATUS_FULLY_PLAYED
: l'elemento è stato completato.
DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE
: indica l'avanzamento del completamento dei contenuti nel formato lungo come numero compreso tra 0.0 e 1.0, inclusi. Questo extra fornisce maggiori informazioni sullo stato diPARTIALLY_PLAYING
, in modo che Android Auto o Android Automotive OS mostri un indicatore di avanzamento più significativo, ad esempio una barra di avanzamento. Se utilizzi questo extra, consulta la sezione relativa all'aggiornamento della barra di avanzamento nella visualizzazione Sfoglia durante la riproduzione dei contenuti di questa guida per scoprire come mantenere aggiornato questo indicatore dopo la sua impressione iniziale.
Per visualizzare gli indicatori che vengono visualizzati mentre l'utente sfoglia l'albero di esplorazione dei contenuti multimediali, crea un bundle di contenuti aggiuntivi che includa una o più di queste costanti e passa il bundle al metodo MediaDescription.Builder.setExtras()
.
Il seguente snippet di codice mostra come visualizzare gli indicatori per un elemento multimediale esplicito completato al 70%:
Kotlin
import androidx.media.utils.MediaConstants val extras = Bundle() extras.putLong( MediaConstants.METADATA_KEY_IS_EXPLICIT, MediaConstants.METADATA_VALUE_ATTRIBUTE_PRESENT) extras.putInt( MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_STATUS, MediaConstants.DESCRIPTION_EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED) extras.putDouble( MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE, 0.7) val description = MediaDescriptionCompat.Builder() .setMediaId(/*...*/) .setTitle(resources.getString(/*...*/)) .setExtras(extras) .build() return MediaBrowserCompat.MediaItem(description, /* flags */)
Java
import androidx.media.utils.MediaConstants; Bundle extras = new Bundle(); extras.putLong( MediaConstants.METADATA_KEY_IS_EXPLICIT, MediaConstants.METADATA_VALUE_ATTRIBUTE_PRESENT); extras.putInt( MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_STATUS, MediaConstants.DESCRIPTION_EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED); extras.putDouble( MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE, 0.7); MediaDescriptionCompat description = new MediaDescriptionCompat.Builder() .setMediaId(/*...*/) .setTitle(resources.getString(/*...*/)) .setExtras(extras) .build(); return new MediaBrowserCompat.MediaItem(description, /* flags */);
Per visualizzare gli indicatori di un elemento multimediale attualmente in riproduzione, puoi
dichiarare i valori Long
per METADATA_KEY_IS_EXPLICIT
o EXTRA_DOWNLOAD_STATUS
in MediaMetadataCompat
del tuo mediaSession
. Non puoi visualizzare gli indicatori
DESCRIPTION_EXTRAS_KEY_COMPLETION_STATUS
o
DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE
nella visualizzazione della riproduzione.
Il seguente snippet di codice mostra come indicare che il brano corrente nella visualizzazione della riproduzione è esplicito e scaricato:
Kotlin
import androidx.media.utils.MediaConstants mediaSession.setMetadata( MediaMetadataCompat.Builder() .putString( MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, "Song Name") .putString( MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, "Artist name") .putString( MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI, albumArtUri.toString()) .putLong( MediaConstants.METADATA_KEY_IS_EXPLICIT, MediaConstants.METADATA_VALUE_ATTRIBUTE_PRESENT) .putLong( MediaDescriptionCompat.EXTRA_DOWNLOAD_STATUS, MediaDescriptionCompat.STATUS_DOWNLOADED) .build())
Java
import androidx.media.utils.MediaConstants; mediaSession.setMetadata( new MediaMetadataCompat.Builder() .putString( MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, "Song Name") .putString( MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, "Artist name") .putString( MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI, albumArtUri.toString()) .putLong( MediaConstants.METADATA_KEY_IS_EXPLICIT, MediaConstants.METADATA_VALUE_ATTRIBUTE_PRESENT) .putLong( MediaDescriptionCompat.EXTRA_DOWNLOAD_STATUS, MediaDescriptionCompat.STATUS_DOWNLOADED) .build());
Aggiornare la barra di avanzamento nella visualizzazione Sfoglia durante la riproduzione dei contenuti
Come accennato in precedenza, puoi utilizzare l'extra
DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE
per mostrare una barra di avanzamento per i contenuti riprodotti parzialmente nella
visualizzazione Sfoglia. Tuttavia, se un utente continua a riprodurre i contenuti riprodotti parzialmente
da Android Auto o Android Automotive OS, l'indicatore diventa
impreciso con il passare del tempo.
Per mantenere aggiornata la barra di avanzamento di Android Auto e Android Automotive OS, puoi fornire informazioni aggiuntive in MediaMetadataCompat
e PlaybackStateCompat
per collegare i contenuti in corso agli elementi multimediali nella visualizzazione Sfoglia. Perché l'elemento multimediale abbia una barra di avanzamento che si aggiorna automaticamente, devono essere soddisfatti i seguenti requisiti:
- Al momento della creazione, l'
MediaItem
deve inviareDESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE
nei suoi extra con un valore compreso tra 0,0 e 1,0 inclusi. MediaMetadataCompat
deve inviareMETADATA_KEY_MEDIA_ID
con un valore stringa uguale all'ID media passato aMediaItem
.PlaybackStateCompat
deve includere un extra con la chiavePLAYBACK_STATE_EXTRAS_KEY_MEDIA_ID
che viene mappata a un valore stringa uguale all'ID media trasmesso aMediaItem
.
Il seguente snippet di codice mostra come indicare che l'elemento attualmente in riproduzione è collegato a un elemento nella visualizzazione Sfoglia:
Kotlin
import androidx.media.utils.MediaConstants // When the MediaItem is constructed to show in the browse view. // Suppose the item was 25% complete when the user launched the browse view. val mediaItemExtras = Bundle() mediaItemExtras.putDouble( MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE, 0.25) val description = MediaDescriptionCompat.Builder() .setMediaId("my-media-id") .setExtras(mediaItemExtras) // ...and any other setters. .build() return MediaBrowserCompat.MediaItem(description, /* flags */) // Elsewhere, when the user has selected MediaItem for playback. mediaSession.setMetadata( MediaMetadataCompat.Builder() .putString(MediaMetadata.METADATA_KEY_MEDIA_ID, "my-media-id") // ...and any other setters. .build()) val playbackStateExtras = Bundle() playbackStateExtras.putString( MediaConstants.PLAYBACK_STATE_EXTRAS_KEY_MEDIA_ID, "my-media-id") mediaSession.setPlaybackState( PlaybackStateCompat.Builder() .setExtras(playbackStateExtras) // ...and any other setters. .build())
Java
import androidx.media.utils.MediaConstants; // When the MediaItem is constructed to show in the browse view. // Suppose the item was 25% complete when the user launched the browse view. Bundle mediaItemExtras = new Bundle(); mediaItemExtras.putDouble( MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE, 0.25); MediaDescriptionCompat description = new MediaDescriptionCompat.Builder() .setMediaId("my-media-id") .setExtras(mediaItemExtras) // ...and any other setters. .build(); return MediaBrowserCompat.MediaItem(description, /* flags */); // Elsewhere, when the user has selected MediaItem for playback. mediaSession.setMetadata( new MediaMetadataCompat.Builder() .putString(MediaMetadata.METADATA_KEY_MEDIA_ID, "my-media-id") // ...and any other setters. .build()); Bundle playbackStateExtras = new Bundle(); playbackStateExtras.putString( MediaConstants.PLAYBACK_STATE_EXTRAS_KEY_MEDIA_ID, "my-media-id"); mediaSession.setPlaybackState( new PlaybackStateCompat.Builder() .setExtras(playbackStateExtras) // ...and any other setters. .build());
Visualizzare i risultati di ricerca sfogliabili

Figura 5. Visualizzazione della riproduzione con un'opzione "Risultati di ricerca" per visualizzare gli elementi multimediali correlati alla ricerca vocale dell'utente.
La tua app può fornire risultati di ricerca contestuali che vengono visualizzati agli utenti quando avviano una query di ricerca. Android Auto e Android Automotive OS mostrano questi risultati tramite interfacce di query di ricerca o tramite funzionalità che si basano su query effettuate in precedenza nella sessione. Per saperne di più, consulta la sezione Supportare le azioni vocali di questa guida.
Per visualizzare i risultati di ricerca esplorabili, includi la chiave costante
BROWSER_SERVICE_EXTRAS_KEY_SEARCH_SUPPORTED
nel bundle di extra del metodo onGetRoot()
del servizio, mappando il valore booleano true
.
Il seguente snippet di codice mostra come abilitare il supporto nel metodo onGetRoot()
:
Kotlin
import androidx.media.utils.MediaConstants @Nullable fun onGetRoot( @NonNull clientPackageName: String, clientUid: Int, @Nullable rootHints: Bundle ): BrowserRoot { val extras = Bundle() extras.putBoolean( MediaConstants.BROWSER_SERVICE_EXTRAS_KEY_SEARCH_SUPPORTED, true) return BrowserRoot(ROOT_ID, extras) }
Java
import androidx.media.utils.MediaConstants; @Nullable @Override public BrowserRoot onGetRoot( @NonNull String clientPackageName, int clientUid, @Nullable Bundle rootHints) { Bundle extras = new Bundle(); extras.putBoolean( MediaConstants.BROWSER_SERVICE_EXTRAS_KEY_SEARCH_SUPPORTED, true); return new BrowserRoot(ROOT_ID, extras); }
Per iniziare a fornire risultati di ricerca, esegui l'override del metodo onSearch()
nel servizio di navigazione multimediale. Android Auto e Android Automotive OS
inoltrano i termini di ricerca dell'utente a questo metodo ogni volta che un utente richiama un'interfaccia di query di ricerca o la funzionalità "Risultati di ricerca".
Puoi organizzare i risultati di ricerca
dal metodo onSearch()
del tuo servizio utilizzando elementi del titolo
per renderli più sfogliabili. Ad esempio, se la tua app riproduce musica, potresti
organizzare i risultati di ricerca per album, artista e brani.
Il seguente snippet di codice mostra una semplice implementazione del metodo onSearch()
:
Kotlin
fun onSearch(query: String, extras: Bundle) { // Detach from results to unblock the caller (if a search is expensive). result.detach() object:AsyncTask() { internal var searchResponse:ArrayList internal var succeeded = false protected fun doInBackground(vararg params:Void):Void { searchResponse = ArrayList() if (doSearch(query, extras, searchResponse)) { succeeded = true } return null } protected fun onPostExecute(param:Void) { if (succeeded) { // Sending an empty List informs the caller that there were no results. result.sendResult(searchResponse) } else { // This invokes onError() on the search callback. result.sendResult(null) } return null } }.execute() } // Populates resultsToFill with search results. Returns true on success or false on error. private fun doSearch( query: String, extras: Bundle, resultsToFill: ArrayList ): Boolean { // Implement this method. }
Java
@Override public void onSearch(final String query, final Bundle extras, Result<List<MediaItem>> result) { // Detach from results to unblock the caller (if a search is expensive). result.detach(); new AsyncTask<Void, Void, Void>() { List<MediaItem> searchResponse; boolean succeeded = false; @Override protected Void doInBackground(Void... params) { searchResponse = new ArrayList<MediaItem>(); if (doSearch(query, extras, searchResponse)) { succeeded = true; } return null; } @Override protected void onPostExecute(Void param) { if (succeeded) { // Sending an empty List informs the caller that there were no results. result.sendResult(searchResponse); } else { // This invokes onError() on the search callback. result.sendResult(null); } } }.execute() } /** Populates resultsToFill with search results. Returns true on success or false on error. */ private boolean doSearch(String query, Bundle extras, ArrayList<MediaItem> resultsToFill) { // Implement this method. }
Azioni di navigazione personalizzate

Figura 6. Azione di navigazione personalizzata singola
Le Azioni di navigazione personalizzate ti consentono di aggiungere icone ed etichette personalizzate agli oggetti MediaItem
della tua app nell'app multimediale dell'auto e di gestire le interazioni degli utenti con queste azioni. In questo modo puoi estendere la funzionalità dell'app Media in
vari modi, ad esempio aggiungendo le azioni "Scarica", "Aggiungi alla coda", "Riproduci radio",
"Preferiti" o "Rimuovi".

Figura 7. Overflow dell'azione di navigazione personalizzata
Se sono presenti più azioni personalizzate di quelle che l'OEM consente di visualizzare, all'utente verrà mostrato un menu di overflow.
Come funzionano?
Ogni azione di navigazione personalizzata è definita da:
- Un ID azione (una stringa identificatore univoca)
- Un'etichetta azione (il testo visualizzato all'utente)
- Un URI dell'icona di azione (una risorsa drawable vettoriale a cui è possibile applicare una tinta)
Definisci un elenco di azioni di navigazione personalizzate a livello globale nell'ambito del tuo
BrowseRoot
. Puoi quindi allegare un sottoinsieme di queste azioni a singoli
MediaItem.
Quando un utente interagisce con un'azione di navigazione personalizzata, la tua app riceve un callback
in onCustomAction()
. Puoi quindi gestire l'azione e aggiornare l'elenco delle azioni per MediaItem
, se necessario. Questa opzione è utile per le azioni stateful
come "Aggiungi ai preferiti" e "Scarica". Per le azioni che non richiedono aggiornamenti, come "Riproduci
radio", non è necessario aggiornare l'elenco delle azioni.

Figura 8. Barra degli strumenti Azione di navigazione personalizzata
Puoi anche collegare azioni di navigazione personalizzate alla radice di un nodo di navigazione. Queste azioni verranno visualizzate in una barra degli strumenti secondaria sotto la barra degli strumenti principale.
Come implementare le azioni di navigazione personalizzate
Ecco i passaggi per aggiungere azioni di navigazione personalizzate al tuo progetto:
- Esegui l'override di due metodi nell'implementazione
di
MediaBrowserServiceCompat
: - Analizza i limiti delle azioni in fase di runtime:
- In
onGetRoot()
, ottieni il numero massimo di azioni consentite per ogniMediaItem
utilizzando la chiaveBROWSER_ROOT_HINTS_KEY_CUSTOM_BROWSER_ACTION_LIMIT
inrootHints
Bundle
. Un limite pari a 0 indica che la funzionalità non è supportata dal sistema.
- In
- Crea l'elenco globale delle azioni di navigazione personalizzate:
- Per ogni azione, crea un oggetto
Bundle
con le seguenti chiavi: *EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID
: l'ID azione *EXTRAS_KEY_CUSTOM_BROWSER_ACTION_LABEL
: l'etichetta dell'azione *EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ICON_URI
: l'URI dell'icona dell'azione * Aggiungi tutti gli oggettiBundle
dell'azione a un elenco.
- Per ogni azione, crea un oggetto
- Aggiungi l'elenco globale al tuo
BrowseRoot
:- Negli
BrowseRoot
extraBundle
, aggiungi l'elenco delle azioni comeParcelable
Arraylist
utilizzando la chiaveBROWSER_SERVICE_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ROOT_LIST
.
- Negli
- Aggiungi azioni agli oggetti
MediaItem
:- Puoi aggiungere azioni a singoli oggetti
MediaItem
includendo l'elenco degli ID azione negli extraMediaDescriptionCompat
utilizzando la chiaveDESCRIPTION_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID_LIST
. Questo elenco deve essere un sottoinsieme dell'elenco globale delle azioni definite inBrowseRoot
.
- Puoi aggiungere azioni a singoli oggetti
- Gestisci le azioni e restituisci l'avanzamento o i risultati:
- In
onCustomAction
, gestisci l'azione in base all'ID azione e a eventuali altri dati necessari. Puoi ottenere l'ID diMediaItem
che ha attivato l'azione dagli extra utilizzando la chiaveEXTRAS_KEY_CUSTOM_BROWSER_ACTION_MEDIA_ITEM_ID
. - Puoi aggiornare l'elenco delle azioni per un
MediaItem
includendo la chiaveEXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM
nel bundle di avanzamento o risultati.
- In
Ecco alcune modifiche che puoi apportare al tuo BrowserServiceCompat
per iniziare
a utilizzare le Azioni di navigazione personalizzate.
Override BrowserServiceCompat
Devi eseguire l'override dei seguenti metodi in MediaBrowserServiceCompat
.
public void onLoadItem(String itemId, @NonNull Result<MediaBrowserCompat.MediaItem> result)
public void onCustomAction(@NonNull String action, Bundle extras, @NonNull Result<Bundle> result)
Limite azioni di analisi
Devi verificare quante azioni di navigazione personalizzate sono supportate.
public BrowserRoot onGetRoot(@NonNull String clientPackageName, int clientUid, Bundle rootHints) { rootHints.getInt( MediaConstants.BROWSER_ROOT_HINTS_KEY_CUSTOM_BROWSER_ACTION_LIMIT, 0) }
Creare un'azione di navigazione personalizzata
Ogni azione deve essere inserita in un Bundle
separato.
- ID azione
bundle.putString(MediaConstants.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID, "<ACTION_ID>")
- Etichetta azione
bundle.putString(MediaConstants.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_LABEL, "<ACTION_LABEL>")
- URI dell'icona dell'azione
bundle.putString(MediaConstants.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ICON_URI, "<ACTION_ICON_URI>")
Aggiungere azioni di navigazione personalizzate a Parceable
ArrayList
Aggiungi tutti gli oggetti Custom Browse Action Bundle
a un ArrayList
.
private ArrayList<Bundle> createCustomActionsList( CustomBrowseAction browseActions) { ArrayList<Bundle> browseActionsBundle = new ArrayList<>(); for (CustomBrowseAction browseAction : browseActions) { Bundle action = new Bundle(); action.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID, browseAction.mId); action.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_LABEL, getString(browseAction.mLabelResId)); action.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ICON_URI, browseAction.mIcon); browseActionsBundle.add(action); } return browseActionsBundle; }
Aggiungere l'elenco delle azioni di navigazione personalizzate alla radice di navigazione
public BrowserRoot onGetRoot(@NonNull String clientPackageName, int clientUid, Bundle rootHints) { Bundle browserRootExtras = new Bundle(); browserRootExtras.putParcelableArrayList( BROWSER_SERVICE_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ROOT_LIST, createCustomActionsList())); mRoot = new BrowserRoot(ROOT_ID, browserRootExtras); return mRoot; }
Aggiungere azioni a un MediaItem
MediaDescriptionCompat buildDescription (long id, String title, String subtitle, String description, Uri iconUri, Uri mediaUri, ArrayList<String> browseActionIds) { MediaDescriptionCompat.Builder bob = new MediaDescriptionCompat.Builder(); bob.setMediaId(id); bob.setTitle(title); bob.setSubtitle(subtitle); bob.setDescription(description); bob.setIconUri(iconUri); bob.setMediaUri(mediaUri); Bundle extras = new Bundle(); extras.putStringArrayList( DESCRIPTION_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID_LIST, browseActionIds); bob.setExtras(extras); return bob.build(); } MediaItem mediaItem = new MediaItem(buildDescription(...), flags);
Risultato build onCustomAction
- Analizza mediaId da
Bundle extras
:@Override public void onCustomAction( @NonNull String action, Bundle extras, @NonNull Result<Bundle> result){ String mediaId = extras.getString(MediaConstans.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_MEDIA_ITEM_ID); }
- Per i risultati asincroni, stacca il risultato.
result.detach()
- Bundle dei risultati della build
- Messaggio all'utente
mResultBundle.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_MESSAGE, mContext.getString(stringRes))
- Aggiorna elemento(utilizzare per aggiornare le azioni in un elemento)
mResultBundle.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM, mediaId);
- Apri la visualizzazione Riproduzione
//Shows user the PBV without changing the playback state mResultBundle.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_SHOW_PLAYING_ITEM, null);
- Aggiorna nodo di navigazione
//Change current browse node to mediaId mResultBundle.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_BROWSE_NODE, mediaId);
- Messaggio all'utente
- In caso di errore, chiama il numero
result.sendError(resultBundle).
- Se si tratta di un aggiornamento sullo stato di avanzamento, chiama il numero
result.sendProgressUpdate(resultBundle)
. - Termina chiamando il numero
result.sendResult(resultBundle)
.
Aggiorna stato dell'azione
Utilizzando il metodo result.sendProgressUpdate(resultBundle)
con la chiave EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM
, puoi aggiornare MediaItem
in modo che rifletta il nuovo stato dell'azione. In questo modo
puoi fornire all'utente un feedback in tempo reale sui progressi e
sul risultato della sua azione.
Esempio: azione di download
Ecco un esempio di come puoi utilizzare questa funzionalità per implementare un'azione di download con tre stati:
- Download: è lo stato iniziale dell'azione. Quando l'utente seleziona
questa azione, puoi scambiarla con "Download" e chiamare
sendProgressUpdate
per aggiornare l'interfaccia utente. - Download: questo stato indica che il download è in corso. Puoi utilizzare questo stato per mostrare all'utente una barra di avanzamento o un altro indicatore.
- Scaricato: questo stato indica che il download è stato completato. Al termine del
download, puoi sostituire "Download in corso" con "Scaricato" e chiamare
sendResult
con la chiaveEXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM
per indicare che l'elemento deve essere aggiornato. Inoltre, puoi utilizzare il tastoEXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_MESSAGE
per mostrare un messaggio di successo all'utente.
Questo approccio ti consente di fornire all'utente un feedback chiaro sul processo di download e sul suo stato attuale. Puoi aggiungere ancora più dettagli con le icone per mostrare gli stati di download al 25%, 50% e 75%.
Esempio: azione preferita
Un altro esempio è un'azione preferita con due stati:
- Preferiti: questa azione viene visualizzata per gli elementi che non si trovano nell'elenco dei preferiti dell'utente. Quando l'utente seleziona questa azione, puoi scambiarla
con "Preferiti" e chiamare
sendResult
con la chiaveEXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM
per aggiornare l'interfaccia utente. - Nei preferiti: questa azione viene visualizzata per gli elementi presenti nell'elenco
dei preferiti dell'utente. Quando l'utente seleziona questa azione, puoi scambiarla con "Preferiti" e chiamare
sendResult
con il tastoEXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM
per aggiornare l'interfaccia utente.
Questo approccio offre agli utenti un modo chiaro e coerente per gestire i loro elementi preferiti.
Questi esempi mostrano la flessibilità delle Azioni di navigazione personalizzate e come puoi utilizzarle per implementare una serie di funzionalità con feedback in tempo reale per un'esperienza utente migliorata nell'app multimediale dell'auto.
Per un esempio completo di implementazione di questa funzionalità, puoi fare riferimento al progetto
TestMediaApp
.
Attivare il controllo della riproduzione
Android Auto e Android Automotive OS inviano comandi di controllo della riproduzione tramite
MediaSessionCompat
del tuo servizio.
Devi registrare una sessione e implementare i metodi di callback associati.
Registrare una sessione multimediale
Nel metodo onCreate()
del servizio di esplorazione dei contenuti multimediali, crea un MediaSessionCompat
,
quindi registra la sessione multimediale chiamando setSessionToken()
.
Il seguente snippet di codice mostra come creare e registrare una sessione multimediale:
Kotlin
override fun onCreate() { super.onCreate() ... // Start a new MediaSession. val session = MediaSessionCompat(this, "session tag").apply { // Set a callback object that implements MediaSession.Callback // to handle play control requests. setCallback(MyMediaSessionCallback()) } sessionToken = session.sessionToken ... }
Java
public void onCreate() { super.onCreate(); ... // Start a new MediaSession. MediaSessionCompat session = new MediaSessionCompat(this, "session tag"); setSessionToken(session.getSessionToken()); // Set a callback object that implements MediaSession.Callback // to handle play control requests. session.setCallback(new MyMediaSessionCallback()); ... }
Quando crei l'oggetto sessione multimediale, imposti un oggetto callback utilizzato
per gestire le richieste di controllo della riproduzione. Crea questo oggetto di callback fornendo un'implementazione della classe MediaSessionCompat.Callback
per la tua app. La sezione successiva descrive come implementare questo oggetto.
Implementare i comandi di riproduzione
Quando un utente richiede la riproduzione di un elemento multimediale dalla tua app, Android Automotive OS e Android Auto utilizzano la classe MediaSessionCompat.Callback
dell'oggetto MediaSessionCompat
della tua app, che hanno ottenuto dal servizio di esplorazione dei contenuti multimediali dell'app. Quando un utente
vuole controllare la riproduzione dei contenuti, ad esempio mettere in pausa la riproduzione o passare alla
traccia successiva, Android Auto e Android Automotive OS richiamano uno dei metodi
dell'oggetto di callback.
Per gestire la riproduzione dei contenuti, la tua app deve estendere la classe astratta MediaSessionCompat.Callback
e implementare i metodi supportati.
Implementa tutti i seguenti metodi di callback pertinenti per il tipo di contenuti offerti dalla tua app:
onPrepare()
- Richiamato quando viene modificata l'origine media. Android Automotive OS richiama questo metodo anche subito dopo l'avvio. La tua app multimediale deve implementare questo metodo.
onPlay()
- Viene richiamato se l'utente sceglie di riprodurre senza scegliere un elemento specifico. La tua
app deve riprodurre i suoi contenuti predefiniti o, se la riproduzione è stata messa in pausa con
onPause()
, la tua app riprende la riproduzione.Nota:la tua app non deve avviare automaticamente la riproduzione di musica quando Android Automotive OS o Android Auto si connettono al servizio di esplorazione dei contenuti multimediali. Per maggiori informazioni, consulta la sezione relativa all'impostazione dello stato di riproduzione iniziale.
onPlayFromMediaId()
- Viene richiamato quando l'utente sceglie di riprodurre un elemento specifico. Il metodo riceve l'ID che il servizio di esplorazione dei contenuti multimediali ha assegnato all'elemento multimediale nella gerarchia dei contenuti.
onPlayFromSearch()
- Viene richiamato quando l'utente sceglie di riprodurre un brano da una query di ricerca. L'app deve fare una scelta appropriata in base alla stringa di ricerca passata.
onPause()
- Richiamato quando l'utente sceglie di mettere in pausa la riproduzione.
onSkipToNext()
- Viene richiamato quando l'utente sceglie di passare all'elemento successivo.
onSkipToPrevious()
- Richiamato quando l'utente sceglie di passare all'elemento precedente.
onStop()
- Richiamato quando l'utente sceglie di interrompere la riproduzione.
Esegui l'override di questi metodi nella tua app per fornire le funzionalità desiderate. Non
devi implementare un metodo se la sua funzionalità non è supportata dalla tua app. Ad esempio, se la tua app riproduce un live streaming, come una trasmissione sportiva, non
devi implementare il metodo onSkipToNext()
. Puoi utilizzare l'implementazione
predefinita di onSkipToNext()
.
La tua app non ha bisogno di una logica speciale per riprodurre i contenuti tramite gli altoparlanti dell'auto. Quando la tua app riceve una richiesta di riproduzione di contenuti, può riprodurre l'audio allo stesso modo in cui riproduce i contenuti tramite gli altoparlanti o le cuffie dello smartphone dell'utente. Android Auto e Android Automotive OS inviano automaticamente i contenuti audio al sistema dell'auto per la riproduzione tramite gli altoparlanti dell'auto.
Per ulteriori informazioni sulla riproduzione di contenuti audio, consulta Panoramica di MediaPlayer, Panoramica dell'app audio e la panoramica di ExoPlayer.
Impostare le azioni di riproduzione standard
Android Auto e Android Automotive OS mostrano i controlli di riproduzione in base alle
azioni abilitate nell'oggetto
PlaybackStateCompat
.
Per impostazione predefinita, l'app deve supportare le seguenti azioni:
La tua app può supportare anche le seguenti azioni se sono pertinenti ai contenuti dell'app:
Inoltre, hai la possibilità di creare una coda di riproduzione che può essere visualizzata dall'utente, ma non è obbligatorio. Per farlo, chiama i metodi setQueue()
e setQueueTitle()
, attiva l'azione ACTION_SKIP_TO_QUEUE_ITEM
e definisci il callback onSkipToQueueItem()
.
Inoltre, aggiungi il supporto per l'icona Now Playing, che indica i contenuti
in riproduzione. Per farlo, chiama il metodo setActiveQueueItemId()
e trasmetti l'ID dell'elemento attualmente in riproduzione nella coda. Devi
aggiornare setActiveQueueItemId()
ogni volta che la coda cambia.
Android Auto e Android Automotive OS mostrano i pulsanti per ogni azione abilitata
e la coda di riproduzione. Quando vengono
cliccati i pulsanti, il sistema richiama il callback corrispondente da
MediaSessionCompat.Callback
.
Riservare spazio inutilizzato
Android Auto e Android Automotive OS riservano spazio nell'interfaccia utente per le azioni
ACTION_SKIP_TO_PREVIOUS
e ACTION_SKIP_TO_NEXT
. Se la tua app non supporta una di queste funzioni, Android Auto e Android Automotive OS utilizzano lo spazio per visualizzare le azioni personalizzate che crei.
Se non vuoi riempire questi spazi con azioni personalizzate, puoi riservarli
in modo che Android Auto e Android Automotive OS lascino lo spazio vuoto
quando la tua app non supporta la funzione corrispondente. Per farlo, chiama
il metodo setExtras()
con un bundle di extra che contiene costanti corrispondenti alle
funzioni riservate.
SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT
corrisponde a ACTION_SKIP_TO_NEXT
e
SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV
corrisponde a ACTION_SKIP_TO_PREVIOUS
. Utilizza queste costanti come chiavi nel bundle e utilizza il valore booleano true
per i relativi valori.
Imposta PlaybackState iniziale
Poiché Android Auto e Android Automotive OS comunicano con il servizio di esplorazione dei contenuti multimediali, la sessione multimediale comunica lo stato della riproduzione dei contenuti utilizzando PlaybackStateCompat
.
La tua app non deve avviare automaticamente la riproduzione di musica quando Android Automotive OS
o Android Auto si connettono al servizio di esplorazione dei contenuti multimediali. Affidati invece ad Android
Auto e Android Automotive OS per riprendere o avviare la riproduzione in base allo stato dell'auto o alle azioni dell'utente.
Per farlo, imposta l'PlaybackStateCompat
iniziale
della sessione multimediale su
STATE_STOPPED
,
STATE_PAUSED
,
STATE_NONE
,
o STATE_ERROR
.
Le sessioni multimediali in Android Auto e Android Automotive OS durano solo per la
durata del viaggio, quindi gli utenti le avviano e le interrompono di frequente. Per
promuovere un'esperienza fluida tra le sessioni, tieni traccia dello stato della sessione
precedente dell'utente, in modo che quando l'app multimediale riceve una
richiesta di ripresa, l'utente possa riprendere automaticamente da dove aveva interrotto, ad esempio l'ultimo elemento multimediale riprodotto, il PlaybackStateCompat
e
la coda.
Aggiungere azioni di riproduzione personalizzate
Puoi aggiungere azioni di riproduzione personalizzate per visualizzare azioni aggiuntive supportate dalla tua app multimediale. Se lo spazio lo consente (e non è riservato), Android aggiunge le
azioni personalizzate ai controlli di trasporto. In caso contrario, le azioni personalizzate
vengono visualizzate nel menu extra. Le azioni personalizzate vengono visualizzate nell'ordine
in cui vengono aggiunte a PlaybackStateCompat
.
Utilizza le azioni personalizzate per fornire un comportamento diverso dalle azioni standard. Non utilizzarli per sostituire o duplicare le azioni standard.
Puoi aggiungere azioni personalizzate utilizzando il metodo addCustomAction()
nella classe PlaybackStateCompat.Builder
.
Il seguente snippet di codice mostra come aggiungere un'azione personalizzata "Avvia una stazione radio":
Kotlin
val customActionExtras = Bundle() customActionExtras.putInt( androidx.media3.session.MediaConstants.EXTRAS_KEY_COMMAND_BUTTON_ICON_COMPAT, androidx.media3.session.CommandButton.ICON_RADIO) stateBuilder.addCustomAction( PlaybackStateCompat.CustomAction.Builder( CUSTOM_ACTION_START_RADIO_FROM_MEDIA, resources.getString(R.string.start_radio_from_media), startRadioFromMediaIcon // or R.drawable.media3_icon_radio ).run { setExtras(customActionExtras) build() } )
Java
Bundle customActionExtras = new Bundle(); customActionExtras.putInt( androidx.media3.session.MediaConstants.EXTRAS_KEY_COMMAND_BUTTON_ICON_COMPAT, androidx.media3.session.CommandButton.ICON_RADIO); stateBuilder.addCustomAction( new PlaybackStateCompat.CustomAction.Builder( CUSTOM_ACTION_START_RADIO_FROM_MEDIA, resources.getString(R.string.start_radio_from_media), startRadioFromMediaIcon) // or R.drawable.media3_icon_radio .setExtras(customActionExtras) .build());
Per un esempio più dettagliato di questo metodo, consulta il metodo setCustomAction()
nell'app di esempio Universal Android Music Player su GitHub.
Dopo aver creato l'azione personalizzata, la sessione multimediale può rispondere all'azione
eseguendo l'override del metodo onCustomAction()
.
Il seguente snippet di codice mostra come la tua app potrebbe rispondere a un'azione "Avvia un canale radio":
Kotlin
override fun onCustomAction(action: String, extras: Bundle?) { when(action) { CUSTOM_ACTION_START_RADIO_FROM_MEDIA -> { ... } } }
Java
@Override public void onCustomAction(@NonNull String action, Bundle extras) { if (CUSTOM_ACTION_START_RADIO_FROM_MEDIA.equals(action)) { ... } }
Per un esempio più dettagliato di questo metodo, consulta il metodo onCustomAction
nell'app di esempio Universal Android Music Player su GitHub.
Icone per le azioni personalizzate
Ogni azione personalizzata che crei richiede un'icona.
Se la descrizione di questa icona corrisponde a una delle costanti CommandButton.ICON_
, devi impostare questo valore intero per la chiave EXTRAS_KEY_COMMAND_BUTTON_ICON_COMPAT
degli extra dell'azione personalizzata. Sui sistemi supportati, questa operazione sostituirà la risorsa
icona passata a CustomAction.Builder
, consentendo ai componenti di sistema di eseguire il rendering dell'azione
e di altre azioni di riproduzione in uno stile coerente.
Devi anche specificare una risorsa icona. Le app nelle auto possono essere eseguite su schermi di varie dimensioni e densità, quindi le icone che fornisci devono essere risorse grafiche vettoriali. Un disegno vettoriale ti consente di scalare gli asset senza perdere i dettagli. Un elemento disegnabile vettoriale semplifica anche l'allineamento di bordi e angoli ai limiti dei pixel a risoluzioni più piccole.
Se un'azione personalizzata è stateful, ad esempio attiva o disattiva un'impostazione di riproduzione, fornisci icone diverse per i diversi stati, in modo che gli utenti possano vedere una modifica quando selezionano l'azione.
Fornisci stili di icone alternativi per le azioni disattivate
Quando un'azione personalizzata non è disponibile per il contesto corrente, scambia l'icona dell'azione personalizzata con un'icona alternativa che indica che l'azione è disattivata.
Indica il formato audio
Per indicare che i contenuti multimediali attualmente in riproduzione utilizzano un formato audio speciale,
puoi specificare icone visualizzate nelle auto che supportano questa funzionalità. Puoi
impostare
KEY_CONTENT_FORMAT_TINTABLE_LARGE_ICON_URI
e
KEY_CONTENT_FORMAT_TINTABLE_SMALL_ICON_URI
nel bundle di contenuti aggiuntivi dell'elemento multimediale attualmente in riproduzione (trasmesso a
MediaSession.setMetadata()
). Assicurati di impostare entrambi
i contenuti aggiuntivi per adattarsi a layout diversi.
Inoltre, puoi impostare l'extra KEY_IMMERSIVE_AUDIO
per comunicare ai produttori di auto che si tratta di audio immersivo e che devono prestare molta attenzione quando decidono se applicare effetti audio che potrebbero interferire con i contenuti immersivi.
Aggiungere link dall'elemento in riproduzione
Puoi configurare l'elemento multimediale in riproduzione in modo che i sottotitoli codificati, la descrizione o entrambi siano link ad altri elementi multimediali. In questo modo, l'utente può passare rapidamente a elementi correlati, ad esempio altre canzoni dello stesso artista, altri episodi del podcast e così via. Se l'auto supporta questa funzionalità, gli utenti possono toccare il link per sfogliare i contenuti.
Per aggiungere link, configura i metadati
KEY_SUBTITLE_LINK_MEDIA_ID
(per il collegamento dai sottotitoli) o
KEY_DESCRIPTION_LINK_MEDIA_ID
(per il collegamento dalla
descrizione). Per maggiori dettagli, consulta la documentazione di riferimento per questi
campi dei metadati.
Supportare le azioni vocali
La tua app multimediale deve supportare i comandi vocali per offrire ai conducenti un'esperienza sicura e comoda che riduca al minimo le distrazioni. Ad esempio, se la tua app sta riproducendo un elemento multimediale, l'utente può dire "Riproduci [titolo del brano]" per chiedere all'app di riprodurre un altro brano senza guardare o toccare il display dell'auto. Gli utenti possono avviare le query facendo clic sui pulsanti appropriati sul volante o pronunciando le hotword "Ok Google".
Quando Android Auto o Android Automotive OS rileva e interpreta un'azione
vocale, questa viene inviata all'app tramite
onPlayFromSearch()
.
Alla ricezione di questo callback, l'app trova i contenuti corrispondenti alla stringa query
e avvia la riproduzione.
Gli utenti possono specificare diverse categorie di termini nella query: genere, artista,
album, nome del brano, stazione radio o playlist, tra le altre. Quando crei
il supporto per la ricerca, tieni conto di tutte le categorie pertinenti per la tua app.
Se Android Auto o Android Automotive OS rileva che una determinata query rientra in
determinate categorie, aggiunge extra nel parametro extras
. È possibile inviare i seguenti extra:
Tieni conto di una stringa query
vuota, che può essere inviata da
Android Auto o Android Automotive OS se l'utente non specifica i termini di ricerca.
Ad esempio, se l'utente dice "Fammi sentire un po' di musica". In questo caso, la tua app potrebbe
scegliere di avviare un brano ascoltato di recente o suggerito di recente.
Se una ricerca non può essere elaborata rapidamente, non bloccarla in onPlayFromSearch()
.
Imposta invece lo stato di riproduzione su STATE_CONNECTING
ed esegui la ricerca su un thread asincrono.
Una volta avviata la riproduzione, valuta la possibilità di compilare la coda della sessione multimediale con contenuti correlati. Ad esempio, se l'utente richiede la riproduzione di un album, la tua app potrebbe riempire la coda con la tracklist dell'album. Valuta anche l'implementazione del supporto per i risultati di ricerca esplorabili, in modo che un utente possa scegliere una traccia diversa che corrisponda alla sua query.
Oltre alle query "riproduci", Android Auto e Android Automotive OS
riconoscono le query vocali per controllare la riproduzione, ad esempio "metti in pausa la musica" e "prossima
canzone", e abbinano questi comandi ai callback della sessione multimediale appropriati,
come onPause()
e onSkipToNext()
.
Per un esempio dettagliato su come implementare le azioni di riproduzione vocale nella tua app, consulta Assistente Google e app multimediali.
Implementare salvaguardie contro le distrazioni
Poiché lo smartphone di un utente è connesso agli altoparlanti dell'auto quando utilizza Android Auto, devi adottare ulteriori precauzioni per evitare distrazioni per il conducente.
Disattivare gli allarmi in auto
Le app multimediali per Android Auto non devono avviare la riproduzione dell'audio tramite gli altoparlanti dell'auto a meno che l'utente non avvii la riproduzione, ad esempio premendo un pulsante di riproduzione. Nemmeno una sveglia programmata dall'utente dall'app multimediale deve iniziare a riprodurre musica dagli altoparlanti dell'auto.
Per soddisfare questo requisito, la tua app
può utilizzare CarConnection
come segnale prima di riprodurre qualsiasi audio. La tua app può verificare se lo smartphone
proietta contenuti sullo schermo dell'auto osservando LiveData
per il tipo di connessione
all'auto
e controllando se è uguale a
CONNECTION_TYPE_PROJECTION
.
Se lo smartphone dell'utente sta proiettando contenuti, le app multimediali che supportano le sveglie devono eseguire una delle seguenti operazioni:
- Disattiva l'allarme.
- Riproduci la sveglia su
STREAM_ALARM
e fornisci un'interfaccia utente sullo schermo dello smartphone per disattivarla.
Gestire le pubblicità multimediali
Per impostazione predefinita, Android Auto mostra una notifica quando i metadati dei contenuti multimediali cambiano
durante una sessione di riproduzione audio. Quando un'app multimediale passa dalla riproduzione di musica
alla visualizzazione di una pubblicità, è una distrazione mostrare una
notifica all'utente. Per impedire ad Android Auto di mostrare una notifica
in questo caso, devi impostare la chiave dei metadati multimediali
METADATA_KEY_IS_ADVERTISEMENT
su
METADATA_VALUE_ATTRIBUTE_PRESENT
,
come mostrato nel seguente snippet di codice:
Kotlin
import androidx.media.utils.MediaConstants override fun onPlayFromMediaId(mediaId: String, extras: Bundle?) { MediaMetadataCompat.Builder().apply { if (isAd(mediaId)) { putLong( MediaConstants.METADATA_KEY_IS_ADVERTISEMENT, MediaConstants.METADATA_VALUE_ATTRIBUTE_PRESENT) } // ...add any other properties you normally would. mediaSession.setMetadata(build()) } }
Java
import androidx.media.utils.MediaConstants; @Override public void onPlayFromMediaId(String mediaId, Bundle extras) { MediaMetadataCompat.Builder builder = new MediaMetadataCompat.Builder(); if (isAd(mediaId)) { builder.putLong( MediaConstants.METADATA_KEY_IS_ADVERTISEMENT, MediaConstants.METADATA_VALUE_ATTRIBUTE_PRESENT); } // ...add any other properties you normally would. mediaSession.setMetadata(builder.build()); }
Gestire gli errori generali
Quando l'app rileva un errore, imposta lo stato di riproduzione su STATE_ERROR
e fornisci un messaggio di errore utilizzando il metodo
setErrorMessage()
. Consulta la sezione PlaybackStateCompat
per un elenco dei codici di errore che puoi utilizzare quando imposti il messaggio di errore.
I messaggi di errore devono essere rivolti all'utente e localizzati in base alle impostazioni
locali correnti dell'utente. Android Auto e Android Automotive OS possono quindi mostrare il messaggio di errore all'utente.
Ad esempio, se i contenuti non sono disponibili nella regione attuale dell'utente, puoi
utilizzare il codice di errore ERROR_CODE_NOT_AVAILABLE_IN_REGION
quando imposti il messaggio di errore.
Kotlin
mediaSession.setPlaybackState( PlaybackStateCompat.Builder() .setState(PlaybackStateCompat.STATE_ERROR) .setErrorMessage(PlaybackStateCompat.ERROR_CODE_NOT_AVAILABLE_IN_REGION, getString(R.string.error_unsupported_region)) // ...and any other setters. .build())
Java
mediaSession.setPlaybackState( new PlaybackStateCompat.Builder() .setState(PlaybackStateCompat.STATE_ERROR) .setErrorMessage(PlaybackStateCompat.ERROR_CODE_NOT_AVAILABLE_IN_REGION, getString(R.string.error_unsupported_region)) // ...and any other setters. .build());
Per ulteriori informazioni sugli stati di errore, vedi Utilizzo di una sessione multimediale: stati ed errori.
Se un utente di Android Auto deve aprire l'app per smartphone per risolvere un errore, fornisci queste informazioni all'utente nel messaggio. Ad esempio, il messaggio di errore potrebbe essere "Accedi a [nome della tua app]" anziché "Accedi".