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

‫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 של השירות.

בנוסף לאישור אפליקציות מערכת, צריך לאשר ל-Gemini או ל-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.

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

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

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

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