Conectar a um app de música

Há duas maneiras de se conectar a um app de mídia:

  1. MediaController
  2. MediaBrowser

MediaController

Um controlador de mídia interage com uma sessão de mídia para consultar e controlar a reprodução de um app de mídia. Na Media3, a API MediaController implementa a interface Player. Exemplos de apps cliente que usam um controlador de mídia:

Um controlador de mídia também pode ser útil em um app de mídia, por exemplo, se o player e a sessão de mídia estiverem em um Service separado do Activity ou Fragment com a interface.

Criar um MediaController.

Para criar uma MediaController, comece criando uma SessionToken para o MediaSession correspondente. O método onStart() da sua Activity ou Fragment pode ser um bom lugar para isso.

Kotlin

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

Java

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

Usar esse SessionToken para criar um MediaController conecta o controlador à sessão especificada. Isso acontece de forma assíncrona, então você precisa aguardar o resultado e usá-lo quando estiver disponível.

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

Usar um MediaController

O MediaController implementa a interface Player. Assim, é possível usar os comandos definidos na interface para controlar a reprodução do MediaSession conectado. Isso significa que chamar play() em um MediaController vai enviar o comando para o MediaSession conectado, que vai delegar o comando ao Player subjacente.

É possível adicionar um Player.Listener ao controlador para detectar mudanças no estado Player. Consulte o guia Eventos do player para mais detalhes sobre como usar um Player.Listener.

A interface MediaController.Listener define callbacks adicionais para eventos e comandos personalizados do MediaSession conectado. Por exemplo, onCustomCommand() quando a sessão envia um comando personalizado, onAvailableSessionCommandsChanged() quando a sessão muda os comandos disponíveis ou onDisconnected() quando o controlador é desconectado da sessão.

Uma MediaController.Listener pode ser definida ao criar o controlador com um Builder:

Kotlin

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

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

Assim como em outros componentes, libere o MediaController quando ele não for mais necessário, como no método onStop() de um Activity ou Fragment.

Kotlin

MediaController.releaseFuture(controllerFuture)

Java

MediaController.releaseFuture(controllerFuture);

A liberação do controlador ainda vai entregar todos os comandos pendentes enviados para a sessão e só vai desvincular do serviço de sessão depois que esses comandos forem processados ou após um período de tempo limite, o que ocorrer primeiro.

MediaBrowser

Um MediaBrowser se baseia nos recursos oferecidos por um MediaController para permitir também a navegação na biblioteca de mídia oferecida pelo MediaLibraryService de um app de mídia.

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

Usar um MediaBrowser

Para começar a navegar pela biblioteca de conteúdo do app de mídia, primeiro recupere o nó raiz com 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());

Em seguida, navegue pela biblioteca de mídia recuperando os filhos de um MediaItem na biblioteca com getChildren(). Por exemplo, para recuperar os filhos do nó raiz 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());

Mostrar controles de mídia para outro app de mídia

Ao mostrar controles de interface com botões para outro app de mídia, é importante seguir as preferências de botões de mídia declaradas desse app.

Para resolver as preferências do app com as restrições e os requisitos da sua interface, use CommandButton.DisplayConstraints. Você pode definir os limites e as restrições da sua interface, e o método resolve fornece uma lista definitiva de botões para mostrar com o ícone, a posição e a ação pretendida. Se um usuário clicar em um desses botões, use CommandButton.executeAction para acionar a ação associada no app de mídia.

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