חיבור לאפליקציית מדיה

יש שתי דרכים להתחבר לאפליקציית מדיה:

  1. MediaController
  2. MediaBrowser

MediaController

ממשק השליטה במדיה יוצר אינטראקציה עם סשן מדיה כדי לשלוח שאילתות לאפליקציית מדיה ולשלוט בהפעלה שלה. ב-Media3, ה-API של MediaController מטמיע את הממשק Player. דוגמאות לאפליקציות לקוח שמשתמשות בממשק השליטה במדיה:

ממשק השליטה במדיה יכול להיות שימושי גם באפליקציית מדיה, למשל אם הנגן וסשן המדיה נמצאים ב-Service נפרד מה-Activity או מה-Fragment עם ממשק המשתמש.

צור MediaController

כדי ליצור MediaController, קודם צריך ליצור SessionToken ל-MediaSession המתאים. השיטה onStart() של Activity או Fragment יכולה להתאים לכך.

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 המחובר. כלומר, קריאה ל-play() ב-MediaController תשלח את הפקודה ל-MediaSession המחובר, שיעביר את הפקודה ל-Player הבסיסי שלו.

אפשר להוסיף Player.Listener לבקר כדי להאזין לשינויים במצב של Player. במדריך בנושא אירועים של נגן מפורט מידע נוסף על השימוש ב-Player.Listener.

הממשק MediaController.Listener מגדיר קריאות חוזרות (callbacks) נוספות לאירועים ולפקודות בהתאמה אישית מה-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 כשכבר לא צריך אותו, למשל ב-method‏ onStop() של Activity או Fragment.

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 להפעלה של אפליקציית מדיה אחרת

כשמציגים רכיבים אינטראקטיביים עם כפתורים לאפליקציית מדיה אחרת, חשוב לפעול לפי העדפות כפתורי המדיה שהוגדרו באפליקציה הזו.

כדי לפתור את הבעיה של העדפות האפליקציה עם האילוצים והדרישות של ממשק המשתמש, צריך להשתמש ב-CommandButton.DisplayConstraints. אתם יכולים להגדיר את המגבלות וההגבלות של ממשק המשתמש, וה-method‏ 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));
}