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

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

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

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

ساختن MediaLibraryService

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

کاتلین

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 API انتظار دارد کتابخانه رسانه شما در قالب درختی ساختاردهی شده باشد، با یک گره ریشه واحد و گره‌های فرزند که ممکن است قابل پخش یا بیشتر قابل مرور باشند.

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 که به روش callback منتقل شده است، یک getter برای دسترسی راحت به این مقدار ارائه می‌دهد. به‌طور پیش‌فرض، مقدار روی ۰ تنظیم شده است که نشان می‌دهد مرورگر یا کنترل‌کننده از این ویژگی پشتیبانی نمی‌کند:

کاتلین

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