تفعيل تطبيق Android TV لبثّ المحتوى

1. نظرة عامة

شعار Google Cast

ستتعرَّف على هذا الدرس التطبيقي حول كيفية تعديل تطبيق حالي على Android TV لإتاحة البث والاتصال من تطبيقات المُرسِل الحالية التي تعمل بتكنولوجيا Google Cast.

ما هما Google Cast وCast Connect؟

تسمح تكنولوجيا Google Cast للمستخدمين ببث المحتوى من جهاز جوّال إلى تلفزيون. تتألف جلسة Google Cast النموذجية من عنصرين هما: تطبيق المُرسِل وتطبيق المستلِم. تبدأ تطبيقات المُرسِل، مثل تطبيقات الأجهزة الجوّالة أو المواقع الإلكترونية مثل YouTube.com، تشغيل تطبيق مستقبِل البث وتتحكّم فيه. تطبيقات أجهزة استقبال البث هي تطبيقات HTML 5 التي يتم تشغيلها على أجهزة Chromecast وAndroid TV.

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

تعتمد خدمة Cast Connect على هذه البنية الأساسية، حيث يعمل تطبيق Android TV كوحدة استقبال. تسمح مكتبة Cast Connect لتطبيق Android TV بتلقّي الرسائل وحالة بث الوسائط كما لو كان تطبيقًا لاستقبال البثّ.

ما الذي سنبنيه؟

عند إكمال هذا الدرس التطبيقي، ستتمكّن من استخدام تطبيقات مرسِلي البثّ لبثّ الفيديوهات على تطبيق Android TV. يمكن أيضًا لتطبيق Android TV التواصل مع تطبيقات المُرسِلين عبر بروتوكول البث.

المُعطيات

  • كيفية إضافة مكتبة Cast Connect إلى نموذج تطبيق ATV
  • كيفية توصيل مُرسِل البث وتشغيل تطبيق ATV
  • كيفية بدء تشغيل الوسائط على تطبيق ATV من تطبيق مُرسِل تكنولوجيا Google Cast
  • كيفية إرسال حالة الوسائط من تطبيق ATV إلى تطبيقات مرسِلي البثّ.

المتطلبات

2. الحصول على رمز النموذج

يمكنك تنزيل نموذج الرمز بالكامل على جهاز الكمبيوتر...

وفك ضغط ملف zip الذي تم تنزيله.

3- تشغيل نموذج التطبيق

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

صورة لسلسلة من الصور المصغّرة للفيديو (تم تمييز إحداها) تظهر على سطح معاينة فيديو بملء الشاشة عبارة "اتصال البثّ" تظهر في أعلى اليمين

تسجيل أجهزة المطوِّرين

لتفعيل إمكانات Cast Connect لتطوير التطبيقات، يجب تسجيل الرقم التسلسلي لجهاز Chromecast المدمَج في جهاز Android TV الذي ستستخدمه في وحدة تحكم مطوّري برامج Cast. يمكنك العثور على الرقم التسلسلي من خلال الانتقال إلى الإعدادات >. الإعدادات المفضّلة للجهاز > Chromecast Built-in > الرقم التسلسلي على Android TV. يُرجى العِلم أنّ هذا الرقم يختلف عن الرقم التسلسلي للجهاز ويجب الحصول عليه من الطريقة الموضّحة أعلاه.

صورة لشاشة Android TV تعرض جهاز "Chromecast Built-in" ورقم الإصدار والرقم التسلسلي

لأسباب تتعلق بالأمان، لن تعمل ميزة Cast Connect إلا مع التطبيقات المثبَّتة من خلال "متجر Google Play" بدون التسجيل. بعد 15 دقيقة من بدء عملية التسجيل، أعِد تشغيل جهازك.

تثبيت تطبيق المُرسِل على Android

لاختبار إرسال الطلبات من جهاز جوّال، قدّمنا تطبيقًا بسيطًا للمُرسِل يُسمى "إرسال الفيديوهات" كملف mobile-sender-0629.apk في الملف المضغوط لرمز المصدر. سوف نستفيد من ADB لتثبيت APK. في حال تثبيت إصدار مختلف من تطبيق Cast Videos، يُرجى إلغاء تثبيت هذا الإصدار من جميع الملفات الشخصية على الجهاز قبل المتابعة.

  1. فعِّل خيارات المطورين وتصحيح أخطاء USB على هاتف Android.
  2. وصِّل كابل بيانات USB لتوصيل هاتف Android بجهاز كمبيوتر التطوير.
  3. ثبِّت تطبيق "mobile-sender-0629.apk" على هاتف Android.

صورة نافذة طرفية تُشغِّل أمر adb install لتثبيت mobile-sender.apk

  1. يمكنك العثور على تطبيق المُرسِل Cast Videos على هاتف Android. رمز تطبيق مرسل فيديوهات Cast Videos

صورة لتطبيق مرسل فيديوهات Cast Videos قيد التشغيل على شاشة هاتف Android

تثبيت تطبيق Android TV

توضّح التعليمات التالية كيفية فتح نموذج التطبيق المكتمل وتشغيله في "استوديو Android":

  1. اختَر استيراد مشروع في شاشة الترحيب أو انقر على ملف > جديد > استيراد مشروع... خيارات القائمة
  2. اختَر الدليل رمز المجلدapp-done من مجلد "نموذج الرموز" وانقر على "حسنًا".
  3. انقر على ملف > مشروع مزامنة مشروع باستخدام زر Gradle في "استوديو تطبيقات Android" مزامنة المشروع مع ملفات Gradle
  4. فعِّل خيارات المطورين وتصحيح أخطاء USB على جهاز Android TV.
  5. ربط ADB بجهاز Android TV، ومن المفترض أن يظهر الجهاز في "استوديو Android". صورة تعرض جهاز Android TV وهو يظهر في شريط أدوات "استوديو Android"
  6. انقر على الزر زر التشغيل في "استوديو Android"، مثلث أخضر يشير إلى اليمينRun (تشغيل)، من المفترض أن يظهر بعد بضع ثوانٍ تطبيق ATV الذي يحمل اسم Cast Connect Codelab.

لنلعب Cast Connect باستخدام تطبيق ATV

  1. انتقِل إلى شاشة Android TV الرئيسية.
  2. افتح تطبيق مرسل الفيديو Cast من هاتف Android. انقر على زر البث رمز زر البث واختَر جهاز ATV.
  3. سيتم إطلاق تطبيق Cast Connect Codelab ATV على جهاز ATV، وسيشير زر البث في الجهاز إلى أنّ الجهاز متصل بـ رمز زر البث بألوان مقلوبة.
  4. اختَر فيديو من تطبيق ATV وسيبدأ تشغيله على ATV.
  5. على هاتفك الجوّال، تظهر الآن وحدة تحكُّم مصغّرة في أسفل تطبيق المرسِل. يمكنك استخدام زر التشغيل أو الإيقاف المؤقت للتحكم في التشغيل.
  6. اختَر فيديو من الهاتف الجوّال وشغِّله. سيبدأ تشغيل الفيديو على ATV وسيتم عرض وحدة التحكم الموسعة على مرسِل جهازك الجوّال.
  7. يمكنك قفل هاتفك وعندما تفتح قفله، من المفترض أن يظهر لك إشعار على شاشة القفل للتحكّم في تشغيل الوسائط أو إيقاف البث.

صورة لقسم من شاشة هاتف Android مع مشغّل مصغّر يشغّل فيديو

4. تجهيز مشروع البدء

الآن وبعد أن تحقّقنا من تكامل التطبيق المكتمل Cast Connect، نحتاج إلى إتاحة استخدام Cast Connect في تطبيق البدء الذي تم تنزيله. أصبحت جاهزًا الآن للانطلاق في هذا المشروع الأوّلي باستخدام "استوديو Android":

  1. اختَر استيراد مشروع في شاشة الترحيب أو انقر على ملف > جديد > استيراد مشروع...، خيارات القائمة
  2. اختَر الدليل رمز المجلدapp-start من مجلد "نموذج الرموز" وانقر على "حسنًا".
  3. انقر على ملف > زر "مزامنة مشروع المزامنة مع أداة Gradle" في "استوديو Android" مزامنة المشروع مع ملفات Gradle
  4. حدد جهاز ATV وانقر على الزر الزر "تشغيل" في "استوديو Android"، وهو مثلث أخضر يشير إلى اليمينتشغيل لتشغيل التطبيق واستكشاف واجهة المستخدم. شريط أدوات "استوديو Android" يعرض جهاز Android TV الذي تم اختياره

صورة لسلسلة من الصور المصغّرة للفيديو (تم تمييز إحداها) تظهر على سطح معاينة فيديو بملء الشاشة عبارة "اتصال البثّ" تظهر في أعلى اليمين

تصميم التطبيقات

يوفر التطبيق قائمة بالفيديوهات التي يمكن للمستخدم تصفّحها. يمكن للمستخدمين اختيار فيديو لتشغيله على Android TV. يتكون التطبيق من نشاطين رئيسيين: MainActivity وPlaybackActivity.

MainActivity

يحتوي هذا النشاط على جزء (MainFragment). تم ضبط قائمة الفيديوهات والبيانات الوصفية المرتبطة بها في الصف MovieList، وتُستخدم الطريقة setupMovies() لإنشاء قائمة بعناصر Movie.

يمثّل العنصر Movie كيان فيديو يشمل العنوان والوصف والصور المصغّرة وعنوان URL للفيديو. يتم ربط كل عنصر Movie بـ CardPresenter لعرض الصورة المصغّرة للفيديو مع العنوان والاستوديو ويتم تمريرها إلى ArrayObjectAdapter.

عند اختيار عنصر، يتمّ تمرير عنصر Movie المقابل إلى PlaybackActivity.

PlaybackActivity

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

متطلبات استخدام Cast Connect

يستخدم Cast Connect إصدارات جديدة من "خدمات Google Play" تتطلب تحديث تطبيق ATV لاستخدام مساحة الاسم AndroidX.

لإتاحة استخدام Cast Connect في تطبيق Android TV، عليك إنشاء الأحداث ودعمها من جلسة وسائط. تنشئ مكتبة Cast Connect حالة الوسائط استنادًا إلى حالة جلسة الوسائط. تستخدم مكتبة Cast Connect جلسة الوسائط أيضًا للإشارة إلى تلقّيها رسائل معيّنة من أحد المُرسِلين، مثل الإيقاف المؤقت.

5- ضبط دعم Cast

التبعيات

عدِّل ملف build.gradle للتطبيق لتضمين ملحقات المكتبة اللازمة:

dependencies {
    ....

    // Cast Connect libraries
    implementation 'com.google.android.gms:play-services-cast-tv:20.0.0'
    implementation 'com.google.android.gms:play-services-cast:21.1.0'
}

قم بمزامنة المشروع للتأكد من إنشاء المشروع بدون أخطاء.

الإعداد

CastReceiverContext عبارة عن كائن مفردة المنتصف لتنسيق جميع تفاعلات البث. يجب تنفيذ واجهة ReceiverOptionsProvider لتوفير CastReceiverOptions عند إعداد CastReceiverContext.

أنشئ ملف CastReceiverOptionsProvider.kt وأضِف الفئة التالية إلى المشروع:

package com.google.sample.cast.castconnect

import android.content.Context
import com.google.android.gms.cast.tv.ReceiverOptionsProvider
import com.google.android.gms.cast.tv.CastReceiverOptions

class CastReceiverOptionsProvider : ReceiverOptionsProvider {
    override fun getOptions(context: Context): CastReceiverOptions {
        return CastReceiverOptions.Builder(context)
                .setStatusText("Cast Connect Codelab")
                .build()
    }
}

بعد ذلك، حدِّد موفّر خيارات المُستلِم داخل العلامة <application> في ملف AndroidManifest.xml للتطبيق:

<application>
  ...
  <meta-data
    android:name="com.google.android.gms.cast.tv.RECEIVER_OPTIONS_PROVIDER_CLASS_NAME"
    android:value="com.google.sample.cast.castconnect.CastReceiverOptionsProvider" />
</application>

للاتصال بتطبيق ATV من مُرسِل البث، اختَر نشاطًا تريد تشغيله. في هذا الدرس التطبيقي حول الترميز، سنشغِّل MainActivity للتطبيق عند بدء جلسة بث. في ملف AndroidManifest.xml، أضِف فلتر أهداف التشغيل في MainActivity.

<activity android:name=".MainActivity">
  ...
  <intent-filter>
    <action android:name="com.google.android.gms.cast.tv.action.LAUNCH" />
    <category android:name="android.intent.category.DEFAULT" />
  </intent-filter>
</activity>

دورة حياة سياق مستقبِل البث

يجب تشغيل "CastReceiverContext" عند تشغيل التطبيق وإيقاف "CastReceiverContext" عند نقله إلى الخلفية. ننصحك باستخدام "LifecycleObserver" من مكتبة androidx.lifecycle لإدارة مكالمات CastReceiverContext.start() وCastReceiverContext.stop().

افتح ملف MyApplication.kt، وابدأ إعداد سياق البث من خلال استدعاء initInstance() في طريقة onCreate من التطبيق. في الفئة AppLifeCycleObserver start()، تكون السمة CastReceiverContext عند استئناف التطبيق وstop() عند إيقاف التطبيق مؤقتًا:

package com.google.sample.cast.castconnect

import com.google.android.gms.cast.tv.CastReceiverContext
...

class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        CastReceiverContext.initInstance(this)
        ProcessLifecycleOwner.get().lifecycle.addObserver(AppLifecycleObserver())
    }

    class AppLifecycleObserver : DefaultLifecycleObserver {
        override fun onResume(owner: LifecycleOwner) {
            Log.d(LOG_TAG, "onResume")
            CastReceiverContext.getInstance().start()
        }

        override fun onPause(owner: LifecycleOwner) {
            Log.d(LOG_TAG, "onPause")
            CastReceiverContext.getInstance().stop()
        }
    }
}

ربط MediaSession بـ MediaManager

MediaManager هي سمة لـ CastReceiverContext المفردة، وتدير حالة الوسائط، وتعالج القصد من التحميل، وتترجم رسائل مساحة اسم الوسائط من المرسِلين إلى أوامر وسائط، وترسل حالة الوسائط إلى المرسِلين.

عند إنشاء MediaSession، يجب أيضًا تقديم رمز MediaSession المميّز الحالي إلى MediaManager ليعرف مكان إرسال الأوامر واسترداد حالة تشغيل الوسائط. في ملف PlaybackVideoFragment.kt، تأكَّد من إعداد MediaSession قبل ضبط الرمز المميّز على MediaManager.

import com.google.android.gms.cast.tv.CastReceiverContext
import com.google.android.gms.cast.tv.media.MediaManager
...

class PlaybackVideoFragment : VideoSupportFragment() {
    private var castReceiverContext: CastReceiverContext? = null
    ...

    private fun initializePlayer() {
        if (mPlayer == null) {
            ...
            mMediaSession = MediaSessionCompat(getContext(), LOG_TAG)
            ...
            castReceiverContext = CastReceiverContext.getInstance()
            if (castReceiverContext != null) {
                val mediaManager: MediaManager = castReceiverContext!!.getMediaManager()
                mediaManager.setSessionCompatToken(mMediaSession!!.getSessionToken())
            }

        }
    }
}

عند إصدار MediaSession بسبب تشغيل غير نشط، يجب ضبط رمز مميّز فارغ على MediaManager:

private fun releasePlayer() {
    mMediaSession?.release()
    castReceiverContext?.mediaManager?.setSessionCompatToken(null)
    ...
}

لنقم بتشغيل نموذج التطبيق

انقر على الزر الزر &quot;تشغيل&quot; في &quot;استوديو Android&quot;، وهو مثلث أخضر يشير إلى اليمينتشغيل لنشر التطبيق على جهاز ATV، وأغلِق التطبيق وارجع إلى "شاشة ATV الرئيسية". من المُرسِل، انقر على زر البث رمز زر البث واختَر جهاز ATV. ستلاحظ أنّ تطبيق ATV قد تم تشغيله على جهاز ATV وأنّ حالة زر البث متّصلة.

6- جارٍ تحميل الوسائط

يتم إرسال أمر التحميل من خلال عنصر باسم الحزمة الذي حدّدته في وحدة تحكم المطوّرين. يجب إضافة فلتر الأهداف المحدَّد مسبقًا إلى تطبيق Android TV لتحديد النشاط المستهدَف الذي سيتلقّى هذا الهدف. في ملف AndroidManifest.xml، أضِف فلتر الأهداف من التحميل إلى PlayerActivity:

<activity android:name="com.google.sample.cast.castconnect.PlaybackActivity"
          android:launchMode="singleTask"
          android:exported="true">
  <intent-filter>
     <action android:name="com.google.android.gms.cast.tv.action.LOAD"/>
     <category android:name="android.intent.category.DEFAULT" />
  </intent-filter>
</activity>

معالجة طلبات التحميل على Android TV

سنحتاج إلى معالجة النشاط بعد أن تم ضبط النشاط لتلقّي هذه الغاية التي تحتوي على طلب تحميل.

يطلب التطبيق طريقة خاصة تُسمّى "processIntent" عند بدء النشاط. تحتوي هذه الطريقة على منطق معالجة الأهداف الواردة. لمعالجة طلب تحميل، سنعدّل هذه الطريقة وسنرسل الغرض من المعالجة بشكل أكبر من خلال استدعاء طريقة onNewIntent للمثيل MediaManager. إذا اكتشف MediaManager أنّ الغرض هو طلب تحميل، يستخرج الكائن MediaLoadRequestData من الغرض ويستدعي MediaLoadCommandCallback.onLoad(). عدِّل طريقة processIntent في ملف PlaybackVideoFragment.kt للتعامل مع الغرض الذي يحتوي على طلب التحميل:

fun processIntent(intent: Intent?) {
    val mediaManager: MediaManager = CastReceiverContext.getInstance().getMediaManager()
    // Pass intent to Cast SDK
    if (mediaManager.onNewIntent(intent)) {
        return
    }

    // Clears all overrides in the modifier.
    mediaManager.getMediaStatusModifier().clear()

    // If the SDK doesn't recognize the intent, handle the intent with your own logic.
    ...
}

بعد ذلك، سنوسِّع الفئة المجردة MediaLoadCommandCallback التي ستلغي طريقة onLoad() التي استدعتها MediaManager. تتلقى هذه الطريقة بيانات طلب التحميل وتحولها إلى كائن Movie. بعد التحويل، يتم تشغيل الفيلم بواسطة المشغّل المحلي. يتم بعد ذلك تعديل "MediaManager" باستخدام "MediaLoadRequest" وبث "MediaStatus" إلى المُرسِلين المتصلين. أنشِئ صفًا خاصًا مدمَجًا باسم MyMediaLoadCommandCallback في ملف PlaybackVideoFragment.kt:

import com.google.android.gms.cast.MediaLoadRequestData
import com.google.android.gms.cast.MediaInfo
import com.google.android.gms.cast.MediaMetadata
import com.google.android.gms.cast.MediaError
import com.google.android.gms.cast.tv.media.MediaException
import com.google.android.gms.cast.tv.media.MediaCommandCallback
import com.google.android.gms.cast.tv.media.QueueUpdateRequestData
import com.google.android.gms.cast.tv.media.MediaLoadCommandCallback
import com.google.android.gms.tasks.Task
import com.google.android.gms.tasks.Tasks
import android.widget.Toast
...

private inner class MyMediaLoadCommandCallback :  MediaLoadCommandCallback() {
    override fun onLoad(
        senderId: String?, mediaLoadRequestData: MediaLoadRequestData): Task<MediaLoadRequestData> {
        Toast.makeText(activity, "onLoad()", Toast.LENGTH_SHORT).show()
        return if (mediaLoadRequestData == null) {
            // Throw MediaException to indicate load failure.
            Tasks.forException(MediaException(
                MediaError.Builder()
                    .setDetailedErrorCode(MediaError.DetailedErrorCode.LOAD_FAILED)
                    .setReason(MediaError.ERROR_REASON_INVALID_REQUEST)
                    .build()))
        } else Tasks.call {
            play(convertLoadRequestToMovie(mediaLoadRequestData)!!)
            // Update media metadata and state
            val mediaManager = castReceiverContext!!.mediaManager
            mediaManager.setDataFromLoad(mediaLoadRequestData)
            mediaLoadRequestData
        }
    }
}

private fun convertLoadRequestToMovie(mediaLoadRequestData: MediaLoadRequestData?): Movie? {
    if (mediaLoadRequestData == null) {
        return null
    }
    val mediaInfo: MediaInfo = mediaLoadRequestData.getMediaInfo() ?: return null
    var videoUrl: String = mediaInfo.getContentId()
    if (mediaInfo.getContentUrl() != null) {
        videoUrl = mediaInfo.getContentUrl()
    }
    val metadata: MediaMetadata = mediaInfo.getMetadata()
    val movie = Movie()
    movie.videoUrl = videoUrl
    movie.title = metadata?.getString(MediaMetadata.KEY_TITLE)
    movie.description = metadata?.getString(MediaMetadata.KEY_SUBTITLE)
    if(metadata?.hasImages() == true) {
        movie.cardImageUrl = metadata.images[0].url.toString()
    }
    return movie
}

الآن بعد أن تم تحديد طلب معاودة الاتصال، علينا تسجيله في "MediaManager". يجب تسجيل معاودة الاتصال قبل الاتصال بـ MediaManager.onNewIntent(). إضافة setMediaLoadCommandCallback عند إعداد المشغِّل:

private fun initializePlayer() {
    if (mPlayer == null) {
        ...
        mMediaSession = MediaSessionCompat(getContext(), LOG_TAG)
        ...
        castReceiverContext = CastReceiverContext.getInstance()
        if (castReceiverContext != null) {
            val mediaManager: MediaManager = castReceiverContext.getMediaManager()
            mediaManager.setSessionCompatToken(mMediaSession.getSessionToken())
            mediaManager.setMediaLoadCommandCallback(MyMediaLoadCommandCallback())
        }
    }
}

لنقم بتشغيل نموذج التطبيق

انقر على الزر الزر &quot;تشغيل&quot; في &quot;استوديو Android&quot;، وهو مثلث أخضر يشير إلى اليمينتشغيل لنشر التطبيق على جهاز ATV. من المُرسِل، انقر على زر البث رمز زر البث واختَر جهاز ATV. سيتم تشغيل تطبيق ATV على جهاز ATV. اختَر فيديو على جهاز جوّال، وسيبدأ تشغيله على ATV. تحقّق مما إذا كنت قد تلقّيت إشعارًا على هاتفك حيث تتوفّر عناصر التحكّم في التشغيل. جرِّب استخدام عناصر التحكم مثل الإيقاف المؤقت، يجب أن يتم إيقاف الفيديو على جهاز ATV مؤقتًا.

7. إتاحة أوامر التحكّم في البثّ

يتيح التطبيق الحالي الآن الأوامر الأساسية المتوافقة مع جلسة وسائط، مثل التشغيل والإيقاف المؤقت والتقديم/الترجيع. ومع ذلك، هناك بعض أوامر التحكم في البث غير متوفرة في جلسة الوسائط. يجب تسجيل "MediaCommandCallback" للتوافق مع أوامر التحكم في البث هذه.

إضافة MyMediaCommandCallback إلى مثيل MediaManager باستخدام setMediaCommandCallback عند إعداد المشغّل:

private fun initializePlayer() {
    ...
    castReceiverContext = CastReceiverContext.getInstance()
    if (castReceiverContext != null) {
        val mediaManager = castReceiverContext!!.mediaManager
        ...
        mediaManager.setMediaCommandCallback(MyMediaCommandCallback())
    }
}

أنشِئ فئة MyMediaCommandCallback لإلغاء الطرق، مثل onQueueUpdate()، من أجل إتاحة أوامر التحكم في البث هذه:

private inner class MyMediaCommandCallback : MediaCommandCallback() {
    override fun onQueueUpdate(
        senderId: String?,
        queueUpdateRequestData: QueueUpdateRequestData
    ): Task<Void> {
        Toast.makeText(getActivity(), "onQueueUpdate()", Toast.LENGTH_SHORT).show()
        // Queue Prev / Next
        if (queueUpdateRequestData.getJump() != null) {
            Toast.makeText(
                getActivity(),
                "onQueueUpdate(): Jump = " + queueUpdateRequestData.getJump(),
                Toast.LENGTH_SHORT
            ).show()
        }
        return super.onQueueUpdate(senderId, queueUpdateRequestData)
    }
}

8. التعامل مع حالة الوسائط

تعديل حالة الوسائط

يحصل Cast Connect على حالة الوسائط الأساسية من جلسة الوسائط. لإتاحة الميزات المتقدّمة، يمكن لتطبيق Android TV تحديد خصائص الحالة الإضافية وإلغاء هذه الخصائص من خلال MediaStatusModifier. سيتم دائمًا تشغيل MediaStatusModifier على MediaSession التي تم ضبطها في CastReceiverContext.

على سبيل المثال، لتحديد setMediaCommandSupported عند تشغيل معاودة الاتصال onLoad:

import com.google.android.gms.cast.MediaStatus
...
private class MyMediaLoadCommandCallback : MediaLoadCommandCallback() {
    fun onLoad(
        senderId: String?,
        mediaLoadRequestData: MediaLoadRequestData
    ): Task<MediaLoadRequestData> {
        Toast.makeText(getActivity(), "onLoad()", Toast.LENGTH_SHORT).show()
        ...
        return Tasks.call({
            play(convertLoadRequestToMovie(mediaLoadRequestData)!!)
            ...
            // Use MediaStatusModifier to provide additional information for Cast senders.
            mediaManager.getMediaStatusModifier()
                .setMediaCommandSupported(MediaStatus.COMMAND_QUEUE_NEXT, true)
                .setIsPlayingAd(false)
            mediaManager.broadcastMediaStatus()
            // Return the resolved MediaLoadRequestData to indicate load success.
            mediaLoadRequestData
        })
    }
}

الاعتراض على حالة الوسائط قبل إرسالها

مثلما هو الحال مع MessageInterceptor في حزمة تطوير البرامج (SDK) الخاصة بمستقبل الويب، يمكنك تحديد MediaStatusWriter في MediaManager لإجراء تعديلات إضافية على MediaStatus قبل بثه إلى المُرسِلين المتصلين.

على سبيل المثال، يمكنك ضبط بيانات مخصّصة في MediaStatus قبل إرسالها إلى مُرسِلي الأجهزة الجوّالة:

import com.google.android.gms.cast.tv.media.MediaManager.MediaStatusInterceptor
import com.google.android.gms.cast.tv.media.MediaStatusWriter
import org.json.JSONObject
import org.json.JSONException
...

private fun initializePlayer() {
    if (mPlayer == null) {
        ...
        if (castReceiverContext != null) {
            ...
            val mediaManager: MediaManager = castReceiverContext.getMediaManager()
            ...
            // Use MediaStatusInterceptor to process the MediaStatus before sending out.
            mediaManager.setMediaStatusInterceptor(
                MediaStatusInterceptor { mediaStatusWriter: MediaStatusWriter ->
                    try {
                        mediaStatusWriter.setCustomData(JSONObject("{myData: 'CustomData'}"))
                    } catch (e: JSONException) {
                        Log.e(LOG_TAG,e.message,e);
                    }
            })
        }
    }
}        

9. تهانينا

أنت تعرف الآن كيفية تمكين تطبيق Android TV باستخدام Cast Connect من المكتبة.

ويمكنك إلقاء نظرة على دليل المطوِّر لمزيد من التفاصيل: /cast/docs/android_tv_receiver.