Menayangkan konten dengan MediaLibraryService

Aplikasi media sering kali berisi koleksi item media, yang diatur dalam hierarki. Misalnya, lagu dalam album atau episode TV dalam playlist. Hierarki item media ini dikenal sebagai library media.

Contoh konten media yang disusun dalam hierarki
Gambar 1: Contoh hierarki item media yang membentuk library media.

MediaLibraryService menyediakan API standar untuk menayangkan dan mengakses library media Anda. Hal ini dapat membantu, misalnya, saat menambahkan dukungan untuk Android Auto ke aplikasi media Anda, yang menyediakan UI aman pengemudinya sendiri untuk library media Anda.

Mem-build MediaLibraryService

Mengimplementasikan MediaLibraryService mirip dengan mengimplementasikan MediaSessionService, kecuali dalam metode onGetSession(), Anda harus menampilkan MediaLibrarySession, bukan MediaSession.

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

Pastikan untuk mendeklarasikan Service dan izin yang diperlukan dalam file manifes juga:

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

Menggunakan MediaLibrarySession

MediaLibraryService API mengharapkan library media Anda disusun dalam format hierarki, dengan satu node root dan node turunan yang dapat diputar atau dijelajahi lebih lanjut.

MediaLibrarySession memperluas MediaSession API untuk menambahkan API penjelajahan konten. Dibandingkan dengan callback MediaSession, callback MediaLibrarySession menambahkan metode seperti:

  • onGetLibraryRoot() untuk saat klien meminta MediaItem root hierarki konten
  • onGetChildren() untuk saat klien meminta turunan MediaItem dalam hierarki konten
  • onGetSearchResult() untuk saat klien meminta hasil penelusuran dari hierarki konten untuk kueri tertentu

Metode callback yang relevan akan menyertakan objek LibraryParams dengan sinyal tambahan tentang jenis hierarki konten yang diminati aplikasi klien.

Tombol perintah untuk item media

Aplikasi sesi dapat mendeklarasikan tombol perintah yang didukung oleh MediaItem di MediaMetadata. Hal ini memungkinkan penetapan satu atau beberapa entri CommandButton ke item media yang dapat ditampilkan dan digunakan pengontrol untuk mengirim perintah kustom untuk item ke sesi dengan cara yang mudah.

Menyiapkan tombol perintah di sisi sesi

Saat mem-build sesi, aplikasi sesi mendeklarasikan kumpulan tombol perintah yang dapat ditangani sesi sebagai perintah kustom:

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

Saat mem-build item media, aplikasi sesi dapat menambahkan kumpulan ID perintah yang didukung yang mereferensikan perintah sesi dari tombol perintah yang telah disiapkan saat mem-build sesi:

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

Saat pengontrol atau browser terhubung atau memanggil metode lain dari sesi Callback, aplikasi sesi dapat memeriksa ControllerInfo yang diteruskan ke callback untuk mendapatkan jumlah maksimum tombol perintah yang dapat ditampilkan oleh pengontrol atau browser. ControllerInfo yang diteruskan ke metode callback menyediakan pengambil untuk mengakses nilai ini dengan mudah. Secara default, nilai ditetapkan ke 0 yang menunjukkan bahwa browser atau pengontrol tidak mendukung fitur ini:

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

Saat menangani tindakan kustom yang telah dikirim untuk item media, aplikasi sesi dapat mendapatkan ID item media dari argumen Bundle yang diteruskan ke 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);
}

Menggunakan tombol perintah sebagai browser atau pengontrol

Di sisi MediaController, aplikasi dapat mendeklarasikan jumlah maksimum tombol perintah yang didukungnya untuk item media saat mem-build MediaController atau MediaBrowser:

Kotlin

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

Java

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

Saat terhubung ke sesi, aplikasi pengontrol dapat menerima tombol perintah yang didukung oleh item media dan yang pengontrolnya memiliki perintah yang tersedia yang diberikan oleh aplikasi sesi:

Kotlin

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

Java

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

Untuk memudahkan, MediaController dapat mengirim perintah kustom khusus item media dengan MediaController.sendCustomCommand(SessionCommand, MediaItem, Bundle):

Kotlin

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

Java

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