אפליקציות מדיה מכילות לעיתים קרובות אוספים של פריטי מדיה, שמסודרים בהיררכיה. לדוגמה, שירים באלבום או פרקים של תוכנית טלוויזיה בפלייליסט. ההיררכיה הזו של קובצי מדיה נקראת ספריית מדיה.
MediaLibraryService מספק ממשק API סטנדרטי להצגה של ספריית המדיה ולגישה אליה. זה יכול להיות שימושי, למשל, כשמוסיפים תמיכה ב-Android Auto לאפליקציית המדיה, שמספקת ממשק משתמש בטוח לנהיגה לספריית המדיה.
הרכבת MediaLibraryService
ההטמעה של MediaLibraryService דומה להטמעה של MediaSessionService, אבל ב-method 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 ועל ההרשאות הנדרשות גם בקובץ המניפסט:
<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:
כדי לשמור על תאימות עם לקוחות שמשתמשים בממשקי ה-API של סשן המדיה בפלטפורמה, מומלץ לכלול את
<action android:name="android.media.browse.MediaBrowserService"/>ברכיבintent-filter. Media3 מספקת באופן אוטומטי תאימות לדורות קודמים לממשק השירות הזה.כדי להבטיח שההגדרה תתאים גם בעתיד, וכדי לוודא שאפליקציות שמשתמשות ב-Media3 יוכלו לתקשר עם השירות שלכם באמצעות Media3 API, צריך לספק את
<action android:name="androidx.media3.session.MediaLibraryService"/>לצד האפשרות של הפלטפורמה.
כך אפליקציות יכולות למצוא את השירות שלכם דרך PackageManager ולהתחבר אל MediaBrowser דרך אחד מהממשקים האלה.
שימוש בMediaLibrarySession
API של MediaLibraryService מצפה שספריית המדיה תהיה בפורמט עץ, עם צומת שורש אחד וצומתי צאצאים שיכולים להיות ניתנים להפעלה או ניתנים לעיון.
MediaLibrarySession מרחיב את ה-API של MediaSession כדי להוסיף ממשקי API לעיון בתוכן. בהשוואה לקריאה החוזרת (callback) של MediaSession, הקריאה החוזרת של MediaLibrarySession מוסיפה שיטות כמו:
-
onGetLibraryRoot()כשלקוח מבקש את השורשMediaItemשל עץ תוכן -
onGetChildren()כשלקוח מבקש את הצאצאים שלMediaItemבעץ התוכן onGetSearchResult()כשלקוח מבקש תוצאות חיפוש מעץ התוכן של שאילתה נתונה
שיטות קריאה חוזרת רלוונטיות יכללו אובייקט LibraryParams עם אותות נוספים לגבי סוג עץ התוכן שאפליקציית הלקוח מעוניינת בו.
כפתורי פקודות לקובצי מדיה
אפליקציית סשן יכולה להצהיר על כפתורי פקודות שנתמכים על ידי MediaItem ב-MediaMetadata. כך אפשר להקצות רשומה אחת או יותר של CommandButton לפריט מדיה, שבקר יכול להציג ולהשתמש בה כדי לשלוח את הפקודה המותאמת אישית של הפריט לסשן בצורה נוחה.
הגדרת כפתורי פקודות בצד של הסשן
כשיוצרים את הסשן, אפליקציית הסשן מצהירה על קבוצת כפתורי הפקודות שהסשן יכול לטפל בהם כפקודות בהתאמה אישית:
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 שמועבר לשיטת קריאה חוזרת מספק פונקציית 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);