ارائه محتوا با MediaLibraryService

برنامه‌های رسانه‌ای اغلب شامل مجموعه‌هایی از عناصر رسانه‌ای هستند که به‌صورت سلسله‌مراتبی سازمان‌دهی شده‌اند. برای مثال، آهنگ‌های یک آلبوم یا قسمت‌های تلویزیونی در یک فهرست پخش. این سلسله مراتب فایل‌های رسانه‌ای به‌عنوان کتابخانه رسانه شناخته می‌شود.

مثال‌هایی از محتوای رسانه‌ای که به‌صورت سلسله‌مراتبی چیده شده است
شکل ۱: نمونه‌هایی از سلسله‌مراتب فایل رسانه‌ای که کتابخانه رسانه‌ای را تشکیل می‌دهند.

MediaLibraryService میانای برنامه‌سازی کاربردی استانداردشده‌ای برای ارائه و دسترسی به کتابخانه رسانه‌ای شما فراهم می‌کند. این کار می‌تواند مفید باشد، برای مثال، هنگام افزودن پشتیبانی از Android Auto به برنامه رسانه‌ای‌تان، که رابط کاربری ایمن برای راننده برای کتابخانه رسانه‌ای‌تان ارائه می‌دهد.

ساختن MediaLibraryService

پیاده‌سازی MediaLibraryService شبیه به پیاده‌سازی MediaSessionService است، با این تفاوت که در روش onGetSession()، شما باید به‌جای MediaSession، یک MediaLibrarySession برگردانید.

کاتلین

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

جاوا

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 و اجازه‌های لازم را در فایل مانیفست نیز اعلام کنید:

<service
    android:name=".PlaybackService"
    android:foregroundServiceType="mediaPlayback"
    android:exported="true">
    <intent-filter>
        <action android:name="androidx.media3.session.MediaLibraryService"/>
        <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" />

توصیه می‌شود سرویس خود را هم به‌عنوان پلاتفرم و هم به‌عنوان رابط سرویس Media3 ثبت کنید:

  • برای سازگاری با کارخواهانی که از میاناهای برنامه‌سازی کاربردی جلسه رسانه پلاتفرم استفاده می‌کنند، توصیه می‌شود <action android:name="android.media.browse.MediaBrowserService"/> را در عنصر intent-filter بگنجانید. ‫Media3 به‌طور خودکار سازگاری معکوس را برای این میانای سرویس فراهم می‌کند.

  • برای اینکه تنظیمات شما در آینده نیز کار کند و مطمئن شوید برنامه‌هایی که از Media3 استفاده می‌کنند می‌توانند بااستفاده از Media3 API با سرویس شما ارتباط برقرار کنند، <action android:name="androidx.media3.session.MediaLibraryService"/> باید در کنار گزینه پلاتفرم ارائه شود.

این کار به برنامه‌ها امکان می‌دهد سرویس شما را ازطریق PackageManager پیدا کنند و ازطریق هریک از این میان‌ها به MediaBrowser متصل شوند.

استفاده از MediaLibrarySession

«میانای برنامه کاربردی» MediaLibraryService انتظار دارد کتابخانه رسانه‌تان در قالب درختی، با یک گره ریشه و گره‌های فرزند که ممکن است قابل‌پخش یا قابل‌مرور باشند، ساختاربندی شود.

MediaLibrarySession میانای برنامه‌سازی کاربردی MediaSession را گسترش می‌دهد تا میاناهای برنامه‌سازی کاربردی مرور محتوا را اضافه کند. در مقایسه با MediaSession فراخوانی، MediaLibrarySession فراخوانی روش‌هایی مثل این‌ها را اضافه می‌کند:

  • onGetLibraryRoot() برای زمانی که مشتری MediaItem ریشه درخت محتوا را درخواست می‌کند
  • onGetChildren() برای زمانی که مشتری فرزندان MediaItem را در درخت محتوا درخواست می‌کند
  • onGetSearchResult() برای زمانی که مشتری نتایج جستجو را از درخت محتوا برای پُرسمان معینی درخواست می‌کند

روش‌های برگشتی مربوطه شامل شیء LibraryParams با نشان‌های اضافی درباره نوع درخت محتوایی که برنامه مشتری به آن علاقه‌مند است خواهد بود.

دکمه‌های فرمان برای فایل‌های رسانه‌ای

برنامه جلسه می‌تواند دکمه‌های فرمان پشتیبانی‌شده توسط MediaItem را در MediaMetadata اعلام کند. این کار امکان می‌دهد یک یا چند ورودی CommandButton به عنصر رسانه‌ای اختصاص داده شود تا کنترل‌کننده بتواند آن را نمایش دهد و از آن برای ارسال فرمان سفارشی برای عنصر به جلسه به روشی راحت استفاده کند.

دکمه‌های فرمان را در سمت جلسه تنظیم کنید

هنگام ساختن جلسه، برنامه جلسه مجموعه دکمه‌های فرمان را که جلسه می‌تواند به‌عنوان فرمان‌های سفارشی مدیریت کند اعلام می‌کند:

کاتلین

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

جاوا

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

وقتی عنصر رسانه‌ای می‌سازید، برنامه جلسه می‌تواند مجموعه‌ای از شناسه‌های فرمان پشتیبانی‌شده را اضافه کند که به فرمان‌های جلسه دکمه‌های فرمان که هنگام ساختن جلسه راه‌اندازی شده‌اند ارجاع می‌دهد:

کاتلین

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

جاوا

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

وقتی کنترل‌کننده یا مرورگری به روش دیگری از جلسه متصل می‌شود یا آن را فرا می‌خواند Callback، برنامه جلسه می‌تواند ControllerInfo ارسال‌شده به بازخوان را بررسی کند تا حداکثر تعداد دکمه‌های فرمان را که کنترل‌کننده یا مرورگر می‌تواند نمایش دهد به‌دست آورد. ‫ControllerInfo منتقل‌شده به روش برگشتی یک دریافت‌کننده برای دسترسی راحت به این مقدار ارائه می‌دهد. به‌طور پیش‌فرض، مقدار روی ۰ تنظیم شده است که نشان می‌دهد مرورگر یا کنترل‌کننده از این ویژگی پشتیبانی نمی‌کند:

کاتلین

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
}

جاوا

@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 دریافت کند:

کاتلین

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

جاوا

@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، حداکثر تعداد دکمه‌های فرمان را که برای یک عنصر رسانه‌ای پشتیبانی می‌کند اعلام کند:

کاتلین

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

جاوا

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

وقتی به جلسه متصل می‌شود، برنامه کنترل‌کننده می‌تواند دکمه‌های فرمان پشتیبانی‌شده توسط عنصر رسانه‌ای و دکمه‌هایی را که کنترل‌کننده فرمان دردسترس اعطاشده توسط برنامه جلسه را برای آن‌ها دارد دریافت کند:

کاتلین

val commandButtonsForMediaItem = controller.getCommandButtonsForMediaItem(mediaItem)

جاوا

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

کاتلین

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

جاوا

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