แสดงเนื้อหาด้วย MediaLibraryService

แอปสื่อมักจะมีคอลเล็กชันของสื่อ รายการที่จัดระเบียบเป็นลำดับชั้น เช่น เพลงในอัลบั้มหรือตอนของรายการทีวีในเพลย์ลิสต์ ลำดับชั้นของสื่อ รายการนี้เรียกว่าคลังสื่อ

ตัวอย่างเนื้อหาสื่อที่จัดเรียงตามลำดับชั้น
รูปที่ 1: ตัวอย่างลำดับชั้นของรายการสื่อ ที่ประกอบกันเป็น คลังสื่อ

MediaLibraryService มี API ที่ได้มาตรฐานเพื่อแสดงและเข้าถึงคลังสื่อ ซึ่งอาจเป็นประโยชน์ เช่น เมื่อเพิ่มการรองรับ Android Auto ลงในแอปสื่อ ซึ่งมี UI ที่ปลอดภัยสำหรับผู้ขับขี่ของ คลังสื่อ

สร้าง MediaLibraryService

การใช้ MediaLibraryService จะคล้ายกับการใช้ MediaSessionService ยกเว้นว่าในเมธอด onGetSession() คุณ ควรแสดงผล MediaLibrarySession แทน MediaSession

Kotlin

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

  override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaLibrarySession? {
    // 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 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();
  }
}
อย่าลืมประกาศ Service และสิทธิ์ที่จำเป็นในไฟล์ Manifest ด้วย

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

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />

ใช้ MediaLibrarySession

API ของ MediaLibraryService คาดหวังให้คลังสื่อของคุณมีโครงสร้างในรูปแบบโครงสร้างแบบต้นไม้ โดยมีโหนดรากเดียวและโหนดลูกที่อาจเล่นได้ หรือเรียกดูเพิ่มเติมได้

A MediaLibrarySession ขยาย MediaSession API เพื่อเพิ่ม API การเรียกดูเนื้อหา เมื่อเทียบกับ MediaSession Callback แล้ว MediaLibrarySession Callback จะเพิ่มเมธอดต่างๆ เช่น

  • onGetLibraryRoot() เมื่อไคลเอ็นต์ขอ MediaItem รากของโครงสร้างเนื้อหา
  • onGetChildren() เมื่อไคลเอ็นต์ขอรายการย่อยของ MediaItem ในโครงสร้างเนื้อหา
  • onGetSearchResult() เมื่อไคลเอ็นต์ขอผลการค้นหาจากโครงสร้างเนื้อหาสำหรับคำค้นหาที่กำหนด

เมธอดเรียกกลับที่เกี่ยวข้องจะมีออบเจ็กต์ LibraryParams พร้อม สัญญาณเพิ่มเติมเกี่ยวกับประเภทของโครงสร้างเนื้อหาที่แอปไคลเอ็นต์ สนใจ

ปุ่มคำสั่งสำหรับสื่อ รายการ

แอปเซสชันสามารถประกาศปุ่มคำสั่งที่ MediaItem รองรับใน MediaMetadata ซึ่งช่วยให้กำหนดรายการ CommandButton อย่างน้อย 1 รายการให้กับสื่อ รายการที่คอนโทรลเลอร์แสดงและใช้เพื่อส่งคำสั่งที่กำหนดเองสำหรับรายการไปยังเซสชันได้อย่างสะดวก

ตั้งค่าปุ่มคำสั่งในฝั่งเซสชัน

เมื่อสร้างเซสชัน แอปเซสชันจะประกาศชุดปุ่มคำสั่งที่เซสชันจัดการได้เป็นคำสั่งที่กำหนดเอง ดังนี้

Kotlin

val allCommandButtons =
  listOf(
    CommandButton.Builder(CommandButton.ICON_PLAYLIST_ADD)
      .setDisplayName(context.getString(R.string.add_to_playlist))
      .setSessionCommand(SessionCommand(COMMAND_PLAYLIST_ADD, Bundle.EMPTY))
      .setExtras(playlistAddExtras)
      .build(),
    CommandButton.Builder(CommandButton.ICON_RADIO)
      .setDisplayName(context.getString(R.string.radio_station))
      .setSessionCommand(SessionCommand(COMMAND_RADIO, Bundle.EMPTY))
      .setExtras(radioExtras)
      .build(),
  )
// 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(context.getString(R.string.add_to_playlist))
            .setSessionCommand(new SessionCommand(COMMAND_PLAYLIST_ADD, Bundle.EMPTY))
            .setExtras(playlistAddExtras)
            .build(),
        new CommandButton.Builder(CommandButton.ICON_RADIO)
            .setDisplayName(context.getString(R.string.radio_station))
            .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();

เมื่อสร้างสื่อ รายการ แอปเซสชันจะเพิ่มชุดรหัสคำสั่งที่รองรับซึ่งอ้างอิงคำสั่งเซสชันของปุ่มคำสั่งที่ตั้งค่าไว้เมื่อสร้างเซสชันได้ ดังนี้

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

เมื่อคอนโทรลเลอร์หรือเบราว์เซอร์เชื่อมต่อหรือเรียกเมธอดอื่นของ Callback เซสชัน แอปเซสชันจะตรวจสอบ ControllerInfo ที่ส่งไปยัง Callback เพื่อดูจำนวนปุ่มคำสั่งสูงสุดที่คอนโทรลเลอร์หรือเบราว์เซอร์แสดงได้ ControllerInfo ที่ส่งไปยังเมธอด Callback มี Getter เพื่อเข้าถึงค่านี้ได้อย่างสะดวก โดยค่าเริ่มต้นจะตั้งค่าเป็น 0 ซึ่งบ่งบอกว่าเบราว์เซอร์หรือคอนโทรลเลอร์ไม่รองรับฟีเจอร์นี้ ดังนี้

Kotlin

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

  val maxCommandsForMediaItems = browser.maxCommandsForMediaItems
  loadMediaItemAsync(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;
}

เมื่อจัดการการดำเนินการที่กำหนดเองซึ่งส่งมาสำหรับรายการสื่อ แอปเซสชันจะรับรหัสรายการสื่อจากอาร์กิวเมนต์ Bundle ที่ส่งไปยัง 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);
}

ใช้ปุ่มคำสั่งเป็นเบราว์เซอร์หรือคอนโทรลเลอร์

ในฝั่ง MediaController แอปสามารถประกาศจำนวนปุ่มคำสั่งสูงสุดที่รองรับสำหรับสื่อ รายการเมื่อสร้าง MediaController หรือ MediaBrowser ได้ ดังนี้

Kotlin

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

Java

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

เมื่อเชื่อมต่อกับเซสชันแล้ว แอปคอนโทรลเลอร์จะรับ ปุ่มคำสั่งที่สื่อ รายการรองรับและ คอนโทรลเลอร์มี คำสั่งที่พร้อมใช้งานซึ่งแอปเซสชันให้สิทธิ์ได้ ดังนี้

Kotlin

val commandButtonsForMediaItem = controller.getCommandButtonsForMediaItem(mediaItem)

Java

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

Kotlin

val future =
  controller.sendCustomCommand(
    requireNotNull(addToPlaylistButton.sessionCommand),
    mediaItem,
    Bundle.EMPTY,
  )

Java

ListenableFuture<SessionResult> future =
    controller.sendCustomCommand(
        checkNotNull(addToPlaylistButton.sessionCommand), mediaItem, Bundle.EMPTY);