Os apps de mídia geralmente contêm coleções de itens de mídia organizados em uma hierarquia. Por exemplo, músicas em um álbum ou episódios de TV em uma playlist. Essa hierarquia de itens de mídia é conhecida como biblioteca de mídia.
Um MediaLibraryService
fornece uma API padronizada para servir e acessar sua
biblioteca de mídia. Isso pode ser útil, por exemplo, ao adicionar suporte ao
Android Auto ao seu app de mídia, que oferece a própria
interface segura para motoristas para a biblioteca de mídia.
Criar um MediaLibraryService
Implementar um MediaLibraryService
é semelhante a
implementar um MediaSessionService
,
exceto que no método onGetSession()
, você precisa
retornar um MediaLibrarySession
em vez de um MediaSession
.
Kotlin
class PlaybackService : MediaLibraryService() { var mediaLibrarySession: MediaLibrarySession? = null var callback: MediaLibrarySession.Callback = object : MediaLibrarySession.Callback {...} // If desired, validate the controller before returning the media library session override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaLibrarySession? = 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(); } }
Não se esqueça de declarar o Service
e as permissões necessárias no arquivo de manifesto
também:
<service
android:name=".PlaybackService"
android:foregroundServiceType="mediaPlayback"
android:exported="true">
<intent-filter>
<action android:name="androidx.media3.session.MediaSessionService"/>
</intent-filter>
</service>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<!-- For targetSdk 34+ -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
Use um MediaLibrarySession
A API MediaLibraryService
espera que a biblioteca de mídia seja estruturada em um
formato de árvore, com um único nó raiz e nós filhos que podem ser
reproduzidos
ou navegáveis.
Um MediaLibrarySession
amplia a API MediaSession
para adicionar APIs de navegação de conteúdo. Em comparação com o
callback MediaSession
,
o callback MediaLibrarySession
adiciona métodos como:
onGetLibraryRoot()
para quando um cliente solicita aMediaItem
raiz de uma árvore de conteúdoonGetChildren()
para quando um cliente solicita os filhos de umMediaItem
na árvore de conteúdoonGetSearchResult()
para quando um cliente solicita resultados de pesquisa da árvore de conteúdo para uma determinada consulta.
Os métodos de callback relevantes vão incluir um objeto LibraryParams
com outros indicadores sobre o tipo de árvore de conteúdo em que um app cliente
está interessado.
Botões de comando para itens de mídia
Um app de sessão pode declarar botões de comando aceitos por um MediaItem
no
MediaMetadata
. Isso permite atribuir uma ou mais entradas CommandButton
a um item de mídia que um controlador pode mostrar e usar para enviar o comando
personalizado do item à sessão de maneira conveniente.
Configurar botões de comando no lado da sessão
Ao criar a sessão, um app de sessão declara o conjunto de botões de comando que uma sessão pode processar como comandos personalizados:
Kotlin
val allCommandButtons = listOf( CommandButton.Builder(CommandButton.ICON_PLAYLIST_ADD) .setDisplayName(context.getString(R.string.add_to_playlist)) .setDisplayName("Add to playlist") .setIconResId(R.drawable.playlist_add) .setSessionCommand(SessionCommand(COMMAND_PLAYLIST_ADD, Bundle.EMPTY)) .setExtras(playlistAddExtras) .build(), CommandButton.Builder(CommandButton.ICON_RADIO) .setDisplayName(context.getString(R.string.radio_station)) .setIconResId(R.drawable.radio) .setSessionCommand(SessionCommand(COMMAND_RADIO, Bundle.EMPTY)) .setExtras(radioExtras) .build(), // possibly more here ) // 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("Add to playlist") .setIconUri(Uri.parse("http://www.example.com/icon/playlist_add")) .setSessionCommand(new SessionCommand(COMMAND_PLAYLIST_ADD, Bundle.EMPTY)) .setExtras(playlistAddExtras) .build(), new CommandButton.Builder(CommandButton.ICON_RADIO) .setDisplayName("Radio station") .setIconUri(Uri.parse("http://www.example.com/icon/radio")) .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();
Ao criar um item de mídia, um app de sessão pode adicionar um conjunto de IDs de comando aceitos que fazem referência a comandos de sessão de botões de comando que foram configurados ao criar a sessão:
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();
Quando um controlador ou navegador se conecta ou chama outro método da sessão
Callback
, o app da sessão pode inspecionar o ControllerInfo
transmitido para o
callback para receber o número máximo de botões de comando que um controlador ou navegador
pode exibir. O ControllerInfo
transmitido para um método de callback fornece um
getter para acessar esse valor de maneira conveniente. Por padrão, o valor é definido como 0, o que
indica que o navegador ou controlador não oferece suporte a esse recurso:
Kotlin
override fun onGetItem( session: MediaLibrarySession, browser: MediaSession.ControllerInfo, mediaId: String, ): ListenableFuture<LibraryResult<MediaItem>> { val settableFuture = SettableFuture.create<LibraryResult<MediaItem>>() val maxCommandsForMediaItems = browser.maxCommandsForMediaItems scope.launch { loadMediaItem(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; }
Ao processar uma ação personalizada enviada para um item de mídia, o app de sessão
pode receber o ID do item de mídia dos argumentos Bundle
transmitidos para
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); }
Usar botões de comando como um navegador ou controlador
No lado do MediaController
, um app pode declarar o número máximo de botões
de comando compatíveis com um item de mídia ao criar o MediaController
ou
MediaBrowser
:
Kotlin
val browserFuture = MediaBrowser.Builder(context, sessionToken) .setMaxCommandsForMediaItems(3) .buildAsync()
Java
ListenableFuture<MediaBrowser> browserFuture = new MediaBrowser.Builder(context, sessionToken) .setMaxCommandsForMediaItems(3) .buildAsync();
Quando conectado à sessão, o app de controle pode receber os botões de comando compatíveis com o item de mídia e para os quais o controlador tem o comando disponível concedido pelo app de sessão:
Kotlin
val commandButtonsForMediaItem: List<CommandButton> = controller.getCommandButtonsForMediaItem(mediaItem)
Java
ImmutableList<CommandButton> commandButtonsForMediaItem = controller.getCommandButtonsForMediaItem(mediaItem);
Para sua conveniência, um MediaController
pode enviar comandos personalizados específicos do item de mídia
com MediaController.sendCustomCommand(SessionCommand, MediaItem, Bundle)
:
Kotlin
controller.sendCustomCommand(addToPlaylistButton.sessionCommand!!, mediaItem, Bundle.EMPTY)
Java
controller.sendCustomCommand( checkNotNull(addToPlaylistButton.sessionCommand), mediaItem, Bundle.EMPTY);