メディア アプリに接続する方法は 2 つあります。
MediaControllerMediaBrowser
MediaController
メディア コントローラは、メディア セッションとやり取りして、メディア アプリの再生をクエリして制御します。Media3 では、MediaController
API は Player インターフェースを実装します。メディア コントローラを使用するクライアント アプリの例を次に示します。
- Android システムのメディア コントロール
- Android Wear OS コンパニオン アプリ
- Android Auto と Automotive OS
- Google アシスタントなどの音声アシスタント
- メディア コントローラ テストアプリ
メディア コントローラは、メディア アプリ内でも役立ちます。たとえば、プレーヤーとメディア セッションが、UI を持つ Activity または Fragment とは別の Service に存在する場合などです。
MediaController を作成する
MediaController を作成するには、まず対応する MediaSession の SessionToken を作成します。これは、Activity または Fragment の onStart() メソッドで行うのが適切です。
Kotlin
val sessionToken = SessionToken(context, ComponentName(context, PlaybackService::class.java))
Java
SessionToken sessionToken = new SessionToken(context, new ComponentName(context, PlaybackService.class));
この SessionToken を使用して MediaController をビルドすると、コントローラが指定されたセッションに接続されます。これは非同期で行われるため、結果をリッスンし、利用可能になったら使用する必要があります。
Kotlin
val controllerFuture = MediaController.Builder(context, sessionToken).buildAsync() controllerFuture.addListener( { // MediaController is available here with controllerFuture.get() }, MoreExecutors.directExecutor(), )
Java
ListenableFuture<MediaController> controllerFuture = new MediaController.Builder(context, sessionToken).buildAsync(); controllerFuture.addListener( () -> { // MediaController is available here with controllerFuture.get() }, MoreExecutors.directExecutor());
MediaController を使用する
MediaController は Player インターフェースを実装しているため、インターフェースで定義されているコマンドを使用して、接続された MediaSession の再生を制御できます。
つまり、MediaController で play() を呼び出すと、コマンドが接続された MediaSession に送信され、そのコマンドが基盤となる Player に委任されます。
コントローラに Player.Listener を追加して、
Player の状態の変化をリッスンできます。`
Player.Listener` の使用について詳しくは、プレーヤー イベントに関するガイドをご覧ください。
MediaController.Listener インターフェースは、接続された MediaSession からのイベントとカスタム コマンドの追加のコールバックを定義します。たとえば、セッションがカスタム コマンドを送信した場合は
onCustomCommand()、セッションが使用可能な
セッション コマンドを変更した場合は
onAvailableSessionCommandsChanged()、コントローラがセッションから切断された場合は
onDisconnected() が呼び出されます。
MediaController.Listener は、Builder を使用してコントローラをビルドするときに設定できます。
Kotlin
val controllerFuture = MediaController.Builder(context, sessionToken) .setListener( object : MediaController.Listener { override fun onCustomCommand( controller: MediaController, command: SessionCommand, args: Bundle, ): ListenableFuture<SessionResult> { // Handle custom command. return Futures.immediateFuture(SessionResult(SessionResult.RESULT_SUCCESS)) } override fun onDisconnected(controller: MediaController) { // Handle disconnection. } } ) .buildAsync()
Java
ListenableFuture<MediaController> controllerFuture = new MediaController.Builder(context, sessionToken) .setListener( new MediaController.Listener() { @Override public ListenableFuture<SessionResult> onCustomCommand( MediaController controller, SessionCommand command, Bundle args) { // Handle custom command. return Futures.immediateFuture(new SessionResult(SessionResult.RESULT_SUCCESS)); } @Override public void onDisconnected(MediaController controller) { // Handle disconnection. } }) .buildAsync();
他のコンポーネントと同様に、MediaController が不要になったら(Activity または Fragment の onStop() メソッドなど)リリースしてください。
Kotlin
MediaController.releaseFuture(controllerFuture)
Java
MediaController.releaseFuture(controllerFuture);
コントローラをリリースしても、セッションに送信された保留中のコマンドはすべて配信されます。セッション サービスからのバインド解除は、これらのコマンドが処理された後、またはタイムアウト期間が経過した後に行われます。
MediaBrowser
MediaBrowser は、MediaController が提供する機能に基づいて、メディア アプリの MediaLibraryService が提供するメディア ライブラリのブラウジングも可能にします。
MediaBrowser を作成する
Kotlin
val browserFuture = MediaBrowser.Builder(context, sessionToken).buildAsync() browserFuture.addListener( { // MediaBrowser is available here with browserFuture.get() }, MoreExecutors.directExecutor(), )
Java
ListenableFuture<MediaBrowser> browserFuture = new MediaBrowser.Builder(context, sessionToken).buildAsync(); browserFuture.addListener( () -> { // MediaBrowser is available here with browserFuture.get() }, MoreExecutors.directExecutor());
MediaBrowser を使用する
メディア アプリのコンテンツ ライブラリのブラウジングを開始するには、まず getLibraryRoot() でルートノードを取得します。
Kotlin
// Get the library root to start browsing the library tree. val rootFuture = mediaBrowser.getLibraryRoot(/* params= */ null) rootFuture.addListener( { // Root node MediaItem is available here with rootFuture.get().value }, MoreExecutors.directExecutor(), )
Java
// Get the library root to start browsing the library tree. ListenableFuture<LibraryResult<MediaItem>> rootFuture = mediaBrowser.getLibraryRoot(/* params= */ null); rootFuture.addListener( () -> { // Root node MediaItem is available here with rootFuture.get().value }, MoreExecutors.directExecutor());
次に、ライブラリ内の MediaItem の子を getChildren() で取得して、メディア ライブラリ内を移動できます。たとえば、ルートノード MediaItem の子を取得するには、次のようにします。
Kotlin
// Get the library root to start browsing the library tree. val childrenFuture = mediaBrowser.getChildren(rootMediaItem.mediaId, 0, Int.MAX_VALUE, null) childrenFuture.addListener( { // List of children MediaItem nodes is available here with // childrenFuture.get().value }, MoreExecutors.directExecutor(), )
Java
ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> childrenFuture = mediaBrowser.getChildren(rootMediaItem.mediaId, 0, Integer.MAX_VALUE, null); childrenFuture.addListener( () -> { // List of children MediaItem nodes is available here with // childrenFuture.get().value }, MoreExecutors.directExecutor());
別のメディア アプリの再生コントロールを表示する
別のメディア アプリのボタンを含む UI コントロールを表示する場合は、そのアプリで宣言されているメディアボタンの設定に従うことが重要です。
UI の制約と要件に合わせてアプリの設定を解決するには、CommandButton.DisplayConstraints を使用します。UI で実行できる操作の制限を定義できます。resolve() メソッドは、表示するボタンのアイコン、位置、目的のアクションの明確なリストを提供します。ユーザーがこれらのボタンのいずれかをクリックすると、CommandButton.executeAction を使用して、メディア アプリで関連するアクションをトリガーできます。
Kotlin
// Get media button preferences from media app val mediaButtonPreferences = controller.getMediaButtonPreferences() // Declare constraints of UI (example: limit overflow button to one) val displayConstraints = DisplayConstraints.Builder().setMaxButtonsForSlot(CommandButton.SLOT_OVERFLOW, 1).build() // Resolve media app preferences with constraints val resolvedButtons = displayConstraints.resolve(mediaButtonPreferences, controller) // Display buttons in UI for (button in resolvedButtons) { generateUiButton( uiPosition = button.slots[0], icon = getIconRes(button.icon), onClick = { button.executeAction(controller) }, ) }
Java
// Get media button preferences from media app List<CommandButton> mediaButtonPreferences = controller.getMediaButtonPreferences(); // Declare constraints of UI (example: limit overflow button to one) DisplayConstraints displayConstraints = new DisplayConstraints.Builder() .setMaxButtonsForSlot(CommandButton.SLOT_OVERFLOW, 1) .build(); // Resolve media app preferences with constraints List<CommandButton> resolvedButtons = displayConstraints.resolve(mediaButtonPreferences, controller); // Display buttons in UI for (CommandButton button : resolvedButtons) { generateUiButton( /* uiPosition= */ button.slots.get(0), /* icon= */ getIconRes(button.icon), /* onClick= */ () -> button.executeAction(controller)); }