Creare app multimediali per auto

Android Auto e il sistema operativo Android Automotive ti consentono di mettere a disposizione degli utenti i contenuti delle tue app multimediali mentre sono in auto. Un'app multimediale per auto deve fornire un servizio di browser multimediale in modo che Android Auto e il sistema operativo Android Automotive, o un'altra app con un browser multimediale, possano rilevare e mostrare i tuoi contenuti.

Questa guida presuppone che tu abbia già un'app multimediale che riproduce l'audio su un telefono e che l'app multimediale sia conforme all'architettura delle app multimediali di Android.

Questa guida descrive i componenti richiesti di un MediaBrowserService e MediaSession di cui la tua app ha bisogno 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 di Android Automotive OS alla tua app multimediale.

Prima di iniziare

  1. Consulta la documentazione dell'API Android Media.
  2. Consulta la sezione Creare app multimediali per indicazioni sul design.
  3. 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 mostrare i suoi contenuti.
Browser multimediale
Un'API utilizzata dalle app multimediali per scoprire i servizi di browser multimediali e visualizzarne i contenuti. Android Auto e il sistema operativo Android Automotive utilizzano un browser multimediale per trovare il servizio di browser multimediale della tua app.
Elemento multimediale

Il browser multimediale organizza i contenuti in un albero di oggetti MediaItem. Un elemento multimediale può avere uno o entrambi i seguenti flag:

  • FLAG_PLAYABLE: indica che l'elemento è una foglia nell'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 ha elementi secondari. Ad esempio, l'elemento rappresenta un album e i relativi elementi secondari sono i brani dell'album.

Un elemento multimediale che può essere visualizzato e riprodotto funziona come una playlist. Puoi selezionare l'elemento stesso per riprodurre tutti i relativi elementi secondari oppure puoi sfogliarli.

Ottimizzata per il veicolo

Un'attività per un'app per Android Automotive OS conforme alle linee guida per la progettazione di Android Automotive OS. L'interfaccia di queste attività non viene disegnata dal sistema operativo Android Automotive, quindi devi assicurarti che la tua app rispetti le linee guida di progettazione. In genere, include target dei tocchi e dimensioni dei caratteri più grandi, supporto per le modalità giorno e notte e rapporti di contrasto più elevati.

Le interfacce utente ottimizzate per il veicolo possono essere visualizzate solo quando le limitazioni dell'esperienza utente dell'auto (CUXR) non sono attive, in quanto queste interfacce possono richiedere un'attenzione o un'interazione prolungata da parte dell'utente. I CUXR non sono attivi quando l'auto è ferma o parcheggiata, ma lo sono sempre quando è 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 browser multimediale.

Configura i file manifest dell'app

Prima di poter creare il servizio di browser multimediale, devi configurare i file manifest dell'app.

Dichiarare il servizio di browser multimediale

Sia Android Auto che il sistema operativo Android Automotive si connettono alla tua app tramite il servizio di browser multimediale per sfogliare i contenuti multimediali. Dichiara il servizio di browser multimediale 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 del browser multimediale nel manifest. Includi questo codice nel file manifest del modulo del sistema operativo Android Automotive e nel file manifest dell'app per smartphone.

<application>
    ...
    <service android:name=".MyMediaBrowserService"
             android:exported="true">
        <intent-filter>
            <action android:name="android.media.browse.MediaBrowserService"/>
        </intent-filter>
    </service>
    ...
</application>

Specificare le icone delle app

Devi specificare le icone delle app che Android Auto e Android Automotive OS possono utilizzare per rappresentare la tua app nell'interfaccia utente di sistema. Sono necessari due tipi di icone:

  • Icona in Avvio applicazioni
  • Icona di attribuzione

Icona in Avvio applicazioni

L'icona del programma di avvio rappresenta la tua app nell'interfaccia utente di sistema, ad esempio nel programma di avvio e nella barra delle icone. Puoi specificare che vuoi utilizzare l'icona della tua app mobile per rappresentare l'app multimediale dell'auto utilizzando la seguente dichiarazione manifest:

<application
    ...
    android:icon="@mipmap/ic_launcher"
    ...
/>

Per utilizzare un'icona diversa da quella dell'app mobile, imposta la proprietà android:icon nell'elemento <service> del servizio di browser multimediale nel file manifest:

<application>
    ...
    <service
        ...
        android:icon="@mipmap/auto_launcher"
        ...
    />
</application>

Icona di attribuzione

Figura 1. Icona di attribuzione nella scheda dei contenuti multimediali.

L'icona di attribuzione viene utilizzata nei casi in cui i contenuti multimediali hanno la precedenza, come nelle schede multimediali. Valuta la possibilità di riutilizzare l'icona piccola utilizzata per le notifiche. L'icona deve essere monocromatica. Puoi specificare un'icona utilizzata per rappresentare la tua app utilizzando la seguente dichiarazione manifest:

<application>
    ...
    <meta-data
        android:name="androidx.car.app.TintableAttributionIcon"
        android:resource="@drawable/ic_status_icon" />
    ...
</application>

Creare il servizio di browser multimediale

Per creare un servizio di browser multimediale, espandi la classe MediaBrowserServiceCompat. Sia Android Auto che il sistema operativo Android Automotive possono quindi utilizzare il tuo servizio per:

  • Sfoglia la gerarchia dei contenuti dell'app per presentare un menu all'utente.
  • Recupera il token per l'oggetto MediaSessionCompat della tua app per controllare la riproduzione audio.

Puoi anche utilizzare il servizio di browser multimediale per consentire ad altri client di accedere ai contenuti multimediali dalla tua app. Questi client multimediali possono essere altre app sullo smartphone di un utente o altri client remoti.

Flusso di lavoro del servizio del browser multimediale

Questa sezione descrive come il sistema operativo Android Automotive e Android Auto interagiscono con il servizio di browser multimediale durante un flusso di lavoro utente tipico.

  1. L'utente avvia la tua app su Android Automotive OS o Android Auto.
  2. Android Automotive OS o Android Auto contatta il servizio del browser multimediale della tua app utilizzando il metodo onCreate(). Nell'implementazione del metodo onCreate(), devi creare e registrare un oggetto MediaSessionCompat e il relativo oggetto callback.
  3. Il sistema operativo Android Automotive o Android Auto chiama il metodo onGetRoot() del tuo servizio per recuperare l'elemento multimediale principale nella gerarchia dei contenuti. L'elemento multimediale principale non viene visualizzato, ma viene utilizzato per recuperare altri contenuti dalla tua app.
  4. Android Automotive OS o Android Auto chiama il metodo onLoadChildren() del tuo servizio per recuperare gli elementi secondari dell'elemento multimediale principale. Android Automotive OS e Android Auto mostrano questi elementi multimediali come livello superiore degli elementi di contenuto. Per ulteriori informazioni su ciò che il sistema si aspetta a questo livello, consulta la sezione Strutturare il menu principale in questa pagina.
  5. Se l'utente seleziona un elemento multimediale sfogliabile, il metodo onLoadChildren() del servizio viene richiamato di nuovo per recuperare gli elementi secondari dell'elemento del menu selezionato.
  6. 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.
  7. Se supportato 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 il sistema operativo Android Automotive chiamano il servizio del browser multimediale della tua app per scoprire quali contenuti sono disponibili. Per supportare questa funzionalità, devi implementare due metodi nel servizio di browser multimediale: onGetRoot() e onLoadChildren().

Implementa onGetRoot

Il metodo onGetRoot() del tuo servizio restituisce informazioni sul nodo principale della gerarchia dei contenuti. Android Auto e il sistema operativo Android Automotive utilizzano questo nodo principale per richiedere il resto dei contenuti utilizzando il metodo onLoadChildren().

Il seguente snippet di codice mostra un'implementazione semplice 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.

Aggiungere la convalida del pacchetto per onGetRoot()

Quando viene effettuata una chiamata al metodo onGetRoot() del tuo servizio, il pacchetto di chiamata passa al servizio informazioni di identificazione. 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 il clientPackageName con la 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 il sistema operativo Android Automotive, accesso ai tuoi contenuti, il tuo servizio deve sempre restituire un valore non nullo quando queste app di sistema chiamano il metodo onGetRoot().BrowserRoot La firma dell'app di sistema Android Automotive OS può variare in base alla marca e al modello dell'auto, quindi devi consentire le connessioni da tutte le app di sistema per supportare in modo affidabile il sistema operativo Android Automotive.

Il seguente snippet di codice mostra come il tuo servizio può convalidare che il pacchetto di chiamate 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 del pacchetto per il metodo onGetRoot() del tuo servizio.

Oltre a consentire le app di sistema, devi consentire all'Assistente Google di collegarsi al tuo MediaBrowserService. Tieni presente che l'Assistente Google ha nomi pacchetti distinti per lo smartphone, che include Android Auto, e per il sistema operativo Android Automotive.

Implementare onLoadChildren()

Dopo aver ricevuto l'oggetto del nodo principale, Android Auto e Android Automotive OS costruiscono un menu di primo livello chiamando onLoadChildren() sull'oggetto del nodo principale per recuperare i relativi elementi secondari. Le app client creano i sottomenu chiamando lo stesso metodo utilizzando oggetti nodo secondari.

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 di ID come token opachi. Quando un'app client vuole accedere a un sottomenu o riprodurre un elemento multimediale, passa il token. È responsabilità della tua app associare il 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 principali visualizzati come schede di navigazione.

Android Auto e il sistema operativo Android Automotive hanno vincoli specifici relativi alla struttura del menu principale. Questi vengono comunicati a MediaBrowserService tramite gli indizi principali, che possono essere letti tramite l'argomento Bundle passato a onGetRoot(). Se segui questi suggerimenti, il sistema mostrerà 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:

Utilizza il seguente codice per leggere gli indizi principali 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 suddividere 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, ti consigliamo di nidificarlo in un elemento browsable principale a causa del valore del suggerimento per i flag supportati.

Oltre ai suggerimenti principali, ci sono altre due linee guida da seguire per assicurarti 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. Mantieni brevi le etichette per ridurre la probabilità che le stringhe vengano troncate.

Mostrare l'artwork dei contenuti multimediali

L'artwork per gli elementi multimediali deve essere passata come URI locale utilizzando ContentResolver.SCHEME_CONTENT o ContentResolver.SCHEME_ANDROID_RESOURCE. Questo URI locale deve risolvere in un bitmap o in un elemento drawable vettoriale nelle risorse dell'applicazione. Per gli oggetti MediaDescriptionCompat che rappresentano gli elementi della gerarchia dei contenuti, passa l'URI tramite setIconUri(). Per gli oggetti MediaMetadataCompat che rappresentano l'elemento attualmente in riproduzione, passa l'URI tramite putString(), utilizzando una delle seguenti chiavi:

I passaggi riportati di seguito descrivono come scaricare l'artwork da un URI web ed esporlo tramite un URI locale. Per un esempio più completo, consulta l'implementazione di openFile() e dei metodi circostanti nell'app di esempio Universal Android Music Player.

  1. Crea un URI content:// corrispondente all'URI web. Il servizio di browser media e la sessione multimediale trasmettono questo URI dei contenuti ad Android Auto e al sistema operativo Android Automotive.

    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();
    }
    
  2. 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. Il seguente snippet di codice 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 Creare 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à ai titoli e ai metadati rispetto alle immagini.

Elementi della griglia

Questo stile di contenuti dà la priorità alle immagini rispetto a titoli e metadati.

Impostare gli stili di contenuti predefiniti

Puoi impostare i valori predefiniti globali per la visualizzazione degli elementi multimediali includendo determinate costanti nel BrowserRootbundle di extra del metodo onGetRoot() del tuo servizio. Android Auto e Android Automotive OS leggono questo bundle e cercano queste costanti per determinare lo stile appropriato.

I seguenti extra possono essere utilizzati come chiavi nel bundle:

Le chiavi possono essere associate 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 dell'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 dell'elenco "categoria". Sono come gli elementi di elenco ordinari, tranne per il fatto che vengono applicati i margini intorno alle icone degli elementi, poiché le icone hanno un aspetto migliore quando sono piccole. Le icone devono essere drawable vettoriali tinzabili. 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". Sono gli stessi elementi della griglia, tranne per il fatto che vengono applicati i margini intorno alle icone degli elementi, poiché le icone hanno un aspetto migliore quando sono piccole. Le icone devono essere drawable vettoriali tinturabili. Questo suggerimento dovrebbe essere fornito solo per gli elementi sfogliabili.

Lo snippet di codice seguente mostra come impostare lo stile dei contenuti predefinito per gli elementi navigabili su griglie e per gli elementi riproducibili su 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 stili dei contenuti per elemento

L'API Content Style ti consente di ignorare lo stile dei contenuti predefinito per gli elementi secondari di qualsiasi elemento multimediale navigabile, nonché per l'elemento multimediale stesso.

Per ignorare il valore predefinito per gli elementi secondari di un elemento multimediale sfogliabile, crea un pacchetto di extra nel MediaDescription dell'elemento multimediale e aggiungi gli stessi suggerimenti indicati 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 browsable dell'elemento.

Per sostituire il valore predefinito per un determinato elemento multimediale in sé, non per i suoi elementi secondari, crea un pacchetto di extra nel 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 elemento MediaItem navigabile che superi lo stile dei contenuti predefinito sia per se stesso che per i suoi elementi secondari. Si imposta come voce dell'elenco della categoria, i suoi elementi secondari sfogliabili come voci dell'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 gli indicatori di titolo

Per raggruppare gli elementi multimediali correlati, utilizza un suggerimento per elemento. Ogni elemento multimediale in un gruppo deve dichiarare un pacchetto di extra nel proprio MediaDescription che include una mappatura con la chiave DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE e un valore di 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 del gruppo di livello inferiore "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*/);
}

L'app deve passare tutti gli elementi multimediali che vuoi raggruppare come un blocco contiguo. Ad esempio, supponiamo che tu voglia visualizzare due gruppi di elementi multimediali, "Brani" e "Album", in questo ordine, e che la tua app trasmetta cinque elementi multimediali nel seguente ordine:

  1. Elemento multimediale A con extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
  2. Elemento multimediale B con extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums")
  3. Elemento multimediale C con extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
  4. Elemento multimediale D con extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
  5. Elemento multimediale E con extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums")

Poiché gli elementi multimediali per il gruppo "Brani" e il gruppo "Album" non vengono tenuti insieme in blocchi contigui, Android Auto e il sistema operativo Android Automotive lo interpretano come i seguenti quattro gruppi:

  • Gruppo 1 denominato "Brani" contenente l'elemento multimediale A
  • Gruppo 2 denominato "Album" contenente l'elemento multimediale B
  • Gruppo 3 denominato "Brani" 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 invece passare gli elementi multimediali nell'ordine seguente:

  1. Elemento multimediale A con extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
  2. Elemento multimediale C con extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
  3. Elemento multimediale D con extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
  4. Elemento multimediale B con extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums")
  5. Elemento multimediale E con extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums")

Mostrare indicatori di metadati aggiuntivi

Puoi includere indicatori di metadati aggiuntivi per fornire informazioni rapide sui contenuti nella struttura ad albero del browser multimediale e durante la riproduzione. All'interno della struttura ad 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 di riproduzione con metadati che identificano il brano e l'artista, nonché un'icona che indica contenuti espliciti.

Figura 4. Visualizzazione di navigazione con un punto per i contenuti non riprodotti sul primo elemento e una barra di avanzamento per i contenuti riprodotti parzialmente sul secondo elemento.

Le seguenti costanti possono essere utilizzate sia negli extra della descrizione MediaItem sia negli extra MediaMetadata:

Le seguenti costanti possono essere utilizzate solo negli extra della descrizione MediaItem:

Per visualizzare gli indicatori che vengono visualizzati mentre l'utente esplora l'albero di navigazione dei contenuti multimediali, crea un bundle di extra che includa una o più di queste costanti e poi passalo al metodo MediaDescription.Builder.setExtras().

Il seguente snippet di codice mostra come visualizzare gli indicatori per un elemento media 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 valori Long per METADATA_KEY_IS_EXPLICIT o EXTRA_DOWNLOAD_STATUS nel MediaMetadataCompat del tuo mediaSession. Non puoi visualizzare gli indicatori DESCRIPTION_EXTRAS_KEY_COMPLETION_STATUS o DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE nella visualizzazione di riproduzione.

Il seguente snippet di codice mostra come indicare che il brano corrente nella visualizzazione di 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 di navigazione 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 di esplorazione. Tuttavia, se un utente continua a riprodurre i contenuti riprodotti parzialmente da Android Auto o Android Automotive OS, l'indicatore diventa poco preciso con il passare del tempo.

Per Android Auto e Android Automotive OS, per mantenere aggiornata la barra di avanzamento, puoi fornire informazioni aggiuntive in MediaMetadataCompat e PlaybackStateCompat per collegare i contenuti in corso agli elementi multimediali nella visualizzazione di navigazione. Affinché l'elemento multimediale abbia una barra di avanzamento in aggiornamento automatico, devono essere soddisfatti i seguenti requisiti:

Il seguente snippet di codice mostra come indicare che l'elemento attualmente in riproduzione è collegato a un elemento nella visualizzazione di esplorazione:

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());

Figura 5. Visualizzazione di riproduzione con un'opzione "Risultati di ricerca" per visualizzare elementi multimediali correlati alla ricerca vocale dell'utente.

La tua app può fornire risultati di ricerca contestuali che vengono mostrati 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 sulle query effettuate in precedenza nella sessione. Per saperne di più, consulta la sezione Supportare le azioni vocali di questa guida.

Per visualizzare risultati di ricerca sfogliabili, includi la chiave costante BROWSER_SERVICE_EXTRAS_KEY_SEARCH_SUPPORTED nel bundle di extra del metodo onGetRoot() del tuo servizio, mappandola al valore booleano true.

Il seguente snippet di codice mostra come attivare 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, sostituisci il metodo onSearch() nel servizio del browser 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 l'affordance "Risultati di ricerca".

Puoi organizzare i risultati di ricerca del metodo onSearch() del tuo servizio utilizzando gli 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 esplorazione personalizzate

Una singola azione di navigazione personalizzata.

Figura 6. Azione di esplorazione personalizzata singola

Le azioni di ricerca personalizzate ti consentono di aggiungere icone e 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 multimediale in diversi modi, ad esempio aggiungendo le azioni "Scarica", "Aggiungi alla coda", "Riproduci radio", "Fai in modo che sia un preferito" o "Rimuovi".

Un menu extra delle azioni di esplorazione personalizzate.

Figura 7. Azione di ricerca personalizzata con overflow

Se sono presenti più azioni personalizzate di quante consentite dall'OEM, all'utente verrà presentato un menu overflow.

Come funzionano?

Ogni azione di navigazione personalizzata è definita da:

  • Un ID azione (un identificatore di stringa univoco)
  • Un'etichetta azione (il testo visualizzato all'utente)
  • Un URI icona di azione (una risorsa drawable vettoriale che può essere colorata)

Definisci un elenco di azioni di navigazione personalizzate a livello globale nell'ambito del tuo BrowseRoot. A questo punto, puoi associare un sottoinsieme di queste azioni ai singoli MediaItem.

Quando un utente interagisce con un'azione di navigazione personalizzata, la tua app riceve un callback in onCustomAction(). Se necessario, puoi gestire l'azione e aggiornare l'elenco di azioni per MediaItem. Questa opzione è utile per le azioni stateful come "Aggiungi a preferiti" e "Scarica". Per le azioni che non richiedono l'aggiornamento, come "Riproduci radio", non è necessario aggiornare l'elenco delle azioni.

Azioni di esplorazione personalizzate in un nodo radice di esplorazione.

Figura 8. Barra degli strumenti Azione di ricerca personalizzata

Puoi anche associare azioni di esplorazione personalizzate a un nodo di esplorazione principale. Queste azioni verranno visualizzate in una barra degli strumenti secondaria sotto la barra degli strumenti principale.

Come implementare le azioni di navigazione personalizzate

Per aggiungere le azioni di navigazione personalizzate al progetto:

  1. Sostituisci due metodi nell'implementazione di MediaBrowserServiceCompat:
  2. Analizza i limiti di azioni in fase di esecuzione:
    • In onGetRoot(), ottieni il numero massimo di azioni consentite per ogni MediaItem utilizzando la chiave BROWSER_ROOT_HINTS_KEY_CUSTOM_BROWSER_ACTION_LIMIT in rootHints Bundle. Un limite pari a 0 indica che la funzionalità non è supportata dal sistema.
  3. 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 oggetti Bundle dell'azione a un elenco.
  4. Aggiungi l'elenco globale a BrowseRoot:
  5. Aggiungi azioni agli oggetti MediaItem:
    • Puoi aggiungere azioni a singoli oggetti MediaItem includendo l'elenco degli ID azione negli extra MediaDescriptionCompat utilizzando la chiave DESCRIPTION_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID_LIST. Questo elenco deve essere un sottoinsieme dell'elenco globale di azioni definito in BrowseRoot.
  6. Gestisci le azioni e restituisci l'avanzamento o i risultati:

Ecco alcune modifiche che puoi apportare in BrowserServiceCompat per iniziare a utilizzare le azioni di navigazione personalizzate.

Sostituire BrowserServiceCompat

Devi sostituire i 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 di 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 icona 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 Bundle Azione di navigazione personalizzata in 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;
}

Aggiungi l'elenco Azione di navigazione personalizzata 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);

Crea il risultato 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, scollega il risultato. result.detach()
  • Pacchetto di risultati di compilazione
    • Messaggio all'utente
      mResultBundle.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_MESSAGE,
                mContext.getString(stringRes))
      
    • Aggiorna elemento(da 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 il nodo Sfoglia
      //Change current browse node to mediaId
      mResultBundle.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_BROWSE_NODE, mediaId);
      
  • In caso di errore, chiama result.sendError(resultBundle).
  • Se l'aggiornamento dell'avanzamento, chiama result.sendProgressUpdate(resultBundle).
  • Al termine, chiama 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 sull'avanzamento e sul risultato della sua azione.

Esempio: Azione di download

Ecco un esempio di come utilizzare questa funzionalità per implementare un'azione di download con tre stati:

  1. Download: questo è lo stato iniziale dell'azione. Quando l'utente seleziona questa azione, puoi sostituirla con "Downloading" e chiamare sendProgressUpdate per aggiornare l'interfaccia utente.
  2. Download in corso: questo stato indica che il download è in corso. Puoi utilizzare questo stato per mostrare all'utente una barra di avanzamento o un altro indicatore.
  3. Scaricato: questo stato indica che il download è stato completato. Al termine del download, puoi sostituire "Download in corso" con "Download completato" e chiamare sendResult con il tasto EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM per indicare che l'elemento deve essere aggiornato. Inoltre, puoi utilizzare la chiave EXTRAS_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 sulla procedura di download e sul relativo stato corrente. 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:

  1. Aggiungi a preferiti: questa azione viene visualizzata per gli elementi che non sono nell'elenco dei preferiti dell'utente. Quando l'utente seleziona questa azione, puoi sostituirla con "Aggiunta ai preferiti" e chiamare sendResult con il tasto EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM per aggiornare l'interfaccia utente.
  2. Aggiunta ai preferiti: questa azione viene visualizzata per gli elementi che si trovano nell'elenco dei preferiti dell'utente. Quando l'utente seleziona questa azione, puoi sostituirla con "Favorito" e chiamare sendResult con il tasto EXTRAS_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 usarle 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 di riproduzione

Android Auto e il sistema operativo Android Automotive inviano i comandi di controllo della riproduzione tramite MediaSessionCompat del tuo servizio. Devi registrare una sessione e implementare i metodi di callback associati.

Registra una sessione multimediale

Nel metodo onCreate() del servizio del browser multimediale, 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 di callback utilizzato per gestire le richieste di controllo della riproduzione. Questo oggetto di callback viene creato fornendo un'implementazione della classe MediaSessionCompat.Callback per la tua app. La sezione successiva illustra come implementare questo oggetto.

Implementare i comandi di riproduzione

Quando un utente richiede la riproduzione di un elemento multimediale dalla tua app, il sistema operativo Android Automotive e Android Auto utilizzano la classe MediaSessionCompat.Callback dell'oggetto MediaSessionCompat della tua app che hanno ottenuto dal servizio di browser multimediale della tua app. Quando un utente vuole controllare la riproduzione dei contenuti, ad esempio mettere in pausa la riproduzione o passare al brano successivo, Android Auto e il sistema operativo Android Automotive richiamano uno dei metodi dell'oggetto callback.

Per gestire la riproduzione dei contenuti, la tua app deve estendere la MediaSessionCompat.Callback classe astratta e implementare i metodi supportati.

Implementa tutti i seguenti metodi di callback pertinenti per il tipo di contenuti offerti dalla tua app:

onPrepare()
Chiamato quando l'origine multimediale viene modificata. Anche Android Automotive OS invoca questo metodo immediatamente dopo l'avvio. L'app multimediale deve implementare questo metodo.
onPlay()
Chiamato se l'utente sceglie di riprodurre senza scegliere un elemento specifico. L'app deve riprodurre i contenuti predefiniti o, se la riproduzione è stata messa in pausa con onPause(), deve riprenderla.

Nota:l'app non deve avviare automaticamente la riproduzione di musica quando Android Automotive OS o Android Auto si connettono al servizio del browser multimediale. Per ulteriori informazioni, consulta la sezione sull'impostazione dello stato di riproduzione iniziale.

onPlayFromMediaId()
Chiamato quando l'utente sceglie di riprodurre un elemento specifico. Al metodo viene passato l'ID assegnato dal servizio di browser dei contenuti all'elemento multimediale nella gerarchia dei contenuti.
onPlayFromSearch()
Chiamato quando l'utente sceglie di riprodurre contenuti da una query di ricerca. L'app deve fare una scelta appropriata in base alla stringa di ricerca passata.
onPause()
Chiamato quando l'utente sceglie di mettere in pausa la riproduzione.
onSkipToNext()
Chiamato quando l'utente sceglie di passare all'elemento successivo.
onSkipToPrevious()
Chiamato quando l'utente sceglie di passare all'elemento precedente.
onStop()
Chiamato quando l'utente sceglie di interrompere la riproduzione.

Sostituisci questi metodi nella tua app per fornire le funzionalità desiderate. Non è necessario implementare un metodo se la relativa funzionalità non è supportata dall'app. Per esempio, se la tua app riproduce un live streaming, ad esempio una trasmissione sportiva, non è necessario implementare il metodo onSkipToNext(). Puoi utilizzare l'implementazione predefinita di onSkipToNext().

La tua app non ha bisogno di alcuna 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 come 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 riprodurli tramite gli altoparlanti dell'auto.

Per ulteriori informazioni sulla riproduzione di contenuti audio, consulta la panoramica di MediaPlayer, la panoramica delle app audio e la panoramica di ExoPlayer.

Impostare 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 mostrata all'utente, ma non è obbligatoria. Per farlo, chiama i metodi setQueue() e setQueueTitle(), attiva l'azione ACTION_SKIP_TO_QUEUE_ITEM e definisci il callback onSkipToQueueItem().

Aggiungi anche il supporto per l'icona In riproduzione, che indica i contenuti in riproduzione. A tale scopo, chiama il metodo setActiveQueueItemId() e passa l'ID dell'elemento attualmente in riproduzione nella coda. Devi aggiornare setActiveQueueItemId() ogni volta che si verifica una modifica alla coda.

Android Auto e Android Automotive OS mostrano i pulsanti per ogni azione abilitata nonché la coda di riproduzione. Quando si fa clic sui pulsanti, il sistema richiama il callback corrispondente da MediaSessionCompat.Callback.

Prenotare lo 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 mostrare le azioni personalizzate che crei.

Se non vuoi riempire questi spazi con azioni personalizzate, puoi prenotarli in modo che Android Auto e il sistema operativo Android Automotive lascino lo spazio vuoto ogni volta che la tua app non supporta la funzione corrispondente. A tale scopo, chiama il metodo setExtras() con un bundle di extra contenente 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 il valore booleano true per i relativi valori.

Impostare PlaybackState iniziale

Poiché Android Auto e il sistema operativo Android Automotive comunicano con il servizio del browser multimediale, 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 tuo servizio di browser multimediale. Utilizza invece 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 il valore PlaybackStateCompat iniziale della sessione multimediale su STATE_STOPPED, STATE_PAUSED, STATE_NONE o STATE_ERROR.

Le sessioni multimediali in Android Auto e nel sistema operativo Android Automotive durano solo per la durata della guida, pertanto gli utenti le avviano e le interrompono di frequente. Per favorire un'esperienza fluida tra le varie unità, 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 altre azioni supportate dalla tua app multimediale. Se lo spazio lo consente (e non è riservato), Android aggiunge le azioni personalizzate ai controlli per i trasporti. In caso contrario, le azioni personalizzate vengono visualizzate nel menu extra. Le azioni personalizzate vengono visualizzate nell'ordine in cui vengono aggiunte al PlaybackStateCompat.

Utilizza le azioni personalizzate per fornire un comportamento distinto dalle azioni standard. Non utilizzarle 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 un canale 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 superando il 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 dell'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, la risorsa icona passata a CustomAction.Builder verrà sostituita, consentendo ai componenti di sistema di eseguire il rendering della tua 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à, pertanto le icone che fornisci devono essere disegnabili in vettore. Un elemento drawable vettoriale ti consente di ridimensionare gli asset senza perdere i dettagli. Un elemento visibile vettoriale semplifica anche l'allineamento di bordi e angoli ai confini dei pixel a risoluzioni inferiori.

Se un'azione personalizzata è basata sugli stati, 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.

Fornire 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 mostri che l'azione è disattivata.

Figura 6. Esempi di icone di azioni personalizzate non in linea con lo stile.

Indica il formato audio

Per indicare che i contenuti multimediali in riproduzione utilizzano un formato audio speciale, puoi specificare le icone da visualizzare 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 extra dell'elemento multimediale in riproduzione (trasmesso a MediaSession.setMetadata()). Assicurati di impostare entrambi gli extra per adattarti a diversi layout.

Inoltre, puoi impostare l'extra KEY_IMMERSIVE_AUDIO per indicare agli OEM di auto che si tratta di audio immersivo e che devono fare molta attenzione quando decidono se applicare effetti audio che potrebbero interferire con i contenuti immersivi.

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 agli elementi correlati, ad esempio ad altri brani dello stesso artista, ad altre puntate del podcast e così via. Se l'auto supporta questa funzionalità, gli utenti possono toccare il link per accedere ai contenuti.

Per aggiungere i link, configura i metadati KEY_SUBTITLE_LINK_MEDIA_ID (per il link dal sottotitolo) o KEY_DESCRIPTION_LINK_MEDIA_ID (per il link dalla descrizione). Per maggiori dettagli, consulta la documentazione di riferimento per questi campi dei metadati.

Supportare le azioni vocali

L'app multimediale deve supportare le azioni vocali per offrire ai conducenti un'esperienza sicura e pratica che riduca al minimo le distrazioni. Ad esempio, se la tua app riproduce 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 query facendo clic sui pulsanti appropriati sul volante o pronunciando le hotword "Hey Google".

Quando Android Auto o Android Automotive OS rileva e interpreta un'azione vocale, questa viene inviata all'app tramite onPlayFromSearch(). Al ricevimento 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, tra cui genere, artista, album, nome del brano, stazione radio o playlist. Quando sviluppi 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 elementi aggiuntivi al parametro extras. Puoi 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, l'app potrebbe scegliere di avviare un brano ascoltato di recente o appena suggerito.

Se una ricerca non può essere elaborata rapidamente, non bloccare in onPlayFromSearch(). Imposta invece lo stato di riproduzione su STATE_CONNECTING ed esegui la ricerca su un thread asincrono.

Una volta avviata la riproduzione, ti consigliamo 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 compilare la coda con la tracklist dell'album. Valuta anche la possibilità di implementare il supporto per i risultati di ricerca sfogliabili, in modo che un utente possa scegliere un canale diverso che corrisponda alla sua query.

Oltre alle query "play", Android Auto e il sistema operativo Android Automotive riconoscono le query vocali per controllare la riproduzione, ad esempio "metti in pausa la musica" e "brano successivo", e li associano ai callback della sessione multimediale appropriati, come onPause() e onSkipToNext().

Per un esempio dettagliato su come implementare le azioni di riproduzione con comandi vocali nella tua app, consulta Assistente Google e app multimediali.

Implementa salvaguardie per evitare distrazioni

Poiché lo smartphone di un utente è connesso agli altoparlanti dell'auto durante l'utilizzo di Android Auto, devi adottare ulteriori precauzioni per evitare di distrarre il conducente.

Eliminare le sveglie in auto

Le app multimediali di 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. Anche una sveglia programmata dall'utente dall'app multimediale non deve avviare la riproduzione di musica tramite gli altoparlanti dell'auto.

Per soddisfare questo requisito, la tua app può utilizzare CarConnection come indicatore prima di riprodurre qualsiasi audio. L'app può verificare se lo smartphone è in proiezione sullo schermo di un'auto osservando il valore LiveData per il tipo di connessione all'auto e controllando se è uguale a CONNECTION_TYPE_PROJECTION.

Se lo smartphone dell'utente è in modalità di proiezione, 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 gli annunci 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 pubblicazione di un annuncio, è fonte di distrazione mostrare una notifica all'utente. Per impedire ad Android Auto di visualizzare una notifica in questo caso, devi impostare la chiave dei metadati multimediali METADATA_KEY_IS_ADVERTISEMENT su METADATA_VALUE_ATTRIBUTE_PRESENT, come mostrato nello snippet di codice seguente:

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 riscontra un errore, imposta lo stato di riproduzione su STATE_ERROR e fornisci un messaggio di errore utilizzando il metodo setErrorMessage(). Consulta PlaybackStateCompat per un elenco dei codici di errore che puoi utilizzare per impostare il messaggio di errore. I messaggi di errore devono essere rivolti agli utenti e localizzati con la lingua corrente dell'utente. Android Auto e il sistema operativo Android Automotive possono quindi mostrare il messaggio di errore all'utente.

Ad esempio, se i contenuti non sono disponibili nella regione corrente 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 Telefono per risolvere un errore, fornisci queste informazioni all'utente nel messaggio. Ad esempio, il messaggio di errore potrebbe indicare "Accedi a [nome dell'app]" anziché "Accedi".

Altre risorse