سلسله مراتب محتوای خود را بسازید

اندروید اتو و اندروید اتو او اس (AAOS) سرویس مرورگر رسانه برنامه شما را فراخوانی می‌کنند تا بفهمند کدام محتوا در دسترس است. برای پشتیبانی از این قابلیت، شما این دو متد را در سرویس مرورگر رسانه خود پیاده‌سازی می‌کنید.

پیاده‌سازی onGetRoot

متد onGetRoot سرویس شما، اطلاعاتی در مورد گره ریشه سلسله مراتب محتوای شما را برمی‌گرداند. Android Auto و AAOS از این گره ریشه برای درخواست بقیه محتوای شما با استفاده از متد onLoadChildren استفاده می‌کنند. این قطعه کد، پیاده‌سازی متد onGetRoot را نشان می‌دهد:

کاتلین

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)

جاوا

@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 به محتوای شما دسترسی داشته باشند، سرویس شما باید هنگام فراخوانی متد onGetRoot توسط این برنامه‌های سیستمی، یک BrowserRoot غیر تهی (non-null) برگرداند.

امضای برنامه سیستم 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 سرویس خود، به آن کلاس مراجعه کنید.

علاوه بر اجازه دادن به برنامه‌های سیستمی، باید به دستیار گوگل اجازه دهید به MediaBrowserService شما متصل شود. دستیار گوگل از نام‌های بسته مختلفی برای برنامه‌های موبایل و AAOS خود استفاده می‌کند.

پیاده‌سازی onLoadChildren

پس از دریافت شیء گره ریشه شما، Android Auto و AAOS با فراخوانی onLoadChildren روی شیء گره ریشه، یک منوی سطح بالا برای دریافت فرزندان آن ایجاد می‌کنند. برنامه‌های کلاینت با فراخوانی همین روش با استفاده از اشیاء گره فرزند، زیرمنوها را می‌سازند.

هر گره در سلسله مراتب محتوای شما توسط یک شیء MediaBrowserCompat.MediaItem نمایش داده می‌شود. هر یک از این آیتم‌های رسانه‌ای توسط یک رشته شناسه منحصر به فرد شناسایی می‌شوند. برنامه‌های کلاینت با این رشته‌های شناسه به عنوان توکن‌های مبهم رفتار می‌کنند.

وقتی یک برنامه‌ی کلاینت می‌خواهد به یک زیرمنو برود یا یک آیتم رسانه‌ای را پخش کند، توکن را ارسال می‌کند. برنامه‌ی شما مسئول مرتبط کردن توکن با آیتم رسانه‌ای مناسب است.

این قطعه کد، پیاده‌سازی onLoadChildren را نشان می‌دهد.

کاتلین

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)
}

جاوا

@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 در گیت‌هاب مراجعه کنید.

ساختار منوی ریشه

اندروید اتو و اندروید اتو او اس محدودیت‌های خاصی در مورد ساختار منوی ریشه دارند. این محدودیت‌ها از طریق راهنمایی‌های ریشه به MediaBrowserService اطلاع داده می‌شوند که می‌توان آن‌ها را از طریق آرگومان Bundle ارسال شده به onGetRoot() خواند. در صورت رعایت این راهنمایی‌ها، سیستم محتوای ریشه را به صورت تب‌های ناوبری نمایش می‌دهد. اگر این راهنمایی‌ها را رعایت نکنید، ممکن است برخی از محتوای ریشه حذف شوند یا توسط سیستم کمتر قابل کشف باشند.

محتوای ریشه به صورت تب‌های ناوبری نمایش داده می‌شود

شکل ۱. محتوای ریشه که به صورت تب‌های ناوبری نمایش داده می‌شود.

با اعمال این نکات، سیستم محتوای ریشه را به صورت تب‌های ناوبری نمایش می‌دهد. با عدم اعمال این نکات، ممکن است برخی از محتوای ریشه حذف شوند یا کمتر قابل کشف باشند. این نکات به شرح زیر منتقل می‌شوند:

کاتلین

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...
}

جاوا

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 متفاوت باشد، شاخه‌بندی کنید.

برای مثال، اگر معمولاً یک آیتم قابل پخش ریشه را نمایش می‌دهید، ممکن است بخواهید به دلیل مقدار اشاره پرچم‌های پشتیبانی‌شده، آن را زیر یک آیتم قابل مرور ریشه قرار دهید.

جدا از نکات مربوط به ریشه، از این دستورالعمل‌ها برای رندر بهینه تب‌ها استفاده کنید:

  • آیکون‌های تک‌رنگ (ترجیحاً سفید) برای هر آیتم تب

  • برچسب‌های کوتاه و معنادار برای هر آیتم تب (برچسب‌های کوتاه احتمال کوتاه شدن برچسب‌ها را کاهش می‌دهند)