إنشاء التدرّج الهرمي للمحتوى

تستدعي كلّ من 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 عندما تستدعي تطبيقات النظام هذه الطريقة onGetRoot.

تختلف توقيعات تطبيقات نظام التشغيل Android Automotive حسب ماركة السيارة وطرازها. احرص على السماح بالاتصالات من جميع تطبيقات النظام لتوفير الدعم لنظام التشغيل Android Automotive.

تعرض مقتطفة الرمز البرمجي هذه كيف يمكن لخدمتك التحقّق من أنّ الحزمة التي يتم استدعاؤها هي تطبيق نظام:

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" بالاتصال بـ MediaBrowserService. يستخدم "مساعد Google" أسماء حِزم منفصلة للهاتف، بما في ذلك 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 قيودًا معيّنة على بنية القائمة الجذرية. يتم إرسال هذه المعلومات إلى 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...
}

يمكنك اختيار تقسيم منطق بنية تسلسل المحتوى الهرمي استنادًا إلى قيم هذه التلميحات، خاصةً إذا كان التسلسل الهرمي يختلف بين عمليات الدمج خارج Android Auto وAAOS.MediaBrowser

على سبيل المثال، إذا كنت تعرض عادةً عنصرًا قابلاً للتشغيل على المستوى الأعلى، قد تحتاج إلى تضمينه ضمن عنصر قابل للتصفّح على المستوى الأعلى بدلاً من ذلك بسبب قيمة تلميح العلامات المتوافقة.

بالإضافة إلى تلميحات الجذر، اتّبِع الإرشادات التالية لعرض علامات التبويب على النحو الأمثل:

  • رموز أحادية اللون (يفضّل أن تكون بيضاء) لكل عنصر من عناصر علامات التبويب

  • تصنيفات قصيرة وذات معنى لكل عنصر من عناصر علامات التبويب (تقلّل التصنيفات القصيرة من احتمالية اقتطاع التصنيفات)