פקדי מדיה

בקרי המדיה ב-Android נמצאים ליד ההגדרות המהירות. סשנים מתאריך אפליקציות מרובות מסודרות בקרוסלה שניתן להחליק. בקרוסלה רשומים סשנים בסדר הזה:

  • סטרימינג שמושמע באופן מקומי בטלפון
  • שידורים מרוחקים, כמו אלה שזוהו במכשירים חיצוניים או בסשנים של הפעלת Cast
  • סשנים קודמים שניתן להמשיך, לפי הסדר שבו הופעלו לאחרונה

החל מ-Android 13 (רמת API 33), כדי להבטיח שמשתמשים יוכלו לגשת קבוצה של בקרי מדיה לאפליקציות שמפעילות מדיה, לחצני פעולה בלחצני המדיה נגזרים מהמצב Player.

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

איור 1 מציג דוגמה לאופן שבו זה נראה בטלפון ובטאבלט, בהתאמה.

בקרי מדיה במונחים של האופן שבו הם מופיעים במכשירי טלפון וטאבלטים,
            באמצעות דוגמה של טראק לדוגמה שמראה איך הלחצנים עשויים להופיע
איור 1: פקדי מדיה במכשירי טלפון וטאבלט

המערכת מציגה עד חמישה לחצני פעולה על סמך המצב Player, בתור שמתוארים בטבלה הבאה. במצב קומפקטי, רק שלוש הפעולות הראשונות יוצגו משבצות זמן. זה תואם לאופן שבו לחצני המדיה מעובדים בפלטפורמות Android כמו Auto , Assistant ו-Wear OS.

משבצת קריטריונים פעולה
1 playWhenReady שגוי או שההפעלה הנוכחית הוא STATE_ENDED. הפעלה
הערך playWhenReady מוגדר כ-True ומצב ההפעלה הנוכחי הוא STATE_BUFFERING. סימן גרפי שמוצג בזמן טעינה
הערך playWhenReady מוגדר כ-True ומצב ההפעלה הנוכחי הוא STATE_READY. השהיה
2 פקודת הנגן COMMAND_SEEK_TO_PREVIOUS או COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM זמינות. הקודם
אי אפשר להשתמש בפקודת נגן COMMAND_SEEK_TO_PREVIOUS או COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM, והפריסה המותאמת אישית שעדיין לא הוצבה זמינה למלא את המשבצת. בהתאמה אישית
(עדיין לא נתמך ב-Media3 ) PlaybackState התוספות כוללות ערך בוליאני true עבור המפתח EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_PREV. ריק
3 פקודת הנגן COMMAND_SEEK_TO_NEXT או COMMAND_SEEK_TO_NEXT_MEDIA_ITEM זמינות. הבא
אי אפשר להשתמש בפקודת נגן COMMAND_SEEK_TO_NEXT או COMMAND_SEEK_TO_NEXT_MEDIA_ITEM, והפריסה המותאמת אישית שעדיין לא הוצבה זמינה למלא את המשבצת. בהתאמה אישית
(עדיין לא נתמך ב-Media3 ) PlaybackState התוספות כוללות ערך בוליאני true עבור המפתח EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_NEXT. ריק
4 כדי למלא את המשבצת, תוצג לכם פקודה בהתאמה אישית מהפריסה המותאמת אישית שעדיין לא הצבתם. בהתאמה אישית
5 כדי למלא את המשבצת, תוצג לכם פקודה בהתאמה אישית מהפריסה המותאמת אישית שעדיין לא הצבתם. בהתאמה אישית

פקודות מותאמות אישית מסודרות לפי הסדר שבו הן נוספו פריסה מותאמת אישית.

התאמה אישית של לחצני הפקודה

כדי להתאים אישית את בקרי המדיה של המערכת באמצעות Jetpack Media3: אפשר להגדיר את הפריסה המותאמת אישית של הסשן ואת הפקודות הזמינות בהתאם, כאשר מטמיעים MediaSessionService:

  1. ב-onCreate(), מפתחים MediaSession ולהגדיר את הפריסה המותאמת אישית של לחצני פקודה.

  2. ב-MediaSession.Callback.onConnect(), מתן הרשאה לנאמני מידע על ידי הגדרת הפקודות הזמינות שלהם, כולל פקודות מותאמות אישית, בConnectionResult.

  3. ב-MediaSession.Callback.onCustomCommand(), להגיב לפקודה המותאמת אישית שהמשתמש בחר.

Kotlin

class PlaybackService : MediaSessionService() {
  private val customCommandFavorites = SessionCommand(ACTION_FAVORITES, Bundle.EMPTY)
  private var mediaSession: MediaSession? = null

  override fun onCreate() {
    super.onCreate()
    val favoriteButton =
      CommandButton.Builder()
        .setDisplayName("Save to favorites")
        .setIconResId(R.drawable.favorite_icon)
        .setSessionCommand(customCommandFavorites)
        .build()
    val player = ExoPlayer.Builder(this).build()
    // Build the session with a custom layout.
    mediaSession =
      MediaSession.Builder(this, player)
        .setCallback(MyCallback())
        .setCustomLayout(ImmutableList.of(favoriteButton))
        .build()
  }

  private inner class MyCallback : MediaSession.Callback {
    override fun onConnect(
      session: MediaSession,
      controller: MediaSession.ControllerInfo
    ): ConnectionResult {
    // Set available player and session commands.
    return AcceptedResultBuilder(session)
      .setAvailablePlayerCommands(
        ConnectionResult.DEFAULT_PLAYER_COMMANDS.buildUpon()
          .remove(COMMAND_SEEK_TO_NEXT)
          .remove(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM)
          .remove(COMMAND_SEEK_TO_PREVIOUS)
          .remove(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM)
          .build()
      )
      .setAvailableSessionCommands(
        ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon()
          .add(customCommandFavorites)
          .build()
      )
      .build()
    }

    override fun onCustomCommand(
      session: MediaSession,
      controller: MediaSession.ControllerInfo,
      customCommand: SessionCommand,
      args: Bundle
    ): ListenableFuture {
      if (customCommand.customAction == ACTION_FAVORITES) {
        // Do custom logic here
        saveToFavorites(session.player.currentMediaItem)
        return Futures.immediateFuture(SessionResult(SessionResult.RESULT_SUCCESS))
      }
      return super.onCustomCommand(session, controller, customCommand, args)
    }
  }
}

Java

public class PlaybackService extends MediaSessionService {
  private static final SessionCommand CUSTOM_COMMAND_FAVORITES =
      new SessionCommand("ACTION_FAVORITES", Bundle.EMPTY);
  @Nullable private MediaSession mediaSession;

  public void onCreate() {
    super.onCreate();
    CommandButton favoriteButton =
        new CommandButton.Builder()
            .setDisplayName("Save to favorites")
            .setIconResId(R.drawable.favorite_icon)
            .setSessionCommand(CUSTOM_COMMAND_FAVORITES)
            .build();
    Player player = new ExoPlayer.Builder(this).build();
    // Build the session with a custom layout.
    mediaSession =
        new MediaSession.Builder(this, player)
            .setCallback(new MyCallback())
            .setCustomLayout(ImmutableList.of(favoriteButton))
            .build();
  }

  private static class MyCallback implements MediaSession.Callback {
    @Override
    public ConnectionResult onConnect(
        MediaSession session, MediaSession.ControllerInfo controller) {
      // Set available player and session commands.
      return new AcceptedResultBuilder(session)
          .setAvailablePlayerCommands(
              ConnectionResult.DEFAULT_PLAYER_COMMANDS.buildUpon()
                .remove(COMMAND_SEEK_TO_NEXT)
                .remove(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM)
                .remove(COMMAND_SEEK_TO_PREVIOUS)
                .remove(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM)
                .build())
          .setAvailableSessionCommands(
              ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon()
                .add(CUSTOM_COMMAND_FAVORITES)
                .build())
          .build();
    }

    public ListenableFuture onCustomCommand(
        MediaSession session,
        MediaSession.ControllerInfo controller,
        SessionCommand customCommand,
        Bundle args) {
      if (customCommand.customAction.equals(CUSTOM_COMMAND_FAVORITES.customAction)) {
        // Do custom logic here
        saveToFavorites(session.getPlayer().getCurrentMediaItem());
        return Futures.immediateFuture(new SessionResult(SessionResult.RESULT_SUCCESS));
      }
      return MediaSession.Callback.super.onCustomCommand(
          session, controller, customCommand, args);
    }
  }
}

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

עם Jetpack Media3, כשמטמיעים MediaSession, ה-PlaybackState מעודכן באופן אוטומטי לפי נגן המדיה. באופן דומה, תטמיע MediaSessionService, הספרייה מפרסמת באופן אוטומטי התראה MediaStyle בשבילך ולשמור על עדכניות.

לחצני תגובה לפעולה

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

הוספת פקודות בהתאמה אישית כדי לקבל הנחיות איך להגיב לפקודה בהתאמה אישית.

התנהגות לפני מערכת Android 13

לצורך תאימות לאחור, ממשק המשתמש של המערכת ממשיך לספק פריסה חלופית שמשתמשת בפעולות של התראות לגבי אפליקציות שלא מתעדכנות כדי לטרגט ל-Android 13, או שלא כוללים מידע על PlaybackState. לחצני הפעולה נגזר מהרשימה Notification.Action שמצורפת אל MediaStyle התראה. המערכת מציגה עד חמש פעולות לפי הסדר שבו הן נוספו. במצב קומפקטי מוצגים עד שלושה לחצנים, לפי שמועברים אל setShowActionsInCompactView().

הפעולות המותאמות אישית מופיעות לפי הסדר שבו הן נוספו PlaybackState

הקוד הבא לדוגמה ממחיש איך להוסיף פעולות ל-MediaStyle. התראה :

Kotlin

import androidx.core.app.NotificationCompat
import androidx.media3.session.MediaStyleNotificationHelper

var notification = NotificationCompat.Builder(context, CHANNEL_ID)
        // Show controls on lock screen even when user hides sensitive content.
        .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
        .setSmallIcon(R.drawable.ic_stat_player)
        // Add media control buttons that invoke intents in your media service
        .addAction(R.drawable.ic_prev, "Previous", prevPendingIntent) // #0
        .addAction(R.drawable.ic_pause, "Pause", pausePendingIntent) // #1
        .addAction(R.drawable.ic_next, "Next", nextPendingIntent) // #2
        // Apply the media style template
        .setStyle(MediaStyleNotificationHelper.MediaStyle(mediaSession)
                .setShowActionsInCompactView(1 /* #1: pause button */))
        .setContentTitle("Wonderful music")
        .setContentText("My Awesome Band")
        .setLargeIcon(albumArtBitmap)
        .build()

Java

import androidx.core.app.NotificationCompat;
import androidx.media3.session.MediaStyleNotificationHelper;

NotificationCompat.Builder notification = new NotificationCompat.Builder(context, CHANNEL_ID)
        .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
        .setSmallIcon(R.drawable.ic_stat_player)
        .addAction(R.drawable.ic_prev, "Previous", prevPendingIntent)
        .addAction(R.drawable.ic_pause, "Pause", pausePendingIntent)
        .addAction(R.drawable.ic_next, "Next", nextPendingIntent)
        .setStyle(new MediaStyleNotificationHelper.MediaStyle(mediaSession)
                .setShowActionsInCompactView(1 /* #1: pause button */))
        .setContentTitle("Wonderful music")
        .setContentText("My Awesome Band")
        .setLargeIcon(albumArtBitmap)
        .build();

תמיכה בהמשך ההפעלה של מדיה

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

ניתן להפעיל ולהשבית את התכונה של המשך ההפעלה באמצעות אפליקציית ההגדרות. מתחת לקטע קול > אפשרויות מדיה. המשתמש יכול לגשת להגדרות גם דרך מקישים על סמל גלגל השיניים שמופיע אחרי שמחליקים על הקרוסלה המורחבת.

Media3 כולל ממשקי API שמאפשרים לתמוך בקלות בהמשך מדיה. לצפייה המשך הפעלה עם Media3 לקבלת הדרכה לגבי יישום התכונה הזו.

שימוש בממשקי ה-API הקודמים למדיה

בקטע הזה מוסבר איך לשלב את פקדי המדיה של המערכת באמצעות את ממשקי ה-API הקודמים של MediaCompat.

המערכת מאחזרת את המידע הבא MediaMetadata של MediaSession, ויוצג כשהוא יהיה זמין:

  • METADATA_KEY_ALBUM_ART_URI
  • METADATA_KEY_TITLE
  • METADATA_KEY_DISPLAY_TITLE
  • METADATA_KEY_ARTIST
  • METADATA_KEY_DURATION (אם משך הזמן לא מוגדר, סרגל הדילוג לא מוגדר הצגת ההתקדמות)

כדי לוודא שתהיה לכם התראה תקינה ומדויקת על בקרת המדיה, הגדרת הערך של METADATA_KEY_TITLE או METADATA_KEY_DISPLAY_TITLE מטא-נתונים לשם המדיה שמופעלת כרגע.

נגן המדיה מציג את משך הזמן שחלף עבור ההפעלה הנוכחית מדיה, וגם סרגל דילוג שממופה אל MediaSession PlaybackState.

נגן המדיה מציג את ההתקדמות של המדיה שמופעלת כרגע, יחד עם סרגל דילוג שממופה אל MediaSession PlaybackState. סרגל הדילוג מאפשר למשתמשים לשנות את המיקום ומציג את הזמן שחלף למדיה שימושי. כדי שסרגל הדילוג יופעל, צריך להטמיע PlaybackState.Builder#setActions וכוללים ACTION_SEEK_TO.

משבצת פעולה קריטריונים
1 הפעלה המדינה הנוכחית של PlaybackState היא אחת מהאפשרויות הבאות:
  • STATE_NONE
  • STATE_STOPPED
  • STATE_PAUSED
  • STATE_ERROR
סימן גרפי שמוצג בזמן טעינה המדינה הנוכחית של PlaybackState היא אחת מהאפשרויות הבאות:
  • STATE_CONNECTING
  • STATE_BUFFERING
השהיה המדינה הנוכחית של PlaybackState היא אף אחת מהאפשרויות שלמעלה.
2 הקודם PlaybackState פעולות כוללות ACTION_SKIP_TO_PREVIOUS.
בהתאמה אישית PlaybackState פעולות לא כוללות ACTION_SKIP_TO_PREVIOUS, ו-PlaybackState פעולות מותאמות אישית כוללות פעולה מותאמת אישית שעדיין לא בוצעה.
ריק PlaybackState התוספות כוללות ערך בוליאני true עבור המפתח SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV.
3 הבא PlaybackState פעולות כוללות ACTION_SKIP_TO_NEXT.
בהתאמה אישית PlaybackState פעולות לא כוללות ACTION_SKIP_TO_NEXT, ו-PlaybackState פעולות מותאמות אישית כוללות פעולה מותאמת אישית שעדיין לא בוצעה.
ריק PlaybackState התוספות כוללות ערך בוליאני true עבור המפתח SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT.
4 בהתאמה אישית PlaybackState פעולות מותאמות אישית כוללות פעולה מותאמת אישית שעדיין לא בוצעה.
5 בהתאמה אישית PlaybackState פעולות מותאמות אישית כוללות פעולה מותאמת אישית שעדיין לא בוצעה.

הוספת פעולות רגילות

דוגמאות הקוד הבאות ממחישות איך להוסיף את הטקסט הרגיל של PlaybackState פעולות מותאמות אישית.

כדי להפעיל, להשהות, לבצע את הפעולות הקודמות או לבצע את הפעולה הבאה, צריך להגדיר את הפעולות האלה בתור PlaybackState של סשן המדיה.

Kotlin

val session = MediaSessionCompat(context, TAG)
val playbackStateBuilder = PlaybackStateCompat.Builder()
val style = NotificationCompat.MediaStyle()

// For this example, the media is currently paused:
val state = PlaybackStateCompat.STATE_PAUSED
val position = 0L
val playbackSpeed = 1f
playbackStateBuilder.setState(state, position, playbackSpeed)

// And the user can play, skip to next or previous, and seek
val stateActions = PlaybackStateCompat.ACTION_PLAY
    or PlaybackStateCompat.ACTION_PLAY_PAUSE
    or PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS
    or PlaybackStateCompat.ACTION_SKIP_TO_NEXT
    or PlaybackStateCompat.ACTION_SEEK_TO // adding the seek action enables seeking with the seekbar
playbackStateBuilder.setActions(stateActions)

// ... do more setup here ...

session.setPlaybackState(playbackStateBuilder.build())
style.setMediaSession(session.sessionToken)
notificationBuilder.setStyle(style)

Java

MediaSessionCompat session = new MediaSessionCompat(context, TAG);
PlaybackStateCompat.Builder playbackStateBuilder = new PlaybackStateCompat.Builder();
NotificationCompat.MediaStyle style = new NotificationCompat.MediaStyle();

// For this example, the media is currently paused:
int state = PlaybackStateCompat.STATE_PAUSED;
long position = 0L;
float playbackSpeed = 1f;
playbackStateBuilder.setState(state, position, playbackSpeed);

// And the user can play, skip to next or previous, and seek
long stateActions = PlaybackStateCompat.ACTION_PLAY
    | PlaybackStateCompat.ACTION_PLAY_PAUSE
    | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS
    | PlaybackStateCompat.ACTION_SKIP_TO_NEXT
    | PlaybackStateCompat.ACTION_SEEK_TO; // adding this enables the seekbar thumb
playbackStateBuilder.setActions(stateActions);

// ... do more setup here ...

session.setPlaybackState(playbackStateBuilder.build());
style.setMediaSession(session.getSessionToken());
notificationBuilder.setStyle(style);

אם אינך רוצה לחצנים במיקומים הקודמים או הבאים, אל תוסיף ACTION_SKIP_TO_PREVIOUS או ACTION_SKIP_TO_NEXT, ובמקום זאת להוסיף תוספות אל הסשן:

Kotlin

session.setExtras(Bundle().apply {
    putBoolean(SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV, true)
    putBoolean(SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT, true)
})

Java

Bundle extras = new Bundle();
extras.putBoolean(SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV, true);
extras.putBoolean(SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT, true);
session.setExtras(extras);

הוספת פעולות מותאמות אישית

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

Kotlin

val customAction = PlaybackStateCompat.CustomAction.Builder(
    "com.example.MY_CUSTOM_ACTION", // action ID
    "Custom Action", // title - used as content description for the button
    R.drawable.ic_custom_action
).build()

playbackStateBuilder.addCustomAction(customAction)

Java

PlaybackStateCompat.CustomAction customAction = new PlaybackStateCompat.CustomAction.Builder(
        "com.example.MY_CUSTOM_ACTION", // action ID
        "Custom Action", // title - used as content description for the button
        R.drawable.ic_custom_action
).build();

playbackStateBuilder.addCustomAction(customAction);

תגובה לפעולות PlaybackState

כשמשתמש מקיש על לחצן, מערכת SystemUI משתמשת MediaController.TransportControls כדי לשלוח פקודה חזרה אל MediaSession. צריך לרשום שיחה חוזרת שיוכלו להגיב בצורה תקינה לאירועים האלה.

Kotlin

val callback = object: MediaSession.Callback() {
    override fun onPlay() {
        // start playback
    }

    override fun onPause() {
        // pause playback
    }

    override fun onSkipToPrevious() {
        // skip to previous
    }

    override fun onSkipToNext() {
        // skip to next
    }

    override fun onSeekTo(pos: Long) {
        // jump to position in track
    }

    override fun onCustomAction(action: String, extras: Bundle?) {
        when (action) {
            CUSTOM_ACTION_1 -> doCustomAction1(extras)
            CUSTOM_ACTION_2 -> doCustomAction2(extras)
            else -> {
                Log.w(TAG, "Unknown custom action $action")
            }
        }
    }

}

session.setCallback(callback)

Java

MediaSession.Callback callback = new MediaSession.Callback() {
    @Override
    public void onPlay() {
        // start playback
    }

    @Override
    public void onPause() {
        // pause playback
    }

    @Override
    public void onSkipToPrevious() {
        // skip to previous
    }

    @Override
    public void onSkipToNext() {
        // skip to next
    }

    @Override
    public void onSeekTo(long pos) {
        // jump to position in track
    }

    @Override
    public void onCustomAction(String action, Bundle extras) {
        if (action.equals(CUSTOM_ACTION_1)) {
            doCustomAction1(extras);
        } else if (action.equals(CUSTOM_ACTION_2)) {
            doCustomAction2(extras);
        } else {
            Log.w(TAG, "Unknown custom action " + action);
        }
    }
};

המשך מדיה

כדי שאפליקציית הנגן תופיע באזור של ההגדרות המהירות: עליך ליצור התראת MediaStyle עם אסימון MediaSession חוקי.

כדי להציג את הכותרת עבור התראת MediaStyle, יש להשתמש ב- NotificationBuilder.setContentTitle()

כדי להציג את סמל המותג בנגן המדיה, צריך להשתמש ב- NotificationBuilder.setSmallIcon()

כדי לתמוך בהמשך ההפעלה, המפתחים צריכים להטמיע MediaBrowserService ו-MediaSession. חובה להטמיע את הקריאה החוזרת (callback) של onPlay() באמצעות MediaSession.

הטמעת MediaBrowserService

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

המערכת מנסה ליצור קשר עם MediaBrowserService באמצעות חיבור דרך SystemUI. האפליקציה שלך צריכה לאפשר חיבורים כאלה, אחרת היא לא יכולה לתמוך כדי שניתן יהיה להמשיך את ההפעלה.

ניתן לזהות ולאמת חיבורים מ-SystemUI באמצעות שם החבילה com.android.systemui וחתימה. ה-SystemUI חתום באמצעות הפלטפורמה לחתימה. דוגמה לבדיקה מול חתימת הפלטפורמה יכולה להיות שנמצא באפליקציית UAMP.

כדי שניתן יהיה להמשיך להפעיל שוב את ההפעלה, MediaBrowserService צריך להטמיע התנהגויות אלה:

  • onGetRoot() חייב להחזיר במהירות שורש שאינו null. לוגיקה מורכבת אחרת יטופלו בonLoadChildren()

  • מתי בוצעה קריאה לפונקציה onLoadChildren() במזהה המדיה הבסיסית, התוצאה חייבת להכיל FLAG_PLAYABLE לילדים.

  • האפליקציה MediaBrowserService צריכה להחזיר את פריט המדיה האחרון שהופעל כאשר הם מקבלים Additional_RECENT שאילתה. הערך המוחזר צריך להיות פריט מדיה בפועל ולא גנרי מותאמת אישית.

  • MediaBrowserService חייב לספק תנאי מתאים MediaDescription עם שדה לא ריק title וכן כותרת משנה. צריך גם להגדיר URI של סמל או מפת סיביות של סמל.

דוגמאות הקוד הבאות ממחישות איך להטמיע את onGetRoot().

Kotlin

override fun onGetRoot(
    clientPackageName: String,
    clientUid: Int,
    rootHints: Bundle?
): BrowserRoot? {
    ...
    // Verify that the specified package is SystemUI. You'll need to write your 
    // own logic to do this.
    if (isSystem(clientPackageName, clientUid)) {
        rootHints?.let {
            if (it.getBoolean(BrowserRoot.EXTRA_RECENT)) {
                // Return a tree with a single playable media item for resumption.
                val extras = Bundle().apply {
                    putBoolean(BrowserRoot.EXTRA_RECENT, true)
                }
                return BrowserRoot(MY_RECENTS_ROOT_ID, extras)
            }
        }
        // You can return your normal tree if the EXTRA_RECENT flag is not present.
        return BrowserRoot(MY_MEDIA_ROOT_ID, null)
    }
    // Return an empty tree to disallow browsing.
    return BrowserRoot(MY_EMPTY_ROOT_ID, null)

Java

@Override
public BrowserRoot onGetRoot(String clientPackageName, int clientUid,
    Bundle rootHints) {
    ...
    // Verify that the specified package is SystemUI. You'll need to write your
    // own logic to do this.
    if (isSystem(clientPackageName, clientUid)) {
        if (rootHints != null) {
            if (rootHints.getBoolean(BrowserRoot.EXTRA_RECENT)) {
                // Return a tree with a single playable media item for resumption.
                Bundle extras = new Bundle();
                extras.putBoolean(BrowserRoot.EXTRA_RECENT, true);
                return new BrowserRoot(MY_RECENTS_ROOT_ID, extras);
            }
        }
        // You can return your normal tree if the EXTRA_RECENT flag is not present.
        return new BrowserRoot(MY_MEDIA_ROOT_ID, null);
    }
    // Return an empty tree to disallow browsing.
    return new BrowserRoot(MY_EMPTY_ROOT_ID, null);
}