Os apps de música 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.
Uma MediaLibraryService fornece uma API padronizada para exibir e acessar sua
biblioteca de mídia. Isso pode ser útil, por exemplo, ao adicionar suporte ao
Android Auto ao seu app de música, que oferece uma interface segura para motoristas
para sua biblioteca de mídia.
Criar um MediaLibraryService
A implementação de um MediaLibraryService é semelhante à implementação de um
MediaSessionService, exceto que, no método onGetSession(), você
precisa retornar um MediaLibrarySession em vez de um 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 e as permissões necessárias no arquivo de manifesto:
<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" />
Recomendamos registrar seu serviço como a plataforma e a interface de serviço do Media3:
Para compatibilidade com clientes que usam as APIs de sessão de mídia da plataforma, é recomendável incluir
<action android:name="android.media.browse.MediaBrowserService"/>no elementointent-filter. O Media3 oferece automaticamente compatibilidade com versões anteriores para essa interface de serviço.Como uma configuração à prova de obsolescência e para garantir que os apps que usam o Media3 possam se comunicar com seu serviço usando a API Media3, o
<action android:name="androidx.media3.session.MediaLibraryService"/>precisa ser fornecido com a opção de plataforma.
Isso permite que os apps encontrem seu serviço usando PackageManager e se conectem
com um MediaBrowser por qualquer uma dessas interfaces.
Usar um MediaLibrarySession
A API MediaLibraryService espera que sua biblioteca de mídia seja estruturada em um
formato de árvore, com um único nó raiz e nós filhos que podem ser
reproduzíveis ou navegáveis.
Um MediaLibrarySession estende 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 a raizMediaItemde uma árvore de conteúdo.onGetChildren()para quando um cliente solicita os filhos de umMediaItemna árvore de conteúdo.onGetSearchResult()para quando um cliente solicita resultados da pesquisa na á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 compatíveis com 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)) .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();
Ao criar um item de mídia, um app de sessão pode adicionar um conjunto de IDs de comando compatíveis 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 de sessão pode inspecionar o ControllerInfo transmitido ao
callback para receber o número máximo de botões de comando que um controlador ou navegador
pode mostrar. 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 o 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 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; }
Ao processar uma ação personalizada que foi 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 navegador ou controlador
No lado do MediaController, um app pode declarar o número máximo de botões de comando
com suporte para 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 controlador pode receber os botões de comando com suporte do item de mídia e para os quais o controlador tem o comando disponível concedido pelo app de sessão:
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);