미디어 앱에 연결

미디어 앱에 연결하는 방법에는 두 가지가 있습니다.

  1. MediaController
  2. MediaBrowser

MediaController

미디어 컨트롤러는 미디어 세션과 상호작용하여 미디어 앱의 재생을 쿼리하고 제어합니다. Media3에서 MediaController API는 Player 인터페이스를 구현합니다. 미디어 컨트롤러를 사용하는 클라이언트 앱의 예는 다음과 같습니다.

미디어 컨트롤러는 미디어 앱 내에서도 유용할 수 있습니다. 예를 들어 플레이어와 미디어 세션이 UI가 있는 Activity 또는 Fragment와 별도의 Service에 있는 경우입니다.

MediaController 만들기

MediaController를 만들려면 먼저 해당하는 MediaSessionSessionToken을 만듭니다. Activity 또는 FragmentonStart() 메서드가 이 작업에 적합한 위치일 수 있습니다.

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 사용

MediaControllerPlayer 인터페이스를 구현하므로 인터페이스에 정의된 명령어를 사용하여 연결된 MediaSession의 재생을 제어할 수 있습니다. 즉, MediaController에서 play()를 호출하면 연결된 MediaSession에 명령어가 전송되고, 이 명령어가 기본 Player에 위임됩니다.

컨트롤러에 Player.Listener를 추가하여 Player 상태의 변경사항을 수신 대기할 수 있습니다. 플레이어 이벤트 가이드에서 Player.Listener 사용에 관한 자세한 내용을 확인하세요.

MediaController.Listener 인터페이스는 연결된 MediaSession의 이벤트 및 맞춤 명령어에 관한 추가 콜백을 정의합니다. 예를 들어 세션에서 맞춤 명령어를 전송할 때 onCustomCommand(), 세션에서 사용 가능한 세션 명령어를 변경할 때 onAvailableSessionCommandsChanged(), 컨트롤러가 세션에서 연결 해제될 때 onDisconnected()가 있습니다.

MediaController.ListenerBuilder를 사용하여 컨트롤러를 빌드할 때 설정할 수 있습니다.

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();

다른 구성요소와 마찬가지로 Activity 또는 FragmentonStop() 메서드와 같이 더 이상 필요하지 않은 경우 MediaController를 해제해야 합니다.

Kotlin

MediaController.releaseFuture(controllerFuture)

Java

MediaController.releaseFuture(controllerFuture);

컨트롤러를 해제해도 세션에 전송된 모든 대기 중인 명령어는 계속 전달되며 이러한 명령어가 처리된 후 또는 제한 시간이 지난 후에만 세션 서비스에서 바인딩 해제됩니다. 둘 중 먼저 발생하는 시점에 바인딩 해제됩니다.

MediaBrowser

MediaBrowserMediaController에서 제공하는 기능을 기반으로 미디어 앱의 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());

그런 다음 getChildren()을 사용하여 라이브러리의 MediaItem 하위 요소를 가져와 미디어 라이브러리를 탐색할 수 있습니다. 예를 들어 루트 노드 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) },
  )
}

자바

// 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));
}