Connettersi a un'app multimediale

Esistono due modi per connettersi a un'app multimediale:

  1. MediaController
  2. MediaBrowser

MediaController

Un controller multimediale interagisce con una sessione multimediale per eseguire query e controllare la riproduzione di un'app multimediale. In Media3, l'MediaController API implementa l'Player interfaccia. Ecco alcuni esempi di app client che utilizzano un controller multimediale:

Un controller multimediale può essere utile anche all'interno di un'app multimediale, ad esempio se il player e la sessione multimediale si trovano in un Service separato dall'Activity o dal Fragment con l'UI.

Creare un MediaController

Per creare un MediaController, inizia creando un SessionToken per il MediaSession corrispondente. Il metodo onStart() della tua Activity o del tuo Fragment può essere un buon punto di partenza.

Kotlin

val sessionToken = SessionToken(context, ComponentName(context, PlaybackService::class.java))

Java

SessionToken sessionToken =
    new SessionToken(context, new ComponentName(context, PlaybackService.class));

L'utilizzo di questo SessionToken per creare un MediaController collega il controller alla sessione specificata. Questa operazione viene eseguita in modo asincrono, quindi devi ascoltare il risultato e utilizzarlo quando è disponibile.

Kotlin

val controllerFuture = MediaController.Builder(context, sessionToken).buildAsync()
controllerFuture.addListener(
  {
    // MediaController is available here with controllerFuture.get()
  },
  MoreExecutors.directExecutor(),
)

Java

ListenableFuture<MediaController> controllerFuture =
    new MediaController.Builder(context, sessionToken).buildAsync();
controllerFuture.addListener(
    () -> {
      // MediaController is available here with controllerFuture.get()
    },
    MoreExecutors.directExecutor());

Utilizzare un MediaController

MediaController implementa l'interfaccia Player, quindi puoi utilizzare i comandi definiti nell'interfaccia per controllare la riproduzione del MediaSession connesso. Ciò significa che la chiamata a play() su un MediaController invierà il comando al MediaSession connesso, che a sua volta delegherà il comando al Player sottostante.

Puoi aggiungere un Player.Listener al controller per ascoltare le modifiche nello stato di Player. Per ulteriori dettagli sull'utilizzo di un Player.Listener, consulta la guida agli eventi del player.

L'interfaccia MediaController.Listener definisce callback aggiuntivi per eventi e comandi personalizzati dal MediaSession connesso. Ad esempio, onCustomCommand() quando la sessione invia un comando personalizzato, onAvailableSessionCommandsChanged() quando la sessione modifica i comandi di sessione disponibili o onDisconnected() quando il controller viene disconnesso dalla sessione.

È possibile impostare un MediaController.Listener durante la creazione del controller con un Builder:

Kotlin

val controllerFuture =
  MediaController.Builder(context, sessionToken)
    .setListener(
      object : MediaController.Listener {
        override fun onCustomCommand(
          controller: MediaController,
          command: SessionCommand,
          args: Bundle,
        ): ListenableFuture<SessionResult> {
          // Handle custom command.
          return Futures.immediateFuture(SessionResult(SessionResult.RESULT_SUCCESS))
        }

        override fun onDisconnected(controller: MediaController) {
          // Handle disconnection.
        }
      }
    )
    .buildAsync()

Java

ListenableFuture<MediaController> controllerFuture =
    new MediaController.Builder(context, sessionToken)
        .setListener(
            new MediaController.Listener() {
              @Override
              public ListenableFuture<SessionResult> onCustomCommand(
                  MediaController controller, SessionCommand command, Bundle args) {
                // Handle custom command.
                return Futures.immediateFuture(new SessionResult(SessionResult.RESULT_SUCCESS));
              }

              @Override
              public void onDisconnected(MediaController controller) {
                // Handle disconnection.
              }
            })
        .buildAsync();

Come per gli altri componenti, ricordati di rilasciare il MediaController quando non è più necessario, ad esempio nel metodo onStop() di un Activity o di un Fragment.

Kotlin

MediaController.releaseFuture(controllerFuture)

Java

MediaController.releaseFuture(controllerFuture);

Il rilascio del controller continuerà a inviare tutti i comandi in attesa inviati alla sessione e annullerà l'associazione al servizio di sessione solo dopo che questi comandi sono stati gestiti o dopo un periodo di timeout, a seconda di quale si verifica per prima.

MediaBrowser

Un MediaBrowser si basa sulle funzionalità offerte da un MediaController per consentire anche di sfogliare la libreria multimediale offerta dal MediaLibraryService di un'app multimediale.

Creare un MediaBrowser

Kotlin

val browserFuture = MediaBrowser.Builder(context, sessionToken).buildAsync()
browserFuture.addListener(
  {
    // MediaBrowser is available here with browserFuture.get()
  },
  MoreExecutors.directExecutor(),
)

Java

ListenableFuture<MediaBrowser> browserFuture =
    new MediaBrowser.Builder(context, sessionToken).buildAsync();
browserFuture.addListener(
    () -> {
      // MediaBrowser is available here with browserFuture.get()
    },
    MoreExecutors.directExecutor());

Utilizzare un MediaBrowser

Per iniziare a sfogliare la libreria di contenuti dell'app multimediale, recupera prima il nodo radice con getLibraryRoot():

Kotlin

// Get the library root to start browsing the library tree.
val rootFuture = mediaBrowser.getLibraryRoot(/* params= */ null)
rootFuture.addListener(
  {
    // Root node MediaItem is available here with rootFuture.get().value
  },
  MoreExecutors.directExecutor(),
)

Java

// Get the library root to start browsing the library tree.
ListenableFuture<LibraryResult<MediaItem>> rootFuture =
    mediaBrowser.getLibraryRoot(/* params= */ null);
rootFuture.addListener(
    () -> {
      // Root node MediaItem is available here with rootFuture.get().value
    },
    MoreExecutors.directExecutor());

Puoi quindi navigare nella libreria multimediale recuperando gli elementi secondari di un MediaItem nella libreria con getChildren(). Ad esempio, per recuperare gli elementi secondari del nodo radice MediaItem:

Kotlin

// Get the library root to start browsing the library tree.
val childrenFuture = mediaBrowser.getChildren(rootMediaItem.mediaId, 0, Int.MAX_VALUE, null)
childrenFuture.addListener(
  {
    // List of children MediaItem nodes is available here with
    // childrenFuture.get().value
  },
  MoreExecutors.directExecutor(),
)

Java

ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> childrenFuture =
    mediaBrowser.getChildren(rootMediaItem.mediaId, 0, Integer.MAX_VALUE, null);
childrenFuture.addListener(
    () -> {
      // List of children MediaItem nodes is available here with
      // childrenFuture.get().value
    },
    MoreExecutors.directExecutor());

Visualizzare i controlli di riproduzione per un'altra app multimediale

Quando visualizzi i controlli dell'UI con i pulsanti per un'altra app multimediale, è importante seguire le preferenze dei pulsanti multimediali dichiarate di quell' app.

Per risolvere le preferenze dell'app con i vincoli e i requisiti dell'UI, utilizza CommandButton.DisplayConstraints. Puoi definire i limiti e le restrizioni dell'UI e il resolve() metodo fornisce un elenco definitivo di pulsanti da visualizzare con la relativa icona, posizione e azione prevista. Se un utente fa clic su uno di questi pulsanti, puoi utilizzare CommandButton.executeAction per attivare l'azione associata nell'app multimediale.

Kotlin

// Get media button preferences from media app
val mediaButtonPreferences = controller.getMediaButtonPreferences()
// Declare constraints of UI (example: limit overflow button to one)
val displayConstraints =
  DisplayConstraints.Builder().setMaxButtonsForSlot(CommandButton.SLOT_OVERFLOW, 1).build()
// Resolve media app preferences with constraints
val resolvedButtons = displayConstraints.resolve(mediaButtonPreferences, controller)
// Display buttons in UI
for (button in resolvedButtons) {
  generateUiButton(
    uiPosition = button.slots[0],
    icon = getIconRes(button.icon),
    onClick = { button.executeAction(controller) },
  )
}

Java

// Get media button preferences from media app
List<CommandButton> mediaButtonPreferences = controller.getMediaButtonPreferences();
// Declare constraints of UI (example: limit overflow button to one)
DisplayConstraints displayConstraints =
    new DisplayConstraints.Builder()
        .setMaxButtonsForSlot(CommandButton.SLOT_OVERFLOW, 1)
        .build();
// Resolve media app preferences with constraints
List<CommandButton> resolvedButtons =
    displayConstraints.resolve(mediaButtonPreferences, controller);
// Display buttons in UI
for (CommandButton button : resolvedButtons) {
  generateUiButton(
      /* uiPosition= */ button.slots.get(0),
      /* icon= */ getIconRes(button.icon),
      /* onClick= */ () -> button.executeAction(controller));
}