Wyświetlanie treści przy użyciu usługi MediaLibraryService

Aplikacje multimedialne często zawierają kolekcje elementów multimedialnych uporządkowane hierarchicznie. Może to być na przykład album lub odcinki serialu na playliście. Ta hierarchia elementów multimediów nosi nazwę biblioteki multimediów.

Przykłady treści multimedialnych uporządkowanych w hierarchii
Ilustracja 1. Przykłady hierarchii elementów multimedialnych, które tworzą bibliotekę multimediów.

MediaLibraryService udostępnia standardowy interfejs API do obsługi biblioteki multimediów i dostępu do niej. Może to być przydatne na przykład wtedy, gdy dodasz do aplikacji multimedialnej obsługę Androida Auto, która zapewnia własne interfejs użytkownika bezpieczny dla kierowcy dla biblioteki multimediów.

Kompilacja: MediaLibraryService

Implementacja funkcji MediaLibraryService jest podobna do implementacji funkcji MediaSessionService, z tą różnicą, że w metodzie onGetSession() zamiast MediaSession należy zwracać MediaLibrarySession.

Kotlin

class PlaybackService : MediaLibraryService() {
  var mediaLibrarySession: MediaLibrarySession? = null
  var callback: MediaLibrarySession.Callback = object : MediaLibrarySession.Callback {...}

  // If desired, validate the controller before returning the media library session
  override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaLibrarySession? =
    mediaLibrarySession

  // Create your player and media library session in the onCreate lifecycle event
  override fun onCreate() {
    super.onCreate()
    val player = ExoPlayer.Builder(this).build()
    mediaLibrarySession = MediaLibrarySession.Builder(this, player, callback).build()
  }

  // Remember to release the player and media library session in onDestroy
  override fun onDestroy() {
    mediaLibrarySession?.run { 
      player.release()
      release()
      mediaLibrarySession = null
    }
    super.onDestroy()
  }
}

Java

class PlaybackService extends MediaLibraryService {
  MediaLibrarySession mediaLibrarySession = null;
  MediaLibrarySession.Callback callback = new MediaLibrarySession.Callback() {...};

  @Override
  public MediaLibrarySession onGetSession(MediaSession.ControllerInfo controllerInfo) {
    // If desired, validate the controller before returning the media library session
    return mediaLibrarySession;
  }

  // Create your player and media library session in the onCreate lifecycle event
  @Override
  public void onCreate() {
    super.onCreate();
    ExoPlayer player = new ExoPlayer.Builder(this).build();
    mediaLibrarySession = new MediaLibrarySession.Builder(this, player, callback).build();
  }

  // Remember to release the player and media library session in onDestroy
  @Override
  public void onDestroy() {
    if (mediaLibrarySession != null) {
      mediaLibrarySession.getPlayer().release();
      mediaLibrarySession.release();
      mediaLibrarySession = null;
    }
    super.onDestroy();
  }
}

Pamiętaj, aby w pliku manifestu zadeklarować Service i wymagane uprawnienia:

<service
    android:name=".PlaybackService"
    android:foregroundServiceType="mediaPlayback"
    android:exported="true">
    <intent-filter>
        <action android:name="androidx.media3.session.MediaSessionService"/>
    </intent-filter>
</service>

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<!-- For targetSdk 34+ -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />

Użyj MediaLibrarySession

Interfejs API MediaLibraryService wymaga, aby Twoja biblioteka multimediów była ustrukturyzowana w formacie drzewa z pojedynczym węzłem głównym i węzłami podrzędnymi, które mogą być odtwarzane lub przeglądane.

MediaLibrarySession rozszerza interfejs API MediaSession, aby dodać interfejsy API przeglądania treści. W porównaniu z MediaSession wywołaniem zwrotnym wywołanie zwrotne MediaLibrarySession zawiera takie metody jak:

  • onGetLibraryRoot() (gdy klient poprosi o korzeń MediaItem drzewa treści)
  • onGetChildren() gdy klient prosi o podanie elementów podrzędnych elementu MediaItem w drzewie treści
  • onGetSearchResult()gdy klient żąda wyników wyszukiwania z drzewa treści dla danego zapytania

Odpowiednie metody wywołania będą zawierać obiekt LibraryParams z dodatkowymi sygnałami o typie drzewa treści, którym zainteresowana jest aplikacja klienta.

Przyciski poleceń dla multimediów

Aplikacja sesji może zadeklarować przyciski poleceń obsługiwane przez MediaItem w MediaMetadata. Umożliwia to przypisanie co najmniej 1 pozycji CommandButton do elementu multimedialnego, który kontroler może wyświetlać i używać do wysyłania niestandardowych poleceń dotyczących tego elementu do sesji w wygodny sposób.

Przyciski poleceń konfiguracji po stronie sesji

Podczas tworzenia sesji aplikacja sesji deklaruje zestaw przycisków poleceń, które sesja może obsługiwać jako polecenia niestandardowe:

Kotlin

val allCommandButtons =
  listOf(
    CommandButton.Builder(CommandButton.ICON_PLAYLIST_ADD)
      .setDisplayName(context.getString(R.string.add_to_playlist))
      .setDisplayName("Add to playlist")
      .setIconResId(R.drawable.playlist_add)
      .setSessionCommand(SessionCommand(COMMAND_PLAYLIST_ADD, Bundle.EMPTY))
      .setExtras(playlistAddExtras)
      .build(),
    CommandButton.Builder(CommandButton.ICON_RADIO)
      .setDisplayName(context.getString(R.string.radio_station))
      .setIconResId(R.drawable.radio)
      .setSessionCommand(SessionCommand(COMMAND_RADIO, Bundle.EMPTY))
      .setExtras(radioExtras)
      .build(),
    // possibly more here
  )

// Add all command buttons for media items supported by the session.
val session =
  MediaSession.Builder(context, player)
    .setCommandButtonsForMediaItems(allCommandButtons)
    .build()

Java

ImmutableList<CommandButton> allCommandButtons =
    ImmutableList.of(
        new CommandButton.Builder(CommandButton.ICON_PLAYLIST_ADD)
            .setDisplayName("Add to playlist")
            .setIconUri(Uri.parse("http://www.example.com/icon/playlist_add"))
            .setSessionCommand(new SessionCommand(COMMAND_PLAYLIST_ADD, Bundle.EMPTY))
            .setExtras(playlistAddExtras)
            .build(),
        new CommandButton.Builder(CommandButton.ICON_RADIO)
            .setDisplayName("Radio station")
            .setIconUri(Uri.parse("http://www.example.com/icon/radio"))
            .setSessionCommand(new SessionCommand(COMMAND_RADIO, Bundle.EMPTY))
            .setExtras(radioExtras)
            .build());

// Add all command buttons for media items supported by the session.
MediaSession session =
    new MediaSession.Builder(context, player)
        .setCommandButtonsForMediaItems(allCommandButtons)
        .build();

Podczas tworzenia elementu multimedialnego aplikacja sesji może dodać zestaw obsługiwanych identyfikatorów poleceń, które odwołują się do poleceń sesji przycisków poleceń skonfigurowanych podczas tworzenia sesji:

Kotlin

val mediaItem =
  MediaItem.Builder()
    .setMediaMetadata(
      MediaMetadata.Builder()
        .setSupportedCommands(listOf(COMMAND_PLAYLIST_ADD, COMMAND_RADIO))
        .build())
    .build()

Java

MediaItem mediaItem =
    new MediaItem.Builder()
        .setMediaMetadata(
            new MediaMetadata.Builder()
                .setSupportedCommands(ImmutableList.of(COMMAND_PLAYLIST_ADD, COMMAND_RADIO))
                .build())
        .build();

Gdy kontroler lub przeglądarka połączy się z inną metodą sesji Callback lub ją wywoła, aplikacja sesji może sprawdzić wartość ControllerInfo przekazaną do funkcji wywołania zwrotnego, aby uzyskać maksymalną liczbę przycisków sterowania, które kontroler lub przeglądarka mogą wyświetlić. Zmienne ControllerInfo przekazane do metody wywołania zwrotnego zapewniają metodę gettera, która umożliwia wygodny dostęp do tej wartości. Domyślnie wartość jest ustawiona na 0, co oznacza, że przeglądarka lub kontroler nie obsługuje tej funkcji:

Kotlin

override fun onGetItem(
  session: MediaLibrarySession,
  browser: MediaSession.ControllerInfo,
  mediaId: String,
): ListenableFuture<LibraryResult<MediaItem>> {

  val settableFuture = SettableFuture.create<LibraryResult<MediaItem>>()

  val maxCommandsForMediaItems = browser.maxCommandsForMediaItems
  scope.launch {
    loadMediaItem(settableFuture, mediaId, maxCommandsForMediaItems)
  }

  return settableFuture
}

Java

@Override
public ListenableFuture<LibraryResult<MediaItem>> onGetItem(
    MediaLibraryService.MediaLibrarySession session, ControllerInfo browser, String mediaId) {

  SettableFuture<LibraryResult<MediaItem>> settableFuture = SettableFuture.create();

  int maxCommandsForMediaItems = browser.getMaxCommandsForMediaItems();
  loadMediaItemAsync(settableFuture, mediaId, maxCommandsForMediaItems);

  return settableFuture;
}

Podczas obsługi działania niestandardowego wysłanego dla elementu multimedialnego aplikacja sesji może uzyskać identyfikator elementu multimedialnego z argumentów Bundle przekazanych do onCustomCommand:

Kotlin

override fun onCustomCommand(
  session: MediaSession,
  controller: MediaSession.ControllerInfo,
  customCommand: SessionCommand,
  args: Bundle,
): ListenableFuture<SessionResult> {
  val mediaItemId = args.getString(MediaConstants.EXTRA_KEY_MEDIA_ID)
  return if (mediaItemId != null)
    handleCustomCommandForMediaItem(controller, customCommand, mediaItemId, args)
  else handleCustomCommand(controller, customCommand, args)
}

Java

@Override
public ListenableFuture<SessionResult> onCustomCommand(
    MediaSession session,
    ControllerInfo controller,
    SessionCommand customCommand,
    Bundle args) {
  String mediaItemId = args.getString(MediaConstants.EXTRA_KEY_MEDIA_ID);
  return mediaItemId != null
      ? handleCustomCommandForMediaItem(controller, customCommand, mediaItemId, args)
      : handleCustomCommand(controller, customCommand, args);
}

Używanie przycisków poleceń jako przeglądarki lub kontrolera

Podczas tworzenia MediaController lub MediaBrowser aplikacja może zadeklarować maksymalną liczbę przycisków poleceń obsługiwanych przez dany element multimedialny:MediaController

Kotlin

val browserFuture =
  MediaBrowser.Builder(context, sessionToken)
    .setMaxCommandsForMediaItems(3)
    .buildAsync()

Java

ListenableFuture<MediaBrowser> browserFuture =
    new MediaBrowser.Builder(context, sessionToken)
        .setMaxCommandsForMediaItems(3)
        .buildAsync();

Po połączeniu z sesją aplikacja kontrolera może otrzymywać przyciski poleceń obsługiwane przez element multimedialny i dla których kontroler ma dostęp do poleceń przyznanych przez aplikację sesji:

Kotlin

val commandButtonsForMediaItem: List<CommandButton> =
  controller.getCommandButtonsForMediaItem(mediaItem)

Java

ImmutableList<CommandButton> commandButtonsForMediaItem =
    controller.getCommandButtonsForMediaItem(mediaItem);

Dla wygody MediaController może wysyłać polecenia niestandardowe dotyczące konkretnego elementu multimedialnego za pomocą MediaController.sendCustomCommand(SessionCommand, MediaItem, Bundle):

Kotlin

controller.sendCustomCommand(addToPlaylistButton.sessionCommand!!, mediaItem, Bundle.EMPTY)

Java

controller.sendCustomCommand(
    checkNotNull(addToPlaylistButton.sessionCommand), mediaItem, Bundle.EMPTY);