יצירה של היררכיית תוכן

מערכות Android Auto ו-Android Automotive OS ‏ (AAOS) קוראות לשירות של דפדפן המדיה של האפליקציה כדי לגלות איזה תוכן זמין. כדי לתמוך בכך, צריך להטמיע את שתי השיטות האלה בשירות של דפדפן המדיה.

הטמעה של onGetRoot

השיטה onGetRoot של השירות מחזירה מידע על צומת הבסיס בהיררכיית התוכן. מערכות Android Auto ו-AAOS משתמשות בצומת הבסיס הזה כדי לבקש את שאר התוכן שלכם באמצעות השיטה onLoadChildren. בקטע הקוד הבא מוצגת הטמעה של השיטה onGetRoot:

Kotlin

override fun onGetRoot(
    clientPackageName: String,
    clientUid: Int,
    rootHints: Bundle?
): BrowserRoot? =
    // Verify that the specified package is allowed to access your
    // content. You'll need to write your own logic to do this.
    if (!isValid(clientPackageName, clientUid)) {
        // If the request comes from an untrusted package, return null.
        // No further calls will be made to other media browsing methods.

        null
    } else MediaBrowserServiceCompat.BrowserRoot(MY_MEDIA_ROOT_ID, null)

Java

@Override
public BrowserRoot onGetRoot(String clientPackageName, int clientUid,
    Bundle rootHints) {

    // Verify that the specified package is allowed to access your
    // content. You'll need to write your own logic to do this.
    if (!isValid(clientPackageName, clientUid)) {
        // If the request comes from an untrusted package, return null.
        // No further calls will be made to other media browsing methods.

        return null;
    }

    return new MediaBrowserServiceCompat.BrowserRoot(MY_MEDIA_ROOT_ID, null);
}

דוגמה מפורטת לשיטה הזו מופיעה בonGetRoot באפליקציית הדוגמה Universal Android Music Player ב-GitHub.

הוספת אימות של חבילה

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

לדוגמה, אפשר להגביל את הגישה לתוכן של האפליקציה לרשימה של חבילות שאושרו:

  • משווים את clientPackageName לרשימת ההיתרים.
  • מאמתים את האישור ששימש לחתימה על ה-APK של החבילה.

אם אי אפשר לאמת את החבילה, צריך לחזור אל null כדי לחסום את הגישה לתוכן.

כדי לספק לאפליקציות מערכת, כמו Android Auto ו-AAOS, גישה לתוכן שלכם, השירות צריך להחזיר ערך שאינו null‏ BrowserRoot כשמפעילים את השיטה onGetRoot באפליקציות המערכת האלה.

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

בקטע הקוד הזה מוצג איך השירות יכול לאמת שהחבילה שמבצעת את הקריאה היא אפליקציית מערכת:

fun isKnownCaller(
    callingPackage: String,
    callingUid: Int
): Boolean {
    ...
    val isCallerKnown = when {
       // If the system is making the call, allow it.
       callingUid == Process.SYSTEM_UID -> true
       // If the app was signed by the same certificate as the platform
       // itself, also allow it.
       callerSignature == platformSignature -> true
       // ... more cases
    }
    return isCallerKnown
}

קטע הקוד הזה הוא קטע מתוך המחלקה PackageValidator באפליקציית הדוגמה Universal Android Music Player ב-GitHub. בדוגמה המפורטת יותר של יישום אימות חבילות בשיטת onGetRoot של השירות.

בנוסף להרשאה לאפליקציות מערכת, צריך לאפשר ל-Google Assistant להתחבר אל MediaBrowserService. ל-Google Assistant יש שמות חבילה נפרדים לשימוש בטלפון, כולל Android Auto ו-Android AAOS.

הטמעה של onLoadChildren

אחרי קבלת אובייקט צומת השורש, מערכות Android Auto ו-AAOS יוצרות תפריט ברמה העליונה על ידי קריאה ל-onLoadChildren באובייקט צומת השורש כדי לקבל את צאצאיו. אפליקציות לקוח יוצרות תפריטי משנה על ידי קריאה לאותה שיטה באמצעות אובייקטים של צומתי צאצא.

כל צומת בהיררכיית התוכן מיוצג על ידי אובייקט MediaBrowserCompat.MediaItem. כל פריט מדיה מזוהה באמצעות מחרוזת מזהה ייחודית. אפליקציות לקוח מתייחסות למחרוזות המזהים האלה כאל טוקנים אטומים.

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

בקטע הקוד הזה מוצגת הטמעה של onLoadChildren

Kotlin

override fun onLoadChildren(
    parentMediaId: String,
    result: Result<List<MediaBrowserCompat.MediaItem>>
) {
    // Assume for example that the music catalog is already loaded/cached.

    val mediaItems: MutableList&lt;MediaBrowserCompat.MediaItem> = mutableListOf()

    // Check if this is the root menu:
    if (MY_MEDIA_ROOT_ID == parentMediaId) {

        // Build the MediaItem objects for the top level
        // and put them in the mediaItems list.
    } else {

        // Examine the passed parentMediaId to see which submenu we're at
        // and put the descendants of that menu in the mediaItems list.
    }
    result.sendResult(mediaItems)
}

Java

@Override
public void onLoadChildren(final String parentMediaId,
    final Result&lt;List&lt;MediaBrowserCompat.MediaItem>> result) {

    // Assume for example that the music catalog is already loaded/cached.

    List&lt;MediaBrowserCompat.MediaItem> mediaItems = new ArrayList&lt;>();

    // Check if this is the root menu:
    if (MY_MEDIA_ROOT_ID.equals(parentMediaId)) {

        // Build the MediaItem objects for the top level
        // and put them in the mediaItems list.
    } else {

        // Examine the passed parentMediaId to see which submenu we're at
        // and put the descendants of that menu in the mediaItems list.
    }
    result.sendResult(mediaItems);
}

דוגמה לשימוש בשיטה הזו מופיעה ב-onLoadChildren באפליקציית הדוגמה Universal Android Music Player ב-GitHub.

מבנה תפריט השורש

יש מגבלות ספציפיות לגבי המבנה של תפריט השורש ב-Android Auto וב-Android Automotive OS. המידע הזה מועבר אל MediaBrowserService באמצעות רמזים של שורש, שאפשר לקרוא אותם באמצעות ארגומנט החבילה שמועבר אל onGetRoot(). אם פועלים לפי הרמזים האלה, המערכת מציגה את תוכן השורש ככרטיסיות ניווט. אם לא תפעלו לפי ההצעות האלה, יכול להיות שהמערכת תסיר חלק מהתוכן הבסיסי או תגביל את האפשרות למצוא אותו.

תוכן ברמת הבסיס מוצג ככרטיסיות ניווט

איור 1. תוכן הבסיס מוצג ככרטיסיות ניווט.

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

Kotlin

import androidx.media.utils.MediaConstants

override fun onGetRoot(
    clientPackageName: String,
    clientUid: Int,
    rootHints: Bundle
): BrowserRoot {

  val maximumRootChildLimit = rootHints.getInt(
      MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_LIMIT,
      /* defaultValue= */ 4)
  val supportedRootChildFlags = rootHints.getInt(
      MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS,
      /* defaultValue= */ MediaItem.FLAG_BROWSABLE)

  // Rest of method...
}

Java

import androidx.media.utils.MediaConstants;

// Later, in your MediaBrowserServiceCompat.
@Override
public BrowserRoot onGetRoot(
    String clientPackageName, int clientUid, Bundle rootHints) {

    int maximumRootChildLimit = rootHints.getInt(
        MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_LIMIT,
        /* defaultValue= */ 4);
    int supportedRootChildFlags = rootHints.getInt(
        MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS,
        /* defaultValue= */ MediaItem.FLAG_BROWSABLE);

    // Rest of method...
}

אפשר לבחור לפצל את הלוגיקה של מבנה היררכיית התוכן על סמך הערכים של הרמזים האלה, במיוחד אם ההיררכיה משתנה בין MediaBrowser שילובים מחוץ ל-Android Auto ול-AAOS.

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

בנוסף לרמזים לגבי שורש, כדאי להשתמש בהנחיות האלה כדי להציג כרטיסיות בצורה אופטימלית:

  • סמלים מונוכרומטיים (רצוי לבנים) לכל פריט בכרטיסייה

  • תוויות קצרות ומשמעותיות לכל פריט בכרטיסייה (תוויות קצרות מקטינות את הסיכוי שהתוויות ייחתכו)