تفعيل بث تطبيق Android

1. نظرة عامة

شعار Google Cast

سيعلمك هذا الدليل التعليمي كيفية تعديل تطبيق فيديو حالي على Android لبث المحتوى على جهاز مزوّد بتقنية Google Cast.

ما المقصود بتكنولوجيا Google Cast؟

تتيح تكنولوجيا Google Cast للمستخدمين بث المحتوى من جهاز جوّال إلى التلفزيون. ويمكن للمستخدمين بعد ذلك استخدام أجهزتهم الجوّالة كجهاز تحكّم عن بُعد لتشغيل الوسائط على التلفزيون.

تتيح لك حزمة تطوير البرامج (SDK) لنظام Google Cast توسيع نطاق تطبيقك للتحكّم في التلفزيون أو نظام الصوت. تسمح لك حزمة تطوير البرامج (SDK) لتكنولوجيا Cast بإضافة مكونات واجهة المستخدم اللازمة بناءً على قائمة التحقق من تصميم Google Cast.

يتم توفير قائمة التحقّق من تصميم Google Cast لجعل تجربة المستخدم في Cast بسيطة ويمكن توقّعها على جميع المنصات المتوافقة.

ما الذي سننشئه؟

عند إكمال هذا الدليل التعليمي حول رموز البرامج، سيكون لديك تطبيق فيديو على Android يمكنه بث الفيديوهات إلى جهاز مزوّد بتكنولوجيا Google Cast.

المُعطيات

  • كيفية إضافة حزمة تطوير البرامج (SDK) لمنصة Google Cast إلى نموذج تطبيق فيديو
  • كيفية إضافة زر البث لاختيار جهاز Google Cast
  • كيفية الاتصال بجهاز بث وتشغيل جهاز استقبال وسائط
  • كيفية بث فيديو
  • كيفية إضافة وحدة تحكّم صغيرة في جهاز بث الوسائط إلى تطبيقك
  • كيفية إتاحة إشعارات الوسائط وعناصر التحكّم في شاشة القفل
  • كيفية إضافة وحدة تحكّم موسّعة
  • كيفية تقديم شاشة مقدمة
  • كيفية تخصيص التطبيقات المصغّرة لأجهزة البث
  • كيفية الدمج مع Cast Connect

المتطلبات

  • أحدث إصدار من حزمة تطوير البرامج (SDK) لنظام التشغيل Android
  • الإصدار 3.2 من استوديو Android أو الإصدارات الأحدث
  • جهاز جوّال واحد يعمل بنظام التشغيل Android 4.1 أو إصدار أحدث من نظام التشغيل Jelly Bean (المستوى 16 من واجهة برمجة التطبيقات)
  • كابل بيانات USB لتوصيل جهازك الجوّال بالكمبيوتر المخصّص للتطوير
  • جهاز Google Cast، مثل Chromecast أو Android TV تم ضبطه للوصول إلى الإنترنت
  • تلفزيون أو شاشة مزوّدة بمنفذ إدخال HDMI.
  • يجب توفُّر جهاز Chromecast مع Google TV لاختبار عملية دمج Cast Connect، ولكنّه اختياري لبقية خطوات Codelab. إذا لم يكن لديك هذا البرنامج، يُرجى تخطّي خطوة إضافة دعم Cast Connect في نهاية هذا الدليل التوجيهي.

تجربة الاستخدام

  • يجب أن تكون لديك معرفة سابقة بتطوير تطبيقات Android وKotlin.
  • ستحتاج أيضًا إلى معرفة سابقة بمشاهدة التلفزيون :)

كيف ستستخدم هذا البرنامج التعليمي؟

قراءة النص فقط قراءته وإكمال التمارين

ما مدى رضاك عن تجربة إنشاء تطبيقات Android؟

مبتدئ متوسط متقدّم

ما مدى رضاك عن تجربة مشاهدة التلفزيون؟

مبتدئ متوسط متقدّم

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

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

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

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

رمز بوصلة

أولاً، لنلقِ نظرة على شكل نموذج التطبيق المكتمل. التطبيق هو مشغل فيديو أساسي. يمكن للمستخدم اختيار فيديو من قائمة، ثم تشغيله على الجهاز أو بثّه على جهاز Google Cast.

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

اختَر استيراد مشروع في شاشة الترحيب أو من خيارات القائمة ملف > جديد > استيراد مشروع....

اختَر الدليل رمز المجلدapp-done من مجلد "نموذج الرموز" وانقر على "حسنًا".

انقر على ملف > زر "مزامنة المشروع مع Gradle" في "استوديو Android" مزامنة المشروع مع ملفات Gradle.

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

وصِّل جهاز Android وانقر على الزر زر التشغيل في "استوديو Android"، وهو مثلث أخضر يشير إلى اليمينتشغيل في Android Studio. من المفترض أن يظهر تطبيق الفيديو الذي يحمل اسم بث الفيديوهات بعد بضع ثوانٍ.

انقر على زرّ البثّ في تطبيق الفيديو واختَر جهاز Google Cast.

اختَر فيديو وانقر على زر التشغيل.

سيبدأ تشغيل الفيديو على جهاز Google Cast.

سيتم عرض وحدة التحكّم الموسّعة. يمكنك استخدام زر التشغيل/الإيقاف المؤقت للتحكّم في التشغيل.

انتقِل مرة أخرى إلى قائمة الفيديوهات.

يظهر الآن جهاز تحكّم صغير في أسفل الشاشة. رسم توضيحي لهاتف Android يعمل بتطبيق "بث الفيديوهات" مع جهاز تحكّم صغير يظهر في أسفل الشاشة

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

انقر على زر الشاشة الرئيسية للجهاز الجوّال. اسحب الإشعارات للأسفل وسترى الآن إشعارًا لجلسة البث.

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

ارجع إلى تطبيق الفيديو وانقر على زر البث لإيقاف البث على جهاز Google Cast.

الأسئلة الشائعة

4. إعداد مشروع البدء

صورة توضيحية لهاتف Android يعمل بتطبيق "بث الفيديوهات"

يجب إضافة ميزة التوافق مع Google Cast إلى تطبيق البدء الذي نزّلته. في ما يلي بعض المصطلحات المتعلّقة بتطبيق Google Cast والتي سنستخدمها في هذا الدليل التعليمي حول البرمجة:

  • تطبيق مُرسِل يعمل على جهاز جوّال أو كمبيوتر محمول
  • تطبيق استقبال يعمل على جهاز Google Cast

أصبحت الآن مستعدًا للبناء على المشروع الأوّلي باستخدام "استوديو Android":

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

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

يُجلب التطبيق قائمة بالفيديوهات من خادم ويب عن بُعد ويقدّم قائمة للمستخدم لتصفّحها. يمكن للمستخدمين اختيار فيديو للاطّلاع على التفاصيل أو تشغيله على الجهاز الجوّال.

يتألف التطبيق من نشاطَين رئيسيَين: VideoBrowserActivity وLocalPlayerActivity. لدمج وظائف Google Cast، يجب اكتساب الأنشطة من AppCompatActivity أو من عنصرها الرئيسي FragmentActivity. يرجع هذا القيد إلى أنّنا سنحتاج إلى إضافة MediaRouteButton (المتوفّرة في مكتبة دعم MediaRouter) كعنصر MediaRouteActionProvider ولن يعمل ذلك إلا إذا كان النشاط يرث من الفئات المذكورة أعلاه. تعتمد مكتبة دعم MediaRouter على مكتبة دعم AppCompat التي توفر الفئات المطلوبة.

VideoBrowserActivity

يحتوي هذا النشاط على Fragment (VideoBrowserFragment). وتتضمّن هذه القائمة ArrayAdapter (VideoListAdapter). ويتم استضافة قائمة الفيديوهات والبيانات الوصفية المرتبطة بها على خادم بعيد كملف JSON. يُستخدَم AsyncTaskLoader (VideoItemLoader) لجلب ملف JSON هذا ومعالجته لإنشاء قائمة بكائنات MediaItem.

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

عندما ينشئ أداة التحميل قائمة MediaItems، تمرّر هذه القائمة إلى VideoListAdapter التي تعرِض بعد ذلك قائمة MediaItems في VideoBrowserFragment. تظهر للمستخدم قائمة بالصور المصغّرة للفيديوهات مع وصف موجز لكل فيديو. عند اختيار عنصر، يتم تحويل MediaItem المقابل إلى Bundle ويتم تمريره إلى LocalPlayerActivity.

LocalPlayerActivity

يعرض هذا النشاط البيانات الوصفية حول فيديو معين ويسمح للمستخدم بتشغيل الفيديو محليًا على جهاز الجوال.

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

التبعيات

بما أنّنا نستخدم AppCompatActivity، نحتاج إلى مكتبة دعم AppCompat. لإدارة قائمة الفيديوهات والحصول على الصور للّائحة بشكل غير متزامن، نستخدم مكتبة Volley.

الأسئلة الشائعة

5- إضافة زرّ "البث"

صورة توضيحية للجزء العلوي من هاتف Android مع تشغيل تطبيق Cast Video، يظهر زرّ البث في أعلى يسار الشاشة

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

التبعيات

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

dependencies {
    implementation 'androidx.appcompat:appcompat:1.5.0'
    implementation 'androidx.mediarouter:mediarouter:1.3.1'
    implementation 'androidx.recyclerview:recyclerview:1.2.1'
    implementation 'com.google.android.gms:play-services-cast-framework:21.1.0'
    implementation 'com.android.volley:volley:1.2.1'
    implementation "androidx.core:core-ktx:1.8.0"
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
}

مزامنة المشروع للتأكّد من أنّ عمليات إنشاء المشروع خالية من الأخطاء

الإعداد

يحتوي إطار عمل Cast على عنصر فريد على مستوى النظام، وهو CastContext، الذي ينسق جميع تفاعلات Cast.

يجب تنفيذ واجهة OptionsProvider لتقديم CastOptions المطلوب لبدء تشغيل العنصر الفردي CastContext. الخيار الأكثر أهمية هو رقم تعريف تطبيق المُستلِم، والذي يُستخدَم لفلترة نتائج اكتشاف أجهزة البث وتشغيل تطبيق المُستلِم عند بدء جلسة بث.

عند تطوير تطبيق متوافق مع Cast، عليك التسجيل كمطوّر على Cast ثم الحصول على معرّف تطبيق لتطبيقك. في هذا الدليل التعليمي حول رموز البرامج، سنستخدم نموذجًا لمعرّف التطبيق.

أضِف ملف CastOptionsProvider.kt الجديد التالي إلى حزمة com.google.sample.cast.refplayer في المشروع:

package com.google.sample.cast.refplayer

import android.content.Context
import com.google.android.gms.cast.framework.OptionsProvider
import com.google.android.gms.cast.framework.CastOptions
import com.google.android.gms.cast.framework.SessionProvider

class CastOptionsProvider : OptionsProvider {
    override fun getCastOptions(context: Context): CastOptions {
        return CastOptions.Builder()
                .setReceiverApplicationId(context.getString(R.string.app_id))
                .build()
    }

    override fun getAdditionalSessionProviders(context: Context): List<SessionProvider>? {
        return null
    }
}

حدِّد الآن OptionsProvider ضمن علامة "application" في ملف AndroidManifest.xml للتطبيق:

<meta-data
    android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
    android:value="com.google.sample.cast.refplayer.CastOptionsProvider" />

يمكنك إعداد CastContext بشكلٍ كسول في طريقة onCreate في VideoBrowserActivity:

import com.google.android.gms.cast.framework.CastContext

private var mCastContext: CastContext? = null

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.video_browser)
    setupActionBar()

    mCastContext = CastContext.getSharedInstance(this)
}

أضِف منطق الإعداد نفسه إلى LocalPlayerActivity.

زر الإرسال

بعد بدء CastContext، علينا إضافة زر البث للسماح للمستخدم باختيار جهاز بث. ينفّذ MediaRouteButton زر البث من مكتبة دعم MediaRouter. مثل أي رمز إجراء يمكنك إضافته إلى نشاطك (باستخدام ActionBar أو Toolbar)، عليك أولاً إضافة عنصر القائمة المقابل إلى قائمتك.

عدِّل ملف res/menu/browse.xml وأضِف عنصر MediaRouteActionProvider في القائمة قبل عنصر الإعدادات:

<item
    android:id="@+id/media_route_menu_item"
    android:title="@string/media_route_menu_title"
    app:actionProviderClass="androidx.mediarouter.app.MediaRouteActionProvider"
    app:showAsAction="always"/>

يمكنك إلغاء طريقة onCreateOptionsMenu() في VideoBrowserActivity باستخدام CastButtonFactory لربط MediaRouteButton بإطار عمل Cast:

import com.google.android.gms.cast.framework.CastButtonFactory

private var mediaRouteMenuItem: MenuItem? = null

override fun onCreateOptionsMenu(menu: Menu): Boolean {
     super.onCreateOptionsMenu(menu)
     menuInflater.inflate(R.menu.browse, menu)
     mediaRouteMenuItem = CastButtonFactory.setUpMediaRouteButton(getApplicationContext(), menu,
                R.id.media_route_menu_item)
     return true
}

يمكنك إلغاء onCreateOptionsMenu في LocalPlayerActivity بطريقة مشابهة.

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

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

6- بث محتوى الفيديو

صورة توضيحية لهاتف Android يستخدم تطبيق &quot;بث الفيديوهات&quot;

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

بث الوسائط

بشكل عام، إذا أردت تشغيل محتوى وسائط على جهاز بث، عليك اتّباع الخطوات التالية:

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

لقد نفّذنا الخطوة 2 في القسم السابق. من السهل تنفيذ الخطوة 3 باستخدام إطار عمل Cast. تؤدي الخطوة 1 إلى ربط عنصر بآخر، وMediaInfo هو عنصر يفهم إطار عمل Cast، وMediaItem هو عنصر يُستخدم في تطبيقنا لتضمين عنصر وسائط، ويمكننا بسهولة ربط MediaItem بـ MediaInfo.

يميز نموذج التطبيق LocalPlayerActivity حاليًا بين التشغيل على الجهاز والتشغيل عن بُعد باستخدام هذا التصنيف:

private var mLocation: PlaybackLocation? = null

enum class PlaybackLocation {
    LOCAL, REMOTE
}

enum class PlaybackState {
    PLAYING, PAUSED, BUFFERING, IDLE
}

ليس من المهم في هذا الدليل التعليمي فهم طريقة عمل كل منطق المشغّل النموذجي بدقة. من المهم معرفة أنّه يجب تعديل مشغّل الوسائط في تطبيقك ليتمكّن من معرفة موقعَي التشغيل بالطريقة نفسها.

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

إدارة جلسة البث

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

ستبدأ جلسة البث تلقائيًا عندما يختار المستخدم جهازًا من خلال زر البث، وستتوقف تلقائيًا عند قطع اتصال المستخدم. تتولى حزمة تطوير البرامج (SDK) لنظام التشغيل Cast أيضًا إعادة الاتصال بجلسة جهاز الاستقبال بسبب مشاكل في الشبكة.

لنضيف SessionManagerListener إلى LocalPlayerActivity:

import com.google.android.gms.cast.framework.CastSession
import com.google.android.gms.cast.framework.SessionManagerListener
...

private var mSessionManagerListener: SessionManagerListener<CastSession>? = null
private var mCastSession: CastSession? = null
...

private fun setupCastListener() {
    mSessionManagerListener = object : SessionManagerListener<CastSession> {
        override fun onSessionEnded(session: CastSession, error: Int) {
            onApplicationDisconnected()
        }

        override fun onSessionResumed(session: CastSession, wasSuspended: Boolean) {
            onApplicationConnected(session)
        }

        override fun onSessionResumeFailed(session: CastSession, error: Int) {
            onApplicationDisconnected()
        }

        override fun onSessionStarted(session: CastSession, sessionId: String) {
            onApplicationConnected(session)
        }

        override fun onSessionStartFailed(session: CastSession, error: Int) {
            onApplicationDisconnected()
        }

        override fun onSessionStarting(session: CastSession) {}
        override fun onSessionEnding(session: CastSession) {}
        override fun onSessionResuming(session: CastSession, sessionId: String) {}
        override fun onSessionSuspended(session: CastSession, reason: Int) {}
        private fun onApplicationConnected(castSession: CastSession) {
            mCastSession = castSession
            if (null != mSelectedMedia) {
                if (mPlaybackState == PlaybackState.PLAYING) {
                    mVideoView!!.pause()
                    loadRemoteMedia(mSeekbar!!.progress, true)
                    return
                } else {
                    mPlaybackState = PlaybackState.IDLE
                    updatePlaybackLocation(PlaybackLocation.REMOTE)
                }
            }
            updatePlayButton(mPlaybackState)
            invalidateOptionsMenu()
        }

        private fun onApplicationDisconnected() {
            updatePlaybackLocation(PlaybackLocation.LOCAL)
            mPlaybackState = PlaybackState.IDLE
            mLocation = PlaybackLocation.LOCAL
            updatePlayButton(mPlaybackState)
            invalidateOptionsMenu()
       }
   }
}

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

يمكن الوصول إلى الجلسة النشطة حاليًا باسم SessionManager.getCurrentSession(). يتم إنشاء الجلسات وإغلاقها تلقائيًا استجابةً لتفاعلات المستخدمين مع مربّعات حوار البث.

نحتاج إلى تسجيل مستمع الجلسة الخاص بنا وتهيئة بعض المتغيرات التي سنستخدمها في النشاط. غيِّر طريقة LocalPlayerActivity onCreate إلى:

import com.google.android.gms.cast.framework.CastContext
...

private var mCastContext: CastContext? = null
...

override fun onCreate(savedInstanceState: Bundle?) {
    ...
    mCastContext = CastContext.getSharedInstance(this)
    mCastSession = mCastContext!!.sessionManager.currentCastSession
    setupCastListener()
    ...
    loadViews()
    ...
    val bundle = intent.extras
    if (bundle != null) {
        ....
        if (shouldStartPlayback) {
              ....

        } else {
            if (mCastSession != null && mCastSession!!.isConnected()) {
                updatePlaybackLocation(PlaybackLocation.REMOTE)
            } else {
                updatePlaybackLocation(PlaybackLocation.LOCAL)
            }
            mPlaybackState = PlaybackState.IDLE
            updatePlayButton(mPlaybackState)
        }
    }
    ...
}

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

في حزمة تطوير البرامج (SDK) لتطبيقات البث، يوفّر RemoteMediaClient مجموعة من واجهات برمجة التطبيقات الملائمة لإدارة تشغيل الوسائط عن بُعد على جهاز الاستقبال. بالنسبة إلى CastSession الذي يتيح تشغيل الوسائط، ستنشئ حزمة SDK مثيلًا من RemoteMediaClient تلقائيًا. ويمكن الوصول إليه من خلال استدعاء طريقة getRemoteMediaClient() في مثيل CastSession. أضِف الطرق التالية إلى LocalPlayerActivity لتحميل الفيديو المحدّد حاليًا على جهاز الاستقبال:

import com.google.android.gms.cast.framework.media.RemoteMediaClient
import com.google.android.gms.cast.MediaInfo
import com.google.android.gms.cast.MediaLoadOptions
import com.google.android.gms.cast.MediaMetadata
import com.google.android.gms.common.images.WebImage
import com.google.android.gms.cast.MediaLoadRequestData

private fun loadRemoteMedia(position: Int, autoPlay: Boolean) {
    if (mCastSession == null) {
        return
    }
    val remoteMediaClient = mCastSession!!.remoteMediaClient ?: return
    remoteMediaClient.load( MediaLoadRequestData.Builder()
                .setMediaInfo(buildMediaInfo())
                .setAutoplay(autoPlay)
                .setCurrentTime(position.toLong()).build())
}

private fun buildMediaInfo(): MediaInfo? {
    val movieMetadata = MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE)
    mSelectedMedia?.studio?.let { movieMetadata.putString(MediaMetadata.KEY_SUBTITLE, it) }
    mSelectedMedia?.title?.let { movieMetadata.putString(MediaMetadata.KEY_TITLE, it) }
    movieMetadata.addImage(WebImage(Uri.parse(mSelectedMedia!!.getImage(0))))
    movieMetadata.addImage(WebImage(Uri.parse(mSelectedMedia!!.getImage(1))))
    return mSelectedMedia!!.url?.let {
        MediaInfo.Builder(it)
            .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
            .setContentType("videos/mp4")
            .setMetadata(movieMetadata)
            .setStreamDuration((mSelectedMedia!!.duration * 1000).toLong())
            .build()
    }
}

عدِّل الآن الطرق الحالية المختلفة لاستخدام منطق جلسة البث لتفعيل ميزة التشغيل عن بُعد:

private fun play(position: Int) {
    startControllersTimer()
    when (mLocation) {
        PlaybackLocation.LOCAL -> {
            mVideoView!!.seekTo(position)
            mVideoView!!.start()
        }
        PlaybackLocation.REMOTE -> {
            mPlaybackState = PlaybackState.BUFFERING
            updatePlayButton(mPlaybackState)
            //seek to a new position within the current media item's new position 
            //which is in milliseconds from the beginning of the stream
            mCastSession!!.remoteMediaClient?.seek(position.toLong())
        }
        else -> {}
    }
    restartTrickplayTimer()
}
private fun togglePlayback() {
    ...
    PlaybackState.IDLE -> when (mLocation) {
        ...
        PlaybackLocation.REMOTE -> {
            if (mCastSession != null && mCastSession!!.isConnected) {
                loadRemoteMedia(mSeekbar!!.progress, true)
            }
        }
        else -> {}
    }
    ...
}
override fun onPause() {
    ...
    mCastContext!!.sessionManager.removeSessionManagerListener(
                mSessionManagerListener!!, CastSession::class.java)
}
override fun onResume() {
    Log.d(TAG, "onResume() was called")
    mCastContext!!.sessionManager.addSessionManagerListener(
            mSessionManagerListener!!, CastSession::class.java)
    if (mCastSession != null && mCastSession!!.isConnected) {
        updatePlaybackLocation(PlaybackLocation.REMOTE)
    } else {
        updatePlaybackLocation(PlaybackLocation.LOCAL)
    }
    super.onResume()
}

بالنسبة إلى طريقة updatePlayButton، غيِّر قيمة المتغيّر isConnected:

private fun updatePlayButton(state: PlaybackState?) {
    ...
    val isConnected = (mCastSession != null
                && (mCastSession!!.isConnected || mCastSession!!.isConnecting))
    ...
}

الآن، انقر على الزر الزر &quot;تشغيل&quot; في &quot;استوديو Android&quot;، وهو مثلث أخضر يشير إلى اليمينتشغيل لتشغيل التطبيق على جهازك الجوّال. اربط جهازك بجهاز البث وابدأ تشغيل فيديو. من المفترض أن ترى الفيديو قيد التشغيل على جهاز الاستقبال.

7- وحدة تحكّم صغيرة

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

صورة توضيحية للجزء السفلي من هاتف Android تعرض مشغّل الفيديوهات المصغر في تطبيق &quot;بث الفيديوهات&quot;

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

أضِف تعريف المقتطف التالي إلى أسفل كلّ من res/layout/player_activity.xml وres/layout/video_browser.xml:

<fragment
    android:id="@+id/castMiniController"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:visibility="gone"
    class="com.google.android.gms.cast.framework.media.widget.MiniControllerFragment"/>

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

8. الإشعارات وشاشة القفل

تتطلّب قائمة التحقّق من تصميم Google Cast أن ينفِّذ تطبيق المُرسِل عناصر التحكّم في الوسائط من إشعار وشاشة القفل.

صورة توضيحية لهاتف Android يعرض عناصر التحكّم في الوسائط في منطقة الإشعارات

توفّر حزمة تطوير البرامج (SDK) لتكنولوجيا Cast MediaNotificationService لمساعدة تطبيق المرسِل في إنشاء عناصر تحكّم في الوسائط للإشعارات وشاشة القفل. يتم دمج الخدمة تلقائيًا في بيان تطبيقك بواسطة أداة Gradle.

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

يمكن تفعيل عناصر التحكّم في الإشعارات وشاشة القفل باستخدام "CastOptions" عند إعداد "CastContext". تكون عناصر التحكّم في الوسائط مفعّلة تلقائيًا في الإشعارات وشاشة القفل. يتم تفعيل ميزة شاشة القفل طالما أنّ الإشعار مفعّل.

يُرجى تعديل CastOptionsProvider وتغيير تنفيذ getCastOptions ليتطابق مع هذا الرمز:

import com.google.android.gms.cast.framework.media.CastMediaOptions
import com.google.android.gms.cast.framework.media.NotificationOptions

override fun getCastOptions(context: Context): CastOptions {
   val notificationOptions = NotificationOptions.Builder()
            .setTargetActivityClassName(VideoBrowserActivity::class.java.name)
            .build()
    val mediaOptions = CastMediaOptions.Builder()
            .setNotificationOptions(notificationOptions)
            .build()
   return CastOptions.Builder()
                .setReceiverApplicationId(context.getString(R.string.app_id))
                .setCastMediaOptions(mediaOptions)
                .build()
}

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

صورة توضيحية لهاتف Android يعرض عناصر التحكّم في الوسائط على شاشة القفل

9. العنصر التمهيدي على سطح الفيديو

تتطلّب قائمة التحقّق من تصميم Google Cast أن يقدّم تطبيق المُرسِل زرّ البث للمستخدمين الحاليين لإعلامهم بأنّ تطبيق المُرسِل يتيح الآن البث ويساعد أيضًا المستخدمين الجدد في Google Cast.

صورة توضيحية تعرض تراكب &quot;البث&quot; التمهيدي حول زر الإرسال في تطبيق &quot;فيديوهات البث&quot; المتوافق مع Android

توفّر حزمة تطوير البرامج (SDK) لتكنولوجيا Cast طريقة عرض مخصّصة، IntroductoryOverlay، يمكن استخدامها لتمييز زر البث عند عرضه للمستخدمين لأول مرة. أضِف الرمز التالي إلى VideoBrowserActivity:

import com.google.android.gms.cast.framework.IntroductoryOverlay
import android.os.Looper

private var mIntroductoryOverlay: IntroductoryOverlay? = null

private fun showIntroductoryOverlay() {
    mIntroductoryOverlay?.remove()
    if (mediaRouteMenuItem?.isVisible == true) {
       Looper.myLooper().run {
           mIntroductoryOverlay = com.google.android.gms.cast.framework.IntroductoryOverlay.Builder(
                    this@VideoBrowserActivity, mediaRouteMenuItem!!)
                   .setTitleText("Introducing Cast")
                   .setSingleTime()
                   .setOnOverlayDismissedListener(
                           object : IntroductoryOverlay.OnOverlayDismissedListener {
                               override fun onOverlayDismissed() {
                                   mIntroductoryOverlay = null
                               }
                          })
                   .build()
          mIntroductoryOverlay!!.show()
        }
    }
}

الآن، أضِف CastStateListener واستدِع طريقة showIntroductoryOverlay عندما يكون جهاز Cast متاحًا من خلال تعديل طريقة onCreate وإلغاء طريقة onResume وonPause لمطابقة ما يلي:

import com.google.android.gms.cast.framework.CastState
import com.google.android.gms.cast.framework.CastStateListener

private var mCastStateListener: CastStateListener? = null

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.video_browser)
    setupActionBar()
    mCastStateListener = object : CastStateListener {
            override fun onCastStateChanged(newState: Int) {
                if (newState != CastState.NO_DEVICES_AVAILABLE) {
                    showIntroductoryOverlay()
                }
            }
        }
    mCastContext = CastContext.getSharedInstance(this)
}

override fun onResume() {
    super.onResume()
    mCastContext?.addCastStateListener(mCastStateListener!!)
}

override fun onPause() {
    super.onPause()
    mCastContext?.removeCastStateListener(mCastStateListener!!)
}

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

10. وحدة تحكّم موسّعة

تتطلّب قائمة التحقّق من تصميم Google Cast أن يقدّم تطبيق المُرسِل وحدة تحكّم موسّعة للوسائط التي يتم بثّها. وحدة التحكّم الموسّعة هي نسخة بملء الشاشة من وحدة التحكّم المصغّرة.

صورة توضيحية لفيديو يتم تشغيله على هاتف Android مع وحدة التحكّم الموسّعة التي تظهر فوقه

توفّر حزمة تطوير البرامج (SDK) لتطبيق Cast تطبيقًا مصغّرًا لوحدة التحكّم الموسّعة باسم ExpandedControllerActivity. هذه فئة مجردة عليك إنشاء فئة فرعية منها لإضافة زرّ بث.

أولاً، أنشئ ملفًا جديدًا لموارد القائمة باسم expanded_controller.xml لجهاز التحكّم الموسّع لتقديم زرّ البث:

<?xml version="1.0" encoding="utf-8"?>

<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto">

    <item
            android:id="@+id/media_route_menu_item"
            android:title="@string/media_route_menu_title"
            app:actionProviderClass="androidx.mediarouter.app.MediaRouteActionProvider"
            app:showAsAction="always"/>

</menu>

أنشئ حزمة جديدة expandedcontrols في الحزمة com.google.sample.cast.refplayer. بعد ذلك، أنشئ ملفًا جديدًا باسم ExpandedControlsActivity.kt في حزمة com.google.sample.cast.refplayer.expandedcontrols.

package com.google.sample.cast.refplayer.expandedcontrols

import android.view.Menu
import com.google.android.gms.cast.framework.media.widget.ExpandedControllerActivity
import com.google.sample.cast.refplayer.R
import com.google.android.gms.cast.framework.CastButtonFactory

class ExpandedControlsActivity : ExpandedControllerActivity() {
    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        super.onCreateOptionsMenu(menu)
        menuInflater.inflate(R.menu.expanded_controller, menu)
        CastButtonFactory.setUpMediaRouteButton(this, menu, R.id.media_route_menu_item)
        return true
    }
}

أعلن الآن عن ExpandedControlsActivity في AndroidManifest.xml ضمن العلامة application أعلى OPTIONS_PROVIDER_CLASS_NAME:

<application>
    ...
    <activity
        android:name="com.google.sample.cast.refplayer.expandedcontrols.ExpandedControlsActivity"
        android:label="@string/app_name"
        android:launchMode="singleTask"
        android:theme="@style/Theme.CastVideosDark"
        android:screenOrientation="portrait"
        android:exported="true">
        <intent-filter>
            <action android:name="android.intent.action.MAIN"/>
        </intent-filter>
        <meta-data
            android:name="android.support.PARENT_ACTIVITY"
            android:value="com.google.sample.cast.refplayer.VideoBrowserActivity"/>
    </activity>
    ...
</application>

عدِّل CastOptionsProvider وغيِّر NotificationOptions وCastMediaOptions لضبط النشاط المستهدَف على ExpandedControlsActivity:

import com.google.sample.cast.refplayer.expandedcontrols.ExpandedControlsActivity

override fun getCastOptions(context: Context): CastOptions {
    val notificationOptions = NotificationOptions.Builder()
            .setTargetActivityClassName(ExpandedControlsActivity::class.java.name)
            .build()
    val mediaOptions = CastMediaOptions.Builder()
            .setNotificationOptions(notificationOptions)
            .setExpandedControllerActivityClassName(ExpandedControlsActivity::class.java.name)
            .build()
    return CastOptions.Builder()
            .setReceiverApplicationId(context.getString(R.string.app_id))
            .setCastMediaOptions(mediaOptions)
            .build()
}

عدِّل طريقة LocalPlayerActivity loadRemoteMedia لعرض ExpandedControlsActivity عند تحميل الوسائط البعيدة:

import com.google.sample.cast.refplayer.expandedcontrols.ExpandedControlsActivity

private fun loadRemoteMedia(position: Int, autoPlay: Boolean) {
    if (mCastSession == null) {
        return
    }
    val remoteMediaClient = mCastSession!!.remoteMediaClient ?: return
    remoteMediaClient.registerCallback(object : RemoteMediaClient.Callback() {
        override fun onStatusUpdated() {
            val intent = Intent(this@LocalPlayerActivity, ExpandedControlsActivity::class.java)
            startActivity(intent)
            remoteMediaClient.unregisterCallback(this)
        }
    })
    remoteMediaClient.load(MediaLoadRequestData.Builder()
                .setMediaInfo(buildMediaInfo())
                .setAutoplay(autoPlay)
                .setCurrentTime(position.toLong()).build())
}

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

11. إضافة ميزة التوافق مع Cast Connect

تسمح مكتبة Cast Connect لتطبيقات المُرسِلين الحالية بالتواصل مع تطبيقات Android TV عبر بروتوكول Cast. يستند Cast Connect إلى البنية الأساسية لنظام Cast، ويعمل تطبيق Android TV كمستلِم.

التبعيات

ملاحظة: لتنفيذ Cast Connect، يجب أن يكون الإصدار play-services-cast-framework هو 19.0.0 أو إصدار أحدث.

LaunchOptions

لتشغيل تطبيق Android TV، الذي يُعرف أيضًا باسم "مستقبل Android"، نحتاج إلى ضبط علامة setAndroidReceiverCompatible على "صحيح" في كائن LaunchOptions. يحدِّد عنصر LaunchOptions هذا كيفية تشغيل المُستلِم ويتم تمريره إلى CastOptions الذي تعرضه فئة CastOptionsProvider. سيؤدي ضبط العلامة المذكورة أعلاه على false إلى تشغيل مستقبل الويب لمعرّف التطبيق المحدّد في Cast Developer Console.

في ملف CastOptionsProvider.kt، أضِف ما يلي إلى الطريقة getCastOptions:

import com.google.android.gms.cast.LaunchOptions
...
val launchOptions = LaunchOptions.Builder()
            .setAndroidReceiverCompatible(true)
            .build()
return new CastOptions.Builder()
        .setLaunchOptions(launchOptions)
        ...
        .build()

ضبط بيانات اعتماد الإطلاق

على جانب المُرسِل، يمكنك تحديد CredentialsData لتمثيل المستخدم الذي ينضم إلى الجلسة. credentials هي سلسلة يمكن للمستخدم تحديدها، شرط أن يفهم تطبيق ATV هذه السلسلة. لا يتم تمرير CredentialsData إلى تطبيق Android TV إلا أثناء التشغيل أو وقت الانضمام. وفي حال ضبطه مرة أخرى أثناء الاتصال، لن يتم تمريره إلى تطبيق Android TV.

لضبط بيانات اعتماد التفعيل، يجب تحديد CredentialsData ونقلها إلى عنصر LaunchOptions. أضِف الرمز التالي إلى طريقة getCastOptions في ملف CastOptionsProvider.kt:

import com.google.android.gms.cast.CredentialsData
...

val credentialsData = CredentialsData.Builder()
        .setCredentials("{\"userId\": \"abc\"}")
        .build()
val launchOptions = LaunchOptions.Builder()
       ...
       .setCredentialsData(credentialsData)
       .build()

ضبط بيانات الاعتماد في LoadRequest

إذا كان تطبيق Web Receiver وتطبيق Android TV يتعاملان مع credentials بشكل مختلف، قد تحتاج إلى تحديد credentials منفصل لكل منهما. ولتنفيذ ذلك، أضِف الرمز التالي في ملف LocalPlayerActivity.kt ضمن الدالة loadRemoteMedia:

remoteMediaClient.load(MediaLoadRequestData.Builder()
       ...
       .setCredentials("user-credentials")
       .setAtvCredentials("atv-user-credentials")
       .build())

استنادًا إلى تطبيق المُستلِم الذي يُجري المُرسِل البث إليه، ستتولى حزمة تطوير البرامج (SDK) الآن تلقائيًا بيانات الاعتماد التي سيتم استخدامها في الجلسة الحالية.

اختبار Cast Connect

خطوات تثبيت حزمة APK لنظام التشغيل Android TV على جهاز "‏Chromecast مع Google TV"

  1. ابحث عن عنوان IP لجهاز Android TV. وهي متاحة عادةً ضمن الإعدادات > الشبكة والإنترنت > (اسم الشبكة التي يتصل بها جهازك). على يسار الصفحة، ستظهر التفاصيل وعنوان IP لجهازك على الشبكة.
  2. استخدِم عنوان IP لجهازك للاتصال به عبر ADB باستخدام وحدة التحكّم الطرفية:
$ adb connect <device_ip_address>:5555
  1. من نافذة المحطة الطرفية، انتقِل إلى مجلد المستوى الأعلى لمحاكيات رمز Lab التي نزّلتها في بداية هذا الإصدار التجريبي من رمز Lab. على سبيل المثال:
$ cd Desktop/android_codelab_src
  1. ثبِّت ملف ‎ .apk في هذا المجلد على Android TV من خلال تشغيل:
$ adb -s <device_ip_address>:5555 install android-tv-app.apk
  1. ستتمكّن الآن من رؤية التطبيق باسم بث الفيديوهات في قائمة تطبيقاتك على جهاز Android TV.
  2. ارجع إلى مشروع Android Studio، وانقر على الزر "تشغيل" لتثبيت تطبيق المرسل وتشغيله على جهازك الجوّال الفعلي. في أعلى يسار الشاشة، انقر على رمز البث واختَر جهاز Android TV من الخيارات المتاحة. من المفترض أن يظهر لك الآن تطبيق Android TV على جهاز Android TV، ومن المفترض أن يتيح لك تشغيل الفيديو التحكّم في تشغيله باستخدام جهاز التحكّم عن بُعد في Android TV.

12. تخصيص التطبيقات المصغّرة لميزة البث

يمكنك تخصيص تطبيقات Cast المصغّرة من خلال ضبط الألوان وتصميم الأزرار والنص والملصقات المصغّرة واختيار أنواع الأزرار التي تريد عرضها.

تحديث res/values/styles_castvideo.xml

<style name="Theme.CastVideosTheme" parent="Theme.AppCompat.Light.NoActionBar">
    ...
    <item name="mediaRouteTheme">@style/CustomMediaRouterTheme</item>
    <item name="castIntroOverlayStyle">@style/CustomCastIntroOverlay</item>
    <item name="castMiniControllerStyle">@style/CustomCastMiniController</item>
    <item name="castExpandedControllerStyle">@style/CustomCastExpandedController</item>
    <item name="castExpandedControllerToolbarStyle">
        @style/ThemeOverlay.AppCompat.ActionBar
    </item>
    ...
</style>

حدِّد المظاهر المخصّصة التالية:

<!-- Customize Cast Button -->
<style name="CustomMediaRouterTheme" parent="Theme.MediaRouter">
    <item name="mediaRouteButtonStyle">@style/CustomMediaRouteButtonStyle</item>
</style>
<style name="CustomMediaRouteButtonStyle" parent="Widget.MediaRouter.Light.MediaRouteButton">
    <item name="mediaRouteButtonTint">#EEFF41</item>
</style>

<!-- Customize Introductory Overlay -->
<style name="CustomCastIntroOverlay" parent="CastIntroOverlay">
    <item name="castButtonTextAppearance">@style/TextAppearance.CustomCastIntroOverlay.Button</item>
    <item name="castTitleTextAppearance">@style/TextAppearance.CustomCastIntroOverlay.Title</item>
</style>
<style name="TextAppearance.CustomCastIntroOverlay.Button" parent="android:style/TextAppearance">
    <item name="android:textColor">#FFFFFF</item>
</style>
<style name="TextAppearance.CustomCastIntroOverlay.Title" parent="android:style/TextAppearance.Large">
    <item name="android:textColor">#FFFFFF</item>
</style>

<!-- Customize Mini Controller -->
<style name="CustomCastMiniController" parent="CastMiniController">
    <item name="castShowImageThumbnail">true</item>
    <item name="castTitleTextAppearance">@style/TextAppearance.AppCompat.Subhead</item>
    <item name="castSubtitleTextAppearance">@style/TextAppearance.AppCompat.Caption</item>
    <item name="castBackground">@color/accent</item>
    <item name="castProgressBarColor">@color/orange</item>
</style>

<!-- Customize Expanded Controller -->
<style name="CustomCastExpandedController" parent="CastExpandedController">
    <item name="castButtonColor">#FFFFFF</item>
    <item name="castPlayButtonDrawable">@drawable/cast_ic_expanded_controller_play</item>
    <item name="castPauseButtonDrawable">@drawable/cast_ic_expanded_controller_pause</item>
    <item name="castStopButtonDrawable">@drawable/cast_ic_expanded_controller_stop</item>
</style>

13. تهانينا

لقد تعرّفت الآن على كيفية تفعيل ميزة البث في تطبيق فيديو باستخدام التطبيقات المصغّرة لحزمة تطوير البرامج (SDK) لبث الوسائط على أجهزة Android.

لمزيد من التفاصيل، يُرجى الاطّلاع على دليل المطوِّر لمرسلي Android.