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

מערכות 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, גישה לתוכן שלכם, השירות צריך להחזיר BrowserRoot שאינו null כשמפעילים את השיטה 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 יש שמות חבילה שונים לאפליקציות לנייד ולאפליקציות ל-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.

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

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

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

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