Creare app multimediali per auto

Android Auto e il sistema operativo Android Automotive ti consentono di mostrare i contenuti delle tue app multimediali agli utenti nella loro 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 browser multimediale, possano rilevare e visualizzare i tuoi contenuti.

Questa guida presuppone che tu disponga già di un'app multimediale che riproduce l'audio su uno smartphone e che quest'ultima sia conforme all'architettura delle app multimediali di Android.

Questa guida descrive i componenti obbligatori di MediaBrowserService e MediaSession necessari alla tua app per funzionare sul sistema operativo Android Auto o Android Automotive. Dopo aver completato l'infrastruttura multimediale di base, puoi aggiungere il supporto per Android Auto e il supporto per il sistema operativo Android Automotive alla tua app multimediale.

Prima di iniziare

  1. Consulta la documentazione relativa all'API Android Media.
  2. Consulta Creare app multimediali per indicazioni sulla progettazione.
  3. Esamina i termini e i concetti chiave elencati in questa sezione.

Termini e concetti chiave

Servizio browser multimediale
Un servizio Android implementato dalla tua app multimediale che è conforme all'API MediaBrowserServiceCompat. La tua app usa questo servizio per esporre i suoi contenuti.
Browser multimediale
Un'API utilizzata dalle app multimediali per scoprire i servizi browser multimediali e visualizzare i relativi contenuti. Android Auto e il sistema operativo Android Automotive utilizzano un browser multimediale per trovare il servizio browser multimediale della tua app.
Elemento multimediale

Il browser multimediale organizza i propri contenuti in una struttura 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 nella struttura ad albero dei contenuti e che ha elementi secondari. Ad esempio, l'elemento rappresenta un album e i relativi brani secondari sono i brani dell'album.

Un elemento multimediale sfogliabile e riproducibile è come una playlist. Puoi selezionare l'elemento stesso per giocare a tutti i suoi elementi secondari oppure sfogliare i relativi elementi secondari.

Ottimizzate per il veicolo

Un'attività per un'app del sistema operativo Android Automotive che rispetta le linee guida per la progettazione del sistema operativo Android Automotive. L'interfaccia per queste attività non è tracciata dal sistema operativo Android Automotive, quindi devi assicurarti che l'app sia conforme alle linee guida di progettazione. In genere, sono inclusi target dei tocchi e dimensioni dei caratteri più grandi, supporto per le modalità diurna e notturna e rapporti di contrasto più elevati.

È possibile visualizzare le interfacce utente ottimizzate per i veicoli solo quando le limitazioni dell'esperienza utente delle auto (CUXR) non sono in vigore, perché queste interfacce possono richiedere attenzione o interazione prolungata da parte dell'utente. I CUXR non sono attivi quando l'auto è ferma o parcheggiata, ma sono sempre attivi quando l'auto è in movimento.

Non è necessario progettare attività per Android Auto, perché Android Auto dispone di un'interfaccia ottimizzata per il veicolo utilizzando le informazioni del servizio browser multimediale.

Configura i file manifest dell'app

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

Dichiara il servizio del browser multimediale

Sia Android Auto che il sistema operativo Android Automotive si connettono alla tua app tramite il servizio browser multimediale per sfogliare gli elementi multimediali. Dichiara il servizio del tuo browser multimediale nel file manifest per consentire ad Android Auto e al sistema operativo Android Automotive di rilevare il servizio e di connettersi alla tua app.

Il seguente snippet di codice mostra come dichiarare il servizio del browser multimediale nel file manifest. Includi questo codice nel file manifest per il modulo del sistema operativo Android Automotive e nel file manifest dell'app Telefono.

<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 delle app che Android Auto e il sistema operativo Android Automotive possono utilizzare per rappresentare la tua app nell'interfaccia utente di sistema. Sono richiesti due tipi di icone:

  • Icona Avvio app
  • Icona di attribuzione

Icona Avvio app

L'icona in Avvio applicazioni rappresenta l'app nell'interfaccia utente di sistema, ad esempio in Avvio app e nella barra delle icone. Puoi specificare di voler utilizzare l'icona dell'app mobile per rappresentare l'app di contenuti multimediali dell'auto utilizzando la seguente dichiarazione del file 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 del 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, ad esempio nelle schede multimediali. Valuta la possibilità di riutilizzare la piccola icona usata per le notifiche. L'icona deve essere monocromatica. Puoi specificare un'icona che viene utilizzata per rappresentare la tua app utilizzando la seguente dichiarazione del file manifest:

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

Crea il servizio del browser multimediale

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

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

Puoi utilizzare il tuo servizio browser multimediale per consentire ad altri client di accedere ai contenuti multimediali dalla tua app. Questi client multimediali potrebbero essere altre app sul telefono di un utente o altri client remoti.

Flusso di lavoro del servizio browser multimediale

Questa sezione descrive in che modo il sistema operativo Android Automotive e Android Auto interagiscono con il servizio browser multimediale durante un tipico flusso di lavoro dell'utente.

  1. L'utente avvia la tua app sul sistema operativo Android Automotive o su Android Auto.
  2. Il sistema operativo Android Automotive o Android Auto contatta il servizio 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 root non viene visualizzato, ma viene utilizzato per recuperare altri contenuti dall'app.
  4. Il sistema operativo Android Automotive o Android Auto chiama il metodo onLoadChildren() del tuo servizio per recuperare gli elementi secondari dell'elemento multimediale principale. Il sistema operativo Android Automotive e Android Auto mostrano questi elementi multimediali come elementi di contenuti di primo livello. Consulta Struttura del menu principale in questa pagina per ulteriori informazioni su ciò che il sistema si aspetta a questo livello.
  5. Se l'utente seleziona un elemento multimediale sfogliabile, il metodo onLoadChildren() del tuo servizio viene richiamato di nuovo per recuperare gli elementi secondari della voce di menu selezionata.
  6. Se l'utente seleziona un elemento multimediale riproducibile, il sistema operativo Android Automotive o Android Auto chiama il metodo di callback della sessione multimediale appropriato per eseguire quell'azione.
  7. Se la funzionalità è supportata dalla tua app, l'utente può anche cercare i tuoi contenuti. In questo caso, il sistema operativo Android Automotive o Android Auto chiama il metodo onSearch() del tuo servizio.

Crea la tua gerarchia dei contenuti

Android Auto e il sistema operativo Android Automotive chiamano il servizio browser multimediale della tua app per scoprire i contenuti disponibili. Per supportare questa operazione, devi implementare due metodi nel servizio browser multimediale: onGetRoot() e onLoadChildren()

Implementazione onGetRoot

Il metodo onGetRoot() del tuo servizio restituisce informazioni sul nodo radice della gerarchia dei tuoi contenuti. Android Auto e il sistema operativo Android Automotive utilizzano questo nodo radice per richiedere gli altri tuoi 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 di 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 per le chiamate trasmette le informazioni identificative al servizio. Il servizio può utilizzare queste informazioni per stabilire 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 non è possibile verificare il pacchetto, restituisci null per negare l'accesso ai tuoi contenuti.

Per consentire alle app di sistema, ad esempio Android Auto e il sistema operativo Android Automotive, di accedere ai tuoi contenuti, il tuo servizio deve restituire sempre un valore BrowserRoot non null quando queste app di sistema chiamano il metodo onGetRoot(). La firma dell'app di sistema del sistema operativo Android Automotive può variare a seconda della marca e del modello dell'auto, quindi devi consentire le connessioni da tutte le app di sistema per supportare in modo efficace il sistema operativo Android Automotive.

Il seguente snippet di codice mostra come il servizio può confermare che il pacchetto di chiamata 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 di Universal Android Music Player su GitHub. Consulta questa classe per un esempio più dettagliato su 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 connettersi a MediaBrowserService. Tieni presente che l'Assistente Google ha nomi di pacchetti separati per lo smartphone, che è Android Auto, e per il sistema operativo Android Automotive.

Implementazione onLoadChildren()

Dopo aver ricevuto l'oggetto del nodo principale, Android Auto e il sistema operativo Android Automotive creano un menu di primo livello chiamando onLoadChildren() sull'oggetto del nodo radice per recuperare i relativi figli. Le app client creano sottomenu richiamando questo stesso metodo tramite oggetti dei nodi secondari.

Ogni nodo nella gerarchia dei contenuti è rappresentato da un oggetto MediaBrowserCompat.MediaItem. Ciascuno di questi elementi multimediali viene identificato da una stringa ID univoca. Le app client trattano queste stringhe ID come token opachi. Quando un'app client vuole passare a un sottomenu o riprodurre un elemento multimediale, trasmette il token. L'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 di 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 riguardo alla struttura del menu principale. Questi vengono comunicati a MediaBrowserService tramite i suggerimenti radice, che possono essere letti tramite l'argomento Bundle passato in onGetRoot(). Seguendo questi suggerimenti, il sistema potrà visualizzare in modo ottimale i contenuti principali come schede di navigazione. Se non segui questi suggerimenti, alcuni contenuti principali potrebbero essere eliminati o resi meno rilevabili dal sistema. Ecco due suggerimenti:

Utilizza il seguente codice per leggere i suggerimenti 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 suddividere la logica della 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 del sistema operativo Android Auto e del sistema operativo Android Automotive. Ad esempio, se normalmente mostri un elemento principale riproducibile, potresti nidificarlo sotto un elemento root consultabile per via del valore del suggerimento dei flag supportati.

Oltre ai suggerimenti principali, esistono 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. Utilizzare etichette brevi riduce la possibilità che le stringhe vengano troncate.

Artwork dei media display

L'artwork per gli elementi multimediali deve essere trasmesso come URI locale utilizzando ContentResolver.SCHEME_CONTENT o ContentResolver.SCHEME_ANDROID_RESOURCE. Questo URI locale deve risolversi in una bitmap o in un vettore di trascinamento nelle risorse dell'applicazione. Per gli oggetti MediaDescriptionCompat che rappresentano elementi nella gerarchia dei contenuti, trasmetti l'URI attraverso setIconUri(). Per gli oggetti MediaMetadataCompat che rappresentano l'elemento attualmente in riproduzione, trasmetti l'URI tramite putString() utilizzando una delle seguenti chiavi:

I passaggi seguenti descrivono come scaricare la grafica 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 di Universal Android Music Player.

  1. Crea un URI content:// corrispondente all'URI web. Il servizio browser multimediale e la sessione multimediale passano questo URI di questo contenuto al sistema operativo Android Auto e 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(), verifica se esiste un file per l'URI corrispondente. In caso contrario, scarica il file immagine e memorizzalo nella cache. 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 ulteriori dettagli sui fornitori di contenuti, consulta la sezione Creazione di un fornitore di contenuti.

Applicare stili di contenuti

Dopo aver creato la gerarchia dei contenuti utilizzando elementi sfogliabili o riproducibili, puoi applicare gli stili dei contenuti per determinare l'aspetto di questi elementi nell'auto.

Puoi utilizzare i seguenti stili di contenuti:

Voci di elenco

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

Elementi della griglia

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

Impostare stili di contenuti predefiniti

Puoi impostare valori predefiniti globali per la modalità di visualizzazione dei tuoi elementi multimediali includendo determinate costanti nel bundle extra BrowserRoot del metodo onGetRoot() del tuo servizio. Android Auto e il sistema operativo Android Automotive 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 mappate ai seguenti valori della costante di numero intero per influenzare la presentazione di questi elementi:

  • DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM: gli elementi corrispondenti vengono presentati come voci 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 voci di elenco di "categoria". Sono uguali ai normali elementi dell'elenco, ad eccezione del 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 disegnate a colori. 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 di "categoria". Sono uguali ai normali elementi della griglia, ad eccezione del 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 disegnate a colori. 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 su griglie e gli elementi riproducibili negli 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);
}

Imposta stili dei contenuti per singolo elemento

L'API Content Style ti consente di sostituire lo stile dei contenuti predefinito per gli elementi secondari di qualsiasi elemento multimediale sfogliabile, oltre che per qualsiasi elemento multimediale stesso.

Per eseguire l'override del valore predefinito per i dispositivi secondari di un elemento multimediale sfogliabile, crea un bundle extra in MediaDescription dell'elemento multimediale e aggiungi gli stessi suggerimenti menzionati in precedenza. DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE si applica ai bambini giocabili dell'elemento, mentre DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE si applica ai figli visualizzabili dell'elemento.

Per eseguire l'override del valore predefinito per un determinato elemento multimediale in sé, non per i relativi elementi secondari, crea un bundle extra nella sezione 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 sfogliabile che sostituisca lo stile dei contenuti predefinito sia per se stesso sia per i relativi elementi secondari. Si definisce come elemento di un elenco di categorie, i relativi elementi secondari sfogliabili come elementi di elenco e gli 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 elementi utilizzando i suggerimenti per il titolo

Per raggruppare gli elementi multimediali correlati, utilizza un suggerimento specifico per ogni elemento. Ogni elemento multimediale in un gruppo deve dichiarare un bundle extra nel criterio MediaDescription che include una mappatura con la chiave DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE e uno stesso valore di stringa. Localizza questa stringa, che viene utilizzata come titolo del gruppo.

Il seguente snippet di codice mostra come creare un elemento MediaItem con un'intestazione di sottogruppo di "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 trasmettere tutti gli elementi multimediali che vuoi raggruppare come blocco contiguo. Ad esempio, supponi di voler visualizzare due gruppi di elementi multimediali, "Brani" e "Album" in questo ordine, e la tua app passa in 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 del gruppo "Brani" e del gruppo "Album" non sono tenuti insieme in blocchi contigui, Android Auto e il sistema operativo Android Automotive lo interpretano come i seguenti quattro gruppi:

  • Gruppo 1 chiamato "Brani" contenente l'elemento multimediale A
  • Gruppo 2 denominato "Album" contenente l'elemento multimediale B
  • Gruppo 3 chiamato "Brani" contenente gli elementi multimediali C e D
  • Gruppo 4 denominato "Album" contenente l'elemento multimediale E

Per visualizzare questi elementi in due gruppi, la tua app deve passare gli elementi multimediali nel seguente ordine:

  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")

Visualizza indicatori di metadati aggiuntivi

Puoi includere indicatori di metadati aggiuntivi per fornire informazioni di riepilogo sui contenuti nella struttura ad albero del browser multimediale e durante la riproduzione. All'interno dell'albero di esplorazione, Android Auto e il sistema operativo Android Automotive leggono gli extra associati a un articolo e cercano alcune costanti per stabilire quali indicatori visualizzare. Durante la riproduzione di contenuti multimediali, Android Auto e il sistema operativo Android Automotive leggono i metadati della sessione multimediale e cercano alcune 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 Sfoglia con un punto per i contenuti non riprodotti sul primo elemento e una barra di avanzamento per i contenuti riprodotti parzialmente sul secondo.

Le seguenti costanti possono essere utilizzate in entrambi gli extra di descrizione MediaItem e MediaMetadata:

Le seguenti costanti possono essere utilizzate solo negli extra delle descrizioni di MediaItem:

Per visualizzare gli indicatori che vengono visualizzati mentre l'utente esplora la struttura di navigazione dei contenuti multimediali, crea un bundle extra 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 completo 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 della 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());

Aggiorna la barra di avanzamento nella visualizzazione Sfoglia durante la riproduzione dei contenuti.

Come accennato in precedenza, puoi utilizzare l'ulteriore 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 dal sistema operativo Android Auto o Android Automotive, l'indicatore diventa impreciso con il passare del tempo.

Affinché Android Auto e il sistema operativo Android Automotive mantengano aggiornata la barra di avanzamento, puoi fornire informazioni aggiuntive in MediaMetadataCompat e PlaybackStateCompat per collegare i contenuti in corso agli elementi multimediali nella visualizzazione Sfoglia. Affinché l'elemento multimediale abbia una barra di avanzamento che si aggiorna automaticamente, 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 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());

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

L'app può fornire risultati di ricerca contestuali da mostrare agli utenti quando avviano una query di ricerca. Android Auto e il sistema operativo Android Automotive mostrano questi risultati tramite le interfacce delle query di ricerca o le offerte che si basano sulle query eseguite in precedenza nella sessione. Per scoprire di più, consulta la sezione Supportare le azioni vocali in questa guida.

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

Il seguente snippet di codice mostra come attivare il supporto con il 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 i risultati di ricerca, esegui l'override del metodo onSearch() nel servizio browser multimediale. Android Auto e il sistema operativo Android Automotive inoltrano i termini di ricerca dell'utente a questo metodo ogni volta che l'utente richiama un'interfaccia per le query di ricerca o l'offerta "Risultati di ricerca".

Puoi organizzare i risultati di ricerca provenienti dal metodo onSearch() del tuo servizio utilizzando title_items per renderli più sfogliabili. Ad esempio, se l'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. Singola azione di consultazione personalizzata

Le azioni di esplorazione personalizzate ti consentono di aggiungere icone ed etichette personalizzate agli oggetti MediaItem della tua app nell'app multimediale dell'auto, nonché di gestire le interazioni degli utenti con queste azioni. Ciò consente di estendere la funzionalità dell'app multimediale in vari modi, ad esempio aggiungendo azioni "Scarica", "Aggiungi alla coda", "Riproduci la radio", "Preferiti" o "Rimuovi".

Un menu extra delle azioni di navigazione personalizzate.

Figura 7. Overflow dell'azione di navigazione personalizzata

Se ci sono più azioni personalizzate di quelle consentite dall'OEM, all'utente verrà presentato un menu extra.

Come funzionano?

Ogni azione di navigazione personalizzata è definita con:

  • Un ID azione (un identificatore di stringa univoco)
  • Un'etichetta dell'azione (il testo mostrato all'utente)
  • URI dell'icona di azione (disegnabile vettoriale che può essere tinto)

Devi definire un elenco di azioni di navigazione personalizzate a livello globale come parte del tuo BrowseRoot. Poi puoi collegare 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(). Puoi quindi gestire l'azione e aggiornare l'elenco di azioni per MediaItem, se necessario. Questo è utile per azioni stateful come "Preferiti" e "Scarica". Per le azioni che non richiedono un aggiornamento, come "Fammi ascoltare la radio", non è necessario aggiornare l'elenco delle azioni.

Azioni di esplorazione personalizzate in una radice del nodo di consultazione.

Figura 8. Barra degli strumenti delle azioni di esplorazione personalizzate

Puoi anche collegare le azioni di esplorazione personalizzate alla radice del nodo di esplorazione. 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 azioni di navigazione personalizzate al tuo progetto, procedi nel seguente modo:

  1. Esegui l'override di due metodi nell'implementazione di MediaBrowserServiceCompat:
  2. Analizza i limiti di azioni in fase di runtime:
    • In onGetRoot(), ottieni il numero massimo di azioni consentite per ogni MediaItem utilizzando la chiave BROWSER_ROOT_HINTS_KEY_CUSTOM_BROWSER_ACTION_LIMIT nella rootHints Bundle. Un limite pari a 0 indica che la funzionalità non è supportata dal sistema.
  3. Crea l'elenco globale di azioni di esplorazione 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 di 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 di 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 delle azioni che hai 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 esplorazione personalizzate.

Esegui l'override di 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

Dovresti controllare 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)
}

Crea un'azione di navigazione personalizzata

Ogni azione deve essere pacchettizzata 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>")
    

Aggiungi azioni di navigazione personalizzate a Parceable ArrayList

Aggiungi tutti gli oggetti Bundle dell'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 Azioni di navigazione personalizzate alla directory principale 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;
}

Aggiungi 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 onCustomAction risultato

  • Analizza l'ID media di 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 risultati asincroni scollega il risultato. result.detach()
  • Bundle di risultati build
    • 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 di 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 esplorazione
      //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 aggiorni l'avanzamento, chiama il numero result.sendProgressUpdate(resultBundle).
  • Termina chiamando il numero result.sendResult(resultBundle).

Aggiorna stato 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 delle sue azioni.

Esempio: azione di download

Di seguito è riportato un esempio di come utilizzare questa funzionalità per implementare un'azione di download con tre stati:

  1. Download: lo stato iniziale dell'azione. Quando l'utente seleziona questa azione, puoi scambiarla con "Download" 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 una barra di avanzamento o un altro indicatore all'utente.
  3. Scaricato: questo stato indica che il download è stato completato. Al termine del download, puoi scambiare l'opzione "Download" con "Scaricato" e chiamare sendResult con la chiave 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 all'utente un messaggio di operazione riuscita.

Questo approccio consente di fornire all'utente un feedback chiaro sul processo di download e sul suo stato attuale. Puoi aggiungere ulteriori dettagli con le icone che mostrano gli stati di download: 25%, 50% e 75%.

Esempio: Azione preferita

Un altro esempio è un'azione preferita con due stati:

  1. Preferiti: questa azione viene visualizzata per gli elementi che non sono presenti nell'elenco dei preferiti dell'utente. Quando l'utente seleziona questa azione, puoi scambiarla con "Aggiunta a preferiti" e chiamare sendResult con la chiave EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM per aggiornare l'interfaccia utente.
  2. Preferito: questa azione viene visualizzata per gli elementi che si trovano nell'elenco dei preferiti dell'utente. Quando l'utente seleziona questa azione, puoi scambiarla con "Preferiti" 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 di gestire i loro elementi preferiti.

Questi esempi mostrano la flessibilità delle azioni di navigazione personalizzate e il modo in cui puoi utilizzarle per implementare una varietà di funzionalità con feedback in tempo reale per una migliore esperienza utente nell'app multimediale dell'auto.

Per un esempio completo di implementazione di questa funzionalità, puoi fare riferimento al progetto TestMediaApp.

Attiva il controllo di riproduzione

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

Registrare una sessione multimediale

Nel metodo onCreate() del tuo servizio 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 callback che viene utilizzato per gestire le richieste di controllo della riproduzione. Per creare questo oggetto callback, fornisci un'implementazione della classe MediaSessionCompat.Callback per la tua app. Nella sezione successiva viene spiegato come implementare questo oggetto.

Implementare 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 dell'app che ha ottenuto dal servizio browser multimediale della tua app. Quando un utente vuole controllare la riproduzione dei contenuti, ad esempio mettendo in pausa la riproduzione o passando alla traccia successiva, Android Auto e il sistema operativo Android Automotive richiamano uno dei metodi dell'oggetto callback.

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

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

onPrepare()
Richiamato quando l'origine multimediale viene modificata. Anche il sistema operativo Android Automotive richiama questo metodo subito dopo l'avvio. L'app multimediale deve implementare questo metodo.
onPlay()
Richiamato se l'utente sceglie la modalità di riproduzione senza scegliere un elemento specifico. Nell'app devono essere riprodotti i contenuti predefiniti oppure, se la riproduzione è stata messa in pausa con onPause(), la riproduzione riprenderà.

Nota: l'app non dovrebbe avviare automaticamente la riproduzione della musica quando il sistema operativo Android Automotive o Android Auto si connettono al servizio browser multimediale. Per maggiori informazioni, consulta la sezione relativa all'impostazione dello stato di riproduzione iniziale.

onPlayFromMediaId()
Richiamato quando l'utente sceglie di riprodurre un elemento specifico. Il metodo viene trasmesso l'ID che il servizio del browser multimediale ha assegnato all'elemento multimediale nella gerarchia dei contenuti.
onPlayFromSearch()
Richiamato quando l'utente sceglie di riprodurre da una query di ricerca. L'app deve fare una scelta appropriata in base alla stringa di ricerca trasmessa.
onPause()
Richiamato quando l'utente sceglie di mettere in pausa la riproduzione.
onSkipToNext()
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 è necessario 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 è necessario implementare il metodo onSkipToNext(). Puoi utilizzare invece l'implementazione predefinita di onSkipToNext().

L'app non richiede una logica speciale per riprodurre i contenuti dagli altoparlanti dell'auto. Quando l'app riceve una richiesta per riprodurre contenuti, può riprodurre l'audio nello stesso modo in cui riproduce i contenuti tramite gli altoparlanti o le cuffie dell'utente. Android Auto e il sistema operativo Android Automotive inviano automaticamente i contenuti audio al sistema dell'auto in modo che vengano riprodotti sugli altoparlanti dell'auto.

Per ulteriori informazioni sulla riproduzione di contenuti audio, consulta le pagine Panoramica di MediaPlayer, Panoramica app Audio e Panoramica di ExoPlayer.

Impostare azioni di riproduzione standard

Android Auto e il sistema operativo Android Automotive mostrano i controlli di riproduzione in base alle azioni attivate nell'oggetto PlaybackStateCompat.

Per impostazione predefinita, l'app deve supportare le seguenti azioni:

La tua app può anche supportare le seguenti azioni se sono pertinenti ai relativi contenuti:

Inoltre, puoi creare una coda di riproduzione che può essere visualizzata dall'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().

Inoltre, aggiungi il supporto per l'icona Ora in riproduzione, che indica i contenuti in riproduzione. Per farlo, chiama il metodo setActiveQueueItemId() e trasmetti l'ID dell'elemento in coda attualmente in riproduzione. Devi aggiornare setActiveQueueItemId() ogni volta che viene apportata una modifica alla coda.

I pulsanti di visualizzazione di Android Auto e del sistema operativo Android Automotive per ogni azione abilitata e la coda di riproduzione. Quando viene fatto clic sui pulsanti, il sistema richiama il callback corrispondente da MediaSessionCompat.Callback.

Prenota spazio inutilizzato

Android Auto e il sistema operativo Android Automotive riservano spazio nella UI 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 il sistema operativo Android Automotive utilizzano lo spazio per visualizzare le azioni personalizzate che crei.

Se non vuoi riempire questi spazi con azioni personalizzate, puoi prenotarle 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 questo scopo, chiama il metodo setExtras() con un bundle di extra che contiene le costanti che corrispondono alle funzioni prenotate. 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 del pacchetto e usa il valore booleano true come valore.

Imposta lo stato di riproduzione iniziale

Mentre 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 riproduzione della musica non dovrebbe iniziare automaticamente quando il sistema operativo Android Automotive o Android Auto si connettono al servizio browser multimediale. Affidati invece ad Android Auto e al sistema operativo Android Automotive per riprendere o avviare la riproduzione in base allo stato dell'auto o alle azioni degli utenti.

A questo scopo, imposta il valore iniziale PlaybackStateCompat della tua 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 del viaggio, quindi gli utenti avviano e interrompono spesso queste sessioni. Per promuovere un'esperienza fluida tra i Drive, è necessario tenere 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, 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 è prenotato), 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 utilizzarle per sostituire o duplicare 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

stateBuilder.addCustomAction(
    PlaybackStateCompat.CustomAction.Builder(
        CUSTOM_ACTION_START_RADIO_FROM_MEDIA,
        resources.getString(R.string.start_radio_from_media),
        startRadioFromMediaIcon
    ).run {
        setExtras(customActionExtras)
        build()
    }
)

Java

stateBuilder.addCustomAction(
    new PlaybackStateCompat.CustomAction.Builder(
        CUSTOM_ACTION_START_RADIO_FROM_MEDIA,
        resources.getString(R.string.start_radio_from_media),
        startRadioFromMediaIcon)
    .setExtras(customActionExtras)
    .build());

Per un esempio più dettagliato di questo metodo, consulta il metodo setCustomAction() nell'app di esempio di Universal Android Music Player su GitHub.

Dopo aver creato l'azione personalizzata, la sessione multimediale può rispondere all'azione sostituendo 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 di Universal Android Music Player su GitHub.

Icone per le azioni personalizzate

Ogni azione personalizzata che crei richiede una risorsa icona. Le app nelle auto possono essere eseguite su schermi di diverse dimensioni e densità, quindi le icone che fornisci devono essere disegnabili vettoriali. Un VectorDrawable consente di scalare gli asset senza perdere i dettagli. L'elemento disegnabile vettoriale consente inoltre di allineare facilmente bordi e angoli ai confini dei pixel con 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 un cambiamento 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'altra che indichi che l'azione è disabilitata.

Figura 6. Esempi di icone di azioni personalizzate fuori stile.

Indica il formato audio

Per indicare che i contenuti multimediali attualmente in riproduzione utilizzano un formato audio speciale, puoi specificare le icone di cui viene eseguito il rendering 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 extra dell'elemento multimediale attualmente in riproduzione (passato a MediaSession.setMetadata()). Assicurati di impostare entrambi questi extra per adattarli a layout diversi.

Inoltre, puoi impostare l'opzione KEY_IMMERSIVE_AUDIO extra per comunicare agli OEM che si tratta di un audio immersivo e che devono fare molta attenzione quando devono decidere se applicare effetti audio che potrebbero interferire con questi contenuti.

Puoi configurare l'elemento multimediale attualmente in riproduzione in modo che il sottotitolo, la descrizione o entrambi siano link ad altri elementi multimediali. In questo modo l'utente può passare rapidamente agli articoli correlati. Ad esempio, potrebbe passare 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 sfogliare i contenuti in questione.

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

Azioni vocali supportate

L'app multimediale deve supportare le azioni vocali per offrire ai conducenti un'esperienza sicura e pratica che riduce al minimo le distrazioni. Ad esempio, se nell'app è in corso la riproduzione di un elemento multimediale, l'utente può dire "Fammi ascoltare [titolo del brano]" per dire all'app di riprodurre un brano diverso senza guardare o toccare il display dell'auto. Gli utenti possono avviare una query facendo clic sui pulsanti appropriati sul volante o pronunciando l'hotword "Hey Google".

Quando Android Auto o il sistema operativo Android Automotive rileva e interpreta un'azione vocale, questa viene inviata all'app tramite onPlayFromSearch(). Dopo aver ricevuto questo callback, l'app trova i contenuti corrispondenti alla stringa query e avvia la riproduzione.

Nella query gli utenti possono specificare diverse categorie di termini: genere, artista, album, nome del brano, stazione radio o playlist. Quando sviluppi il supporto per la ricerca, prendi in considerazione tutte le categorie pertinenti per la tua app. Se Android Auto o il sistema operativo Android Automotive rileva che una determinata query rientra in determinate categorie, vengono aggiunti ulteriori elementi nel parametro extras. Puoi inviare i seguenti extra:

Tieni conto di una stringa query vuota, che può essere inviata da Android Auto o dal sistema operativo Android Automotive 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, valuta la possibilità di inserire i contenuti correlati nella coda della sessione multimediale. Ad esempio, se l'utente richiede la riproduzione di un album, l'app potrebbe riempire la coda con l'elenco di tracce dell'album. Inoltre, valuta la possibilità di implementare il supporto per i risultati di ricerca sfogliabili, in modo che un utente possa scegliere un canale diverso corrispondente alla sua query.

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

Per un esempio dettagliato di come implementare le azioni di riproduzione attivate con i comandi vocali nella tua app, consulta l'articolo sull'Assistente Google e nelle app multimediali.

Implementare misure di salvaguardia contro le distrazioni

Poiché durante l'utilizzo di Android Auto il telefono dell'utente è connesso agli altoparlanti dell'auto, devi adottare precauzioni aggiuntive per evitare di distrarti.

Disattiva gli allarmi dell'auto

Le app multimediali di Android Auto non devono avviare la riproduzione dell'audio dagli altoparlanti dell'auto, a meno che l'utente non avvii la riproduzione premendo, ad esempio, un pulsante di riproduzione. Anche una sveglia programmata dall'utente nell'app multimediale non 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 il telefono è proiettato sullo schermo di un'auto osservando LiveData per il tipo di connessione in auto e controllando se è uguale a CONNECTION_TYPE_PROJECTION.

Se lo smartphone dell'utente sta proiettando, le app multimediali che supportano le sveglie devono eseguire una delle seguenti operazioni:

  • Disattiva l'allarme.
  • Attiva la sveglia tramite STREAM_ALARM e fornisci sullo schermo del telefono un'interfaccia utente 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, la visualizzazione di una notifica per l'utente rappresenta una distrazione. Per evitare che Android Auto mostri 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 si verifica un errore nell'app, imposta lo stato di riproduzione su STATE_ERROR e invia un messaggio di errore utilizzando il metodo setErrorMessage(). Consulta la pagina PlaybackStateCompat per un elenco dei codici di errore che puoi utilizzare quando imposti 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 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, consulta la sezione 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 essere "Accedi a [nome dell'app]" anziché "Accedi".

Altre risorse