Мультимедийные приложения часто содержат коллекции мультимедийных объектов, организованные в иерархию. Например, это могут быть треки в альбоме или серии телешоу в плейлисте. Такая иерархия мультимедийных объектов называется медиатекой.
MediaLibraryService предоставляет стандартизированный API для показа медиатеки и доступа к ней. Это может быть полезно, например, при добавлении поддержки Android Auto в мультимедийное приложение, которое предоставляет собственный безопасный для водителя интерфейс для вашей медиатеки.
Создать 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 и необходимые разрешения в файле манифеста:
<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
MediaLibraryService API ожидает, что медиатека будет иметь древовидную структуру с одним корневым узлом и дочерними узлами, которые могут быть воспроизводимыми или доступными для просмотра.
MediaLibrarySession расширяет MediaSession API, добавляя API для просмотра контента. По сравнению с обратным вызовом 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, переданный обратному вызову, чтобы получить максимальное количество кнопок команд, которые может отображать контроллер или браузер. Объект ControllerInfo, переданный в метод обратного вызова, предоставляет метод получения для удобного доступа к этому значению. По умолчанию задано значение 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);