"مساعد Google" وتطبيقات الوسائط

يتيح لك "مساعد Google" استخدام الطلبات الصوتية للتحكّم في العديد من الأجهزة، مثل Google Home وهاتفك وغير ذلك. يتضمّن الجهاز إمكانية مدمَجة لفهم طلبات الوسائط ("تشغيل أغنية لبيونسيه") ويتوافق مع عناصر التحكّم في الوسائط (مثل الإيقاف المؤقت والتخطّي والتقديم السريع والنقر على رمز الإعجاب).

يتواصل "مساعد Google" مع تطبيقات الوسائط على Android باستخدام جلسة وسائط. ويمكنه استخدام النوايا أو الخدمات لتشغيل تطبيقك وبدء التشغيل. للحصول على أفضل النتائج، يجب أن يوفّر تطبيقك جميع الميزات الموضّحة في هذه الصفحة.

استخدام جلسة وسائط

يجب أن يوفّر كل تطبيق صوت وفيديو جلسة وسائط كي يتمكّن "مساعد Google" من تشغيل أدوات التحكّم في النقل بعد بدء التشغيل.

يُرجى العِلم أنّه على الرغم من أنّ "مساعد Google" يستخدم الإجراءات المدرَجة في هذا القسم فقط، فإنّ أفضل ممارسة هي تنفيذ جميع واجهات برمجة التطبيقات الخاصة بالإعداد والتشغيل لضمان التوافق مع التطبيقات الأخرى. بالنسبة إلى أي إجراءات لا تتوافق معها، يمكن أن تعرض عمليات معاودة الاتصال الخاصة بجلسة الوسائط ببساطة رسالة خطأ باستخدام ERROR_CODE_NOT_SUPPORTED.

فعِّل عناصر التحكّم في الوسائط والنقل من خلال ضبط العلامات التالية في عنصر MediaSession الخاص بتطبيقك:

Kotlin

session.setFlags(
        MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS or
        MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS
)

Java

session.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS |
    MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);

يجب أن تحدّد جلسة الوسائط في تطبيقك الإجراءات التي تتيحها، وأن تنفّذ عمليات معاودة الاتصال الخاصة بجلسة الوسائط. عليك الإفصاح عن الإجراءات المتوافقة في ملف setActions().

يُعدّ مشروع Universal Android Music Player نموذجًا جيدًا لكيفية إعداد جلسة وسائط.

إجراءات التشغيل

من أجل بدء التشغيل من إحدى الخدمات، يجب أن تتضمّن جلسة الوسائط إجراءات PLAY وعمليات رد الاتصال التالية:

الإجراء رد الاتصال
ACTION_PLAY onPlay()
ACTION_PLAY_FROM_SEARCH onPlayFromSearch()
ACTION_PLAY_FROM_URI (*) onPlayFromUri()

يجب أن تنفّذ جلستك أيضًا إجراءات PREPARE وعمليات رد الاتصال الخاصة بها:

الإجراء رد الاتصال
ACTION_PREPARE onPrepare()
ACTION_PREPARE_FROM_SEARCH onPrepareFromSearch()
ACTION_PREPARE_FROM_URI (*) onPrepareFromUri()

(*) لا تعمل الإجراءات المستندة إلى معرّف الموارد الموحّد (URI) في "مساعد Google" إلا مع الشركات التي تقدّم معرّفات الموارد الموحّد (URI) إلى Google. لمزيد من المعلومات حول وصف محتوى الوسائط لـ Google، يُرجى الاطّلاع على مهام لمحتوى الوسائط.

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

تحليل طلبات البحث

عندما يبحث المستخدم عن ملف وسائط محدّد، مثل "تشغيل موسيقى الجاز على [اسم تطبيقك]" أو "الاستماع إلى [عنوان الأغنية]"، تتلقّى طريقة رد الاتصال onPrepareFromSearch() أو onPlayFromSearch() معلمات طلب البحث وحزمة إضافات.

يجب أن يحلّل تطبيقك طلب البحث الصوتي ويبدأ التشغيل باتّباع الخطوات التالية:

  1. استخدِم حزمة الإضافات وسلسلة طلب البحث التي تم إرجاعها من البحث الصوتي لفلترة النتائج.
  2. أنشئ قائمة تشغيل استنادًا إلى هذه النتائج.
  3. تشغيل وسائط الإعلام الأكثر صلة من النتائج

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

تتوفّر الإضافات التالية في نظام التشغيل Android Automotive وAndroid Auto:

يوضّح مقتطف الرمز التالي كيفية إلغاء طريقة onPlayFromSearch() في عملية تنفيذ MediaSession.Callback لتحليل طلب البحث الصوتي وبدء التشغيل:

Kotlin

override fun onPlayFromSearch(query: String?, extras: Bundle?) {
    if (query.isNullOrEmpty()) {
        // The user provided generic string e.g. 'Play music'
        // Build appropriate playlist queue
    } else {
        // Build a queue based on songs that match "query" or "extras" param
        val mediaFocus: String? = extras?.getString(MediaStore.EXTRA_MEDIA_FOCUS)
        if (mediaFocus == MediaStore.Audio.Artists.ENTRY_CONTENT_TYPE) {
            isArtistFocus = true
            artist = extras.getString(MediaStore.EXTRA_MEDIA_ARTIST)
        } else if (mediaFocus == MediaStore.Audio.Albums.ENTRY_CONTENT_TYPE) {
            isAlbumFocus = true
            album = extras.getString(MediaStore.EXTRA_MEDIA_ALBUM)
        }

        // Implement additional "extras" param filtering
    }

    // Implement your logic to retrieve the queue
    var result: String? = when {
        isArtistFocus -> artist?.also {
            searchMusicByArtist(it)
        }
        isAlbumFocus -> album?.also {
            searchMusicByAlbum(it)
        }
        else -> null
    }
    result = result ?: run {
        // No focus found, search by query for song title
        query?.also {
            searchMusicBySongTitle(it)
        }
    }

    if (result?.isNotEmpty() == true) {
        // Immediately start playing from the beginning of the search results
        // Implement your logic to start playing music
        playMusic(result)
    } else {
        // Handle no queue found. Stop playing if the app
        // is currently playing a song
    }
}

Java

@Override
public void onPlayFromSearch(String query, Bundle extras) {
    if (TextUtils.isEmpty(query)) {
        // The user provided generic string e.g. 'Play music'
        // Build appropriate playlist queue
    } else {
        // Build a queue based on songs that match "query" or "extras" param
        String mediaFocus = extras.getString(MediaStore.EXTRA_MEDIA_FOCUS);
        if (TextUtils.equals(mediaFocus,
                MediaStore.Audio.Artists.ENTRY_CONTENT_TYPE)) {
            isArtistFocus = true;
            artist = extras.getString(MediaStore.EXTRA_MEDIA_ARTIST);
        } else if (TextUtils.equals(mediaFocus,
                MediaStore.Audio.Albums.ENTRY_CONTENT_TYPE)) {
            isAlbumFocus = true;
            album = extras.getString(MediaStore.EXTRA_MEDIA_ALBUM);
        }

        // Implement additional "extras" param filtering
    }

    // Implement your logic to retrieve the queue
    if (isArtistFocus) {
        result = searchMusicByArtist(artist);
    } else if (isAlbumFocus) {
        result = searchMusicByAlbum(album);
    }

    if (result == null) {
        // No focus found, search by query for song title
        result = searchMusicBySongTitle(query);
    }

    if (result != null && !result.isEmpty()) {
        // Immediately start playing from the beginning of the search results
        // Implement your logic to start playing music
        playMusic(result);
    } else {
        // Handle no queue found. Stop playing if the app
        // is currently playing a song
    }
}

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

التعامل مع طلبات البحث الفارغة

إذا تم استدعاء onPrepare() أو onPlay() أو onPrepareFromSearch() أو onPlayFromSearch() بدون طلب بحث، يجب أن يشغّل تطبيق الوسائط المحتوى "الحالي". إذا لم تكن هناك وسائط حالية، يجب أن يحاول التطبيق تشغيل شيء ما، مثل أغنية من قائمة التشغيل الأخيرة أو قائمة عشوائية. يستخدم المساعد واجهات برمجة التطبيقات هذه عندما يطلب المستخدم "تشغيل الموسيقى على [اسم تطبيقك]" بدون تقديم معلومات إضافية.

عندما يقول المستخدم "تشغيل الموسيقى على [اسم تطبيقك]"، يحاول نظام التشغيل Android Automotive أو Android Auto تشغيل تطبيقك وتشغيل الصوت من خلال استدعاء طريقة onPlayFromSearch() في تطبيقك. ومع ذلك، بما أنّ المستخدم لم يذكر اسم الوسائط، تتلقّى الطريقة onPlayFromSearch() مَعلمة طلب بحث فارغة. في هذه الحالات، يجب أن يستجيب تطبيقك من خلال تشغيل الصوت على الفور، مثل أغنية من قائمة التشغيل الأخيرة أو قائمة عشوائية.

تحديد إمكانية استخدام الإجراءات الصوتية القديمة

في معظم الحالات، يمنح التعامل مع إجراءات التشغيل الموضّحة أعلاه تطبيقك كل وظائف التشغيل التي يحتاجها. ومع ذلك، تتطلّب بعض الأنظمة أن يحتوي تطبيقك على فلتر Intent للبحث. يجب أن توضّح في ملفات البيان لتطبيقك توافقه مع فلتر Intent هذا.

أدرِج الرمز التالي في ملف البيان لتطبيق على الهاتف:

<activity>
    <intent-filter>
        <action android:name=
             "android.media.action.MEDIA_PLAY_FROM_SEARCH" />
        <category android:name=
             "android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

عناصر التحكّم في التشغيل

بعد أن تصبح جلسة الوسائط في تطبيقك نشطة، يمكن أن يوجّه "مساعد Google" طلبات صوتية للتحكّم في التشغيل وتعديل البيانات الوصفية للوسائط. لكي يعمل ذلك، يجب أن يتيح الرمز البرمجي الإجراءات التالية وينفّذ عمليات الرجوع المقابلة:

الإجراء رد الاتصال الوصف
ACTION_SKIP_TO_NEXT onSkipToNext() الفيديو التالي
ACTION_SKIP_TO_PREVIOUS onSkipToPrevious() الأغنية السابقة
ACTION_PAUSE, ACTION_PLAY_PAUSE onPause() إيقاف مؤقت
ACTION_STOP onStop() إيقاف
ACTION_PLAY onPlay() استئناف
ACTION_SEEK_TO onSeekTo() الترجيع بمقدار 30 ثانية
ACTION_SET_RATING onSetRating(android.support.v4.media.RatingCompat) يعجبني/لا يعجبني
ACTION_SET_CAPTIONING_ENABLED onSetCaptioningEnabled(boolean) تفعيل/إيقاف الترجمة والشرح

ملاحظة:

  • لكي تعمل أوامر البحث، يجب أن يكون تطبيق PlaybackState محدّثًا بأحدث إصدار من state, position, playback speed, and update time. يجب أن يستدعي التطبيق setPlaybackState() عند تغيُّر الحالة.
  • يجب أن يحرص تطبيق الوسائط أيضًا على تعديل البيانات الوصفية لجلسة الوسائط باستمرار. يتيح ذلك طرح أسئلة مثل "ما هي الأغنية التي يتم تشغيلها؟" يجب أن يستدعي التطبيق setMetadata() عند تغيير الحقول السارية (مثل عنوان الأغنية والفنان والاسم).
  • يجب ضبط MediaSession.setRatingType() للإشارة إلى نوع التقييم الذي يتيحه التطبيق، ويجب أن ينفّذ التطبيق onSetRating(). إذا كان التطبيق لا يتيح التقييم، يجب ضبط نوع التقييم على RATING_NONE.

من المرجّح أن تختلف الإجراءات الصوتية التي تتيحها حسب نوع المحتوى.

نوع المحتوى الإجراءات المطلوبة
موسيقى

يجب أن تتوافق مع: التشغيل والإيقاف المؤقت والإيقاف والانتقال إلى التالي والانتقال إلى السابق

ننصح بشدة بتوفير الدعم لما يلي: Seek To

بودكاست

يجب أن تتوافق مع: التشغيل والإيقاف المؤقت والإيقاف والانتقال إلى

نوصي بتوفير الدعم لما يلي: الانتقال إلى التالي والانتقال إلى السابق

كتاب مسموع يجب أن تتوافق مع: التشغيل والإيقاف المؤقت والإيقاف والانتقال إلى
راديو يجب أن تتوافق مع: التشغيل والإيقاف المؤقت والإيقاف
أخبار يجب أن تتوافق مع: التشغيل والإيقاف المؤقت والإيقاف والانتقال إلى التالي والانتقال إلى السابق
فيديو

يجب أن تتوافق مع: التشغيل والإيقاف المؤقت والإيقاف والبحث عن موضع معيّن والترجيع والتقديم السريع

ننصح بشدة بتوفير الدعم لما يلي: الانتقال إلى التالي والانتقال إلى السابق

يجب أن يتيح منتجك أكبر عدد ممكن من الإجراءات المذكورة أعلاه، مع الاستجابة بشكل سليم لأي إجراءات أخرى. على سبيل المثال، إذا كان بإمكان مستخدمي الإصدار المميز فقط الرجوع إلى العنصر السابق، قد يظهر خطأ إذا طلب مستخدم الإصدار المجاني من "مساعد Google" الرجوع إلى العنصر السابق. اطّلِع على قسم معالجة الأخطاء للحصول على مزيد من الإرشادات.

أمثلة على طلبات البحث الصوتية التي يمكنك تجربتها

يقدّم الجدول التالي بعض نماذج طلبات البحث التي يجب استخدامها أثناء اختبار عملية التنفيذ:

MediaSession Callback عبارة "Ok Google" التي يجب استخدامها
onPlay()

تشغيل ("Play")

استئناف ("Resume")

onPlayFromSearch()
onPlayFromUri()
الموسيقى

"تشغيل موسيقى أو أغاني على (اسم التطبيق)": هذا طلب بحث فارغ.

"تشغيل (أغنية | فنان | ألبوم | نوع موسيقي | قائمة تشغيل) على (اسم التطبيق)"

الراديو "تشغيل (التردّد | المحطة) على (اسم التطبيق)"
كتاب مسموع

"أريد الاستماع إلى كتابي المسموع على (اسم التطبيق)".

"اقرأ (الكتاب المسموع) على (اسم التطبيق)".

برامج بودكاست "تشغيل (برنامج البودكاست) على (اسم التطبيق)"
onPause() إيقاف مؤقت ("Pause")
onStop() إيقاف ("Stop")
onSkipToNext() "الأغنية التالية (song | episode | track)"
onSkipToPrevious() "السابق (أغنية | حلقة | مقطع صوتي)"
onSeekTo()

"إعادة التشغيل"

"تخطّي ## ثانية"

"الرجوع ## دقيقة"

غير متوفر (يجب إبقاء MediaMetadata محدّثًا) "ما الذي يتم تشغيله الآن؟"

الأخطاء

يتعامل "مساعد Google" مع الأخطاء التي تحدث في جلسة وسائط عند حدوثها، ويُبلغ المستخدمين بها. احرص على أنّ جلسة الوسائط تعدّل حالة النقل ورمز الخطأ في PlaybackState بشكل صحيح، كما هو موضّح في العمل مع جلسة وسائط. يتعرّف &quot;مساعد Google&quot; على جميع رموز الخطأ التي تعرضها الدالة getErrorCode().

الحالات التي يتم التعامل معها بشكل خاطئ عادةً

في ما يلي بعض الأمثلة على حالات الخطأ التي يجب التأكّد من التعامل معها بشكل صحيح:

  • على المستخدم تسجيل الدخول
    • اضبط رمز الخطأ PlaybackState على ERROR_CODE_AUTHENTICATION_EXPIRED.
    • اضبط رسالة الخطأ PlaybackState.
    • إذا كان ذلك مطلوبًا لتشغيل المحتوى، اضبط حالة PlaybackState على STATE_ERROR، وإلا احتفظ ببقية PlaybackState كما هي.
  • يطلب المستخدم إجراءً غير متاح
    • اضبط رمز الخطأ PlaybackState بشكل مناسب. على سبيل المثال، اضبط قيمة PlaybackState على ERROR_CODE_NOT_SUPPORTED إذا كان الإجراء غير متاح أو ERROR_CODE_PREMIUM_ACCOUNT_REQUIRED إذا كان الإجراء محميًا من خلال تسجيل الدخول.
    • اضبط رسالة الخطأ PlaybackState.
    • احتفِظ ببقية PlaybackState كما هي.
  • طلبات المستخدمين للحصول على محتوى غير متوفّر في التطبيق
    • اضبط رمز الخطأ PlaybackState بشكل مناسب. على سبيل المثال، استخدِم ERROR_CODE_NOT_AVAILABLE_IN_REGION.
    • اضبط رسالة الخطأ PlaybackState.
    • اضبط حالة PlaybackSate على STATE_ERROR لإيقاف التشغيل مؤقتًا، أو احتفظ ببقية PlaybackState كما هي.
  • يطلب المستخدم محتوًى لا يتوفّر فيه تطابق تام. على سبيل المثال، قد يطلب مستخدم لديه حساب مجاني محتوًى متاحًا فقط للمستخدمين الذين لديهم حساب مدفوع.
    • ننصحك بعدم عرض رسالة خطأ، بل إعطاء الأولوية للعثور على محتوى مشابه لتشغيله. سيتولّى &quot;مساعد Google&quot; نطق الرد الصوتي الأكثر صلة بالموضوع قبل بدء التشغيل.

التشغيل مع تحديد نية

يمكن للمساعد تشغيل تطبيق صوتي أو فيديو وبدء التشغيل من خلال إرسال intent مع رابط لصفحة معيّنة.

يمكن أن يأتي الغرض ورابط الصفحة في التطبيق من مصادر مختلفة:

  • عندما يبدأ &quot;مساعد Google&quot; تطبيقًا على الأجهزة الجوّالة، يمكنه استخدام &quot;بحث Google&quot; لاسترداد المحتوى الذي تم ترميزه والذي يوفّر إجراء مشاهدة مع رابط.
  • عندما يبدأ "مساعد Google" تطبيق بث تلفزيوني، يجب أن يتضمّن تطبيقك مزوّد خدمة البحث على التلفزيون لعرض معرّفات الموارد المنتظمة (URI) الخاصة بمحتوى الوسائط. يرسل &quot;مساعد Google&quot; طلب بحث إلى مقدّم المحتوى، ويجب أن يعرض هذا الطلب غرضًا يحتوي على معرّف موارد منتظم (URI) للرابط لصفحة في التطبيق وإجراءً اختياريًا. إذا عرض طلب البحث إجراءً في Intent، يرسل &quot;مساعد Google&quot; هذا الإجراء وعنوان URI إلى تطبيقك. وإذا لم يحدّد موفّر المحتوى إجراءً، سيضيف &quot;مساعد Google&quot; ACTION_VIEW إلى Intent.

يضيف &quot;مساعد Google&quot; السمة الإضافية EXTRA_START_PLAYBACK بالقيمة true إلى الغرض الذي يرسله إلى تطبيقك. يجب أن يبدأ تطبيقك التشغيل عند تلقّي غرض يتضمّن EXTRA_START_PLAYBACK.

التعامل مع الأهداف أثناء النشاط

يمكن للمستخدمين الطلب من &quot;مساعد Google&quot; تشغيل محتوى أثناء تشغيل تطبيقك لمحتوى من طلب سابق. وهذا يعني أنّ تطبيقك يمكنه تلقّي نوايا جديدة لبدء التشغيل بينما يكون نشاط التشغيل قد تم إطلاقه وتفعيله.

يجب أن تتجاوز الأنشطة التي تتوافق مع الأهداف التي تتضمّن روابط لصفحات معيّنة onNewIntent() الطلبات الجديدة.

عند بدء التشغيل، قد يضيف &quot;مساعد Google&quot; علامات إضافية إلى الغرض الذي يرسله إلى تطبيقك، وتحديدًا قد يضيف FLAG_ACTIVITY_CLEAR_TOP أو FLAG_ACTIVITY_NEW_TASK أو كليهما. على الرغم من أنّ الرمز لا يحتاج إلى التعامل مع هذه العلامات، إلا أنّ نظام التشغيل Android يستجيب لها. قد يؤثر ذلك في سلوك تطبيقك عند وصول طلب تشغيل ثانٍ باستخدام معرّف URI جديد بينما لا يزال معرّف URI السابق قيد التشغيل. ننصحك باختبار طريقة استجابة تطبيقك في هذه الحالة. يمكنك استخدام أداة سطر الأوامر adb لمحاكاة الموقف (الثابت 0x14000000 هو OR المنطقي الثنائي للعلامتَين):

adb shell 'am start -a android.intent.action.VIEW --ez android.intent.extra.START_PLAYBACK true -d "<first_uri>"' -f 0x14000000
adb shell 'am start -a android.intent.action.VIEW --ez android.intent.extra.START_PLAYBACK true -d "<second_uri>"' -f 0x14000000

التشغيل من خدمة

إذا كان تطبيقك يتضمّن media browser service يسمح بالاتصالات من &quot;مساعد Google&quot;، يمكن أن يبدأ &quot;مساعد Google&quot; التطبيق من خلال التواصل مع media session الخاص بالخدمة. يجب ألا تبدأ خدمة متصفّح الوسائط نشاطًا أبدًا. سيطلق "المساعد" نشاطك استنادًا إلى PendingIntent الذي تحدّده باستخدام setSessionActivity().

احرص على ضبط MediaSession.Token عند تهيئة خدمة متصفّح الوسائط. يجب ضبط إجراءات التشغيل المتوافقة في جميع الأوقات، بما في ذلك أثناء عملية التهيئة. يتوقّع &quot;مساعد Google&quot; أن يضبط تطبيق الوسائط إجراءات التشغيل قبل أن يرسل &quot;مساعد Google&quot; أمر التشغيل الأول.

للبدء من إحدى الخدمات، ينفّذ "مساعد Google" واجهات برمجة التطبيقات الخاصة ببرنامج تصفّح الوسائط. ويُجري عمليات استدعاء TransportControls التي تؤدي إلى تشغيل عمليات ردّ PLAY على جلسة الوسائط في تطبيقك.

يوضّح المخطّط البياني التالي ترتيب المكالمات التي ينشئها &quot;مساعد Google&quot; وعمليات معاودة الاتصال المقابلة لجلسة الوسائط. (لا يتم إرسال دوال رد الاتصال الخاصة بالتحضير إلا إذا كان تطبيقك يتيحها). جميع عمليات الاستدعاء غير متزامنة. لا ينتظر &quot;مساعد Google&quot; أي ردّ من تطبيقك.

بدء التشغيل باستخدام جلسة وسائط

عندما يوجّه المستخدم طلبًا صوتيًا لتشغيل المحتوى، يردّ "مساعد Google" بإعلان قصير. بعد اكتمال الإعلان، ينفّذ "مساعد Google" إجراء PLAY. ولا تنتظر أي حالة تشغيل محدّدة.

إذا كان تطبيقك يتيح إجراءات ACTION_PREPARE_*، يطلب "مساعد Google" تنفيذ الإجراء PREPARE قبل بدء الإعلان.

الاتصال بخدمة MediaBrowserService

لاستخدام خدمة لبدء تطبيقك، يجب أن يتمكّن &quot;مساعد Google&quot; من الاتصال بـ MediaBrowserService الخاص بالتطبيق واسترداد MediaSession.Token. يتم التعامل مع طلبات الربط في طريقة onGetRoot() الخاصة بالخدمة. هناك طريقتان للتعامل مع الطلبات:

  • قبول جميع طلبات الربط
  • قبول طلبات الربط من تطبيق "مساعد Google" فقط

قبول جميع طلبات الربط

يجب عرض BrowserRoot للسماح لـ "مساعد Google" بإرسال أوامر إلى جلسة الوسائط. أسهل طريقة هي السماح لجميع تطبيقات MediaBrowser بالاتصال بخدمة MediaBrowserService. يجب عرض BrowserRoot غير فارغ. إليك الرمز الساري من مشغّل الموسيقى الشامل:

Kotlin

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

    // To ensure you are not allowing any arbitrary app to browse your app's contents, you
    // need to check the origin:
    if (!packageValidator.isCallerAllowed(this, clientPackageName, clientUid)) {
        // If the request comes from an untrusted package, return an empty browser root.
        // If you return null, then the media browser will not be able to connect and
        // no further calls will be made to other media browsing methods.
        Log.i(TAG, "OnGetRoot: Browsing NOT ALLOWED for unknown caller. Returning empty "
                + "browser root so all apps can use MediaController. $clientPackageName")
        return MediaBrowserServiceCompat.BrowserRoot(MEDIA_ID_EMPTY_ROOT, null)
    }

    // Return browser roots for browsing...
}

Java

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

    // To ensure you are not allowing any arbitrary app to browse your app's contents, you
    // need to check the origin:
    if (!packageValidator.isCallerAllowed(this, clientPackageName, clientUid)) {
        // If the request comes from an untrusted package, return an empty browser root.
        // If you return null, then the media browser will not be able to connect and
        // no further calls will be made to other media browsing methods.
        LogHelper.i(TAG, "OnGetRoot: Browsing NOT ALLOWED for unknown caller. "
                + "Returning empty browser root so all apps can use MediaController."
                + clientPackageName);
        return new MediaBrowserServiceCompat.BrowserRoot(MEDIA_ID_EMPTY_ROOT, null);
    }

    // Return browser roots for browsing...
}

قبول حزمة تطبيق "مساعد Google" وتوقيعه

يمكنك السماح لـ "مساعد Google" بالاتصال بخدمة متصفّح الوسائط من خلال البحث عن اسم الحزمة والتوقيع. سيتلقّى تطبيقك اسم الحزمة في طريقة onGetRoot الخاصة بفئة MediaBrowserService. يجب عرض BrowserRoot للسماح لـ "مساعد Google" بإرسال أوامر إلى جلسة الوسائط. يحتفظ نموذج مشغّل الموسيقى الشامل بقائمة بأسماء الحِزم والتواقيع المعروفة. في ما يلي أسماء الحِزم والتواقيع التي يستخدمها "مساعد Google".

<signature name="Google" package="com.google.android.googlequicksearchbox">
    <key release="false">19:75:b2:f1:71:77:bc:89:a5:df:f3:1f:9e:64:a6:ca:e2:81:a5:3d:c1:d1:d5:9b:1d:14:7f:e1:c8:2a:fa:00</key>
    <key release="true">f0:fd:6c:5b:41:0f:25:cb:25:c3:b5:33:46:c8:97:2f:ae:30:f8:ee:74:11:df:91:04:80:ad:6b:2d:60:db:83</key>
</signature>

<signature name="Google Assistant on Android Automotive OS" package="com.google.android.carassistant">
    <key release="false">17:E2:81:11:06:2F:97:A8:60:79:7A:83:70:5B:F8:2C:7C:C0:29:35:56:6D:46:22:BC:4E:CF:EE:1B:EB:F8:15</key>
    <key release="true">74:B6:FB:F7:10:E8:D9:0D:44:D3:40:12:58:89:B4:23:06:A6:2C:43:79:D0:E5:A6:62:20:E3:A6:8A:BF:90:E2</key>
</signature>