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

1- نظرة عامة

شعار Google Cast

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

ما المقصود بـ Google Cast؟

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

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

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

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

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

ما ستتعرَّف عليه

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

المتطلبات

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

التجربة

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

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

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

ما هو تقييمك لتجربتك في إنشاء تطبيقات Android؟

مبتدئ متوسط مختص

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

مبتدئ متوسط مختص

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

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

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

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

رمز بوصلة

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

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

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

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

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

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

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

انقر على زر الإرسال في تطبيق الفيديو واختَر جهاز 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"، وهو مثلث أخضر يشير إلى اليمينRun لتشغيل التطبيق واستكشاف واجهة المستخدم.

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

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

يتألف التطبيق من نشاطَين أساسيَين: 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 وهو تطبيق يعمل بتكنولوجيا Google Cast، ويظهر زر البث في أعلى يسار الشاشة

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

التبعيات

قم بتحديث ملف Build.grale لتضمين تبعيات المكتبة الضرورية:

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

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

الإعداد

يتضمن إطار عمل البث كائنًا سينغلتونيًا عموميًا، وهو CastContext، والذي ينسق جميع تفاعلات البث.

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

عند تطوير تطبيقك الذي يفعِّل تكنولوجيا Google 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 ببطء في VideoBrowserActivity في طريقة onCreate:

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 إلى إطار عمل البث:

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

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

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

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

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

بث الوسائط

بمستوى عالٍ، إذا أردت تشغيل الوسائط على جهاز بث، يجب إجراء ما يلي:

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

سبق أن نفّذنا الخطوة الثانية في القسم السابق. يمكنك تنفيذ الخطوة 3 بسهولة باستخدام إطار عمل Cast. تكمن الخطوة الأولى في ربط عنصر بعنصر آخر، لأنّ 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) للبث.

دعونا نضيف 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 الذي يتيح تشغيل الوسائط، سيتم إنشاء مثيل RemoteMediaClient تلقائيًا من خلال حزمة تطوير البرامج (SDK). يمكن الوصول إليه من خلال استدعاء طريقة 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;، وهو مثلث أخضر يشير إلى اليمينRun (تشغيل) لتشغيل التطبيق على جهازك الجوّال. اتصِل بجهاز البث وابدأ تشغيل فيديو. من المفترض أن يظهر الفيديو على جهاز الاستقبال.

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

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

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

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

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

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

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

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

سيتم تشغيل "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;، وهو مثلث أخضر يشير إلى اليمينRun لتشغيل التطبيق على جهازك الجوّال. يمكنك إرسال فيديو والانتقال إلى صفحة معيّنة من نموذج التطبيق. من المفترض أن يظهر إشعار بشأن الفيديو الذي يتم تشغيله حاليًا على جهاز الاستقبال. اقفل جهازك الجوّال، ومن المفترض أن تعرض شاشة القفل الآن عناصر التحكم في تشغيل الوسائط على جهاز البث.

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

9. تراكب تمهيدي

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

صورة توضيحية تُظهر العنصر الأساسي للبثّ حول زر البثّ في تطبيق &quot;بثّ فيديوهات&quot; على أجهزة Android

توفّر حزمة تطوير البرامج (SDK) للبث عرضًا مخصّصًا 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 عندما يتوفّر جهاز بث، وذلك من خلال تعديل طريقة 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;، وهو مثلث أخضر يشير إلى اليمينRun (تشغيل) لتشغيل التطبيق على جهازك الجوّال، وسيظهر لك الإعلان التمهيدي (محو بيانات التطبيق إذا لم يتم عرض التراكب).

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

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

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

توفّر حزمة تطوير البرامج (SDK) للبث تطبيقًا مصغّرًا لوحدة التحكّم الموسّعة التي تُسمى 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()
}

يمكنك تعديل طريقة loadRemoteMedia في LocalPlayerActivity لعرض 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;تشغيل&quot; في &quot;استوديو Android&quot;، وهو مثلث أخضر يشير إلى اليمينتشغيل لتشغيل التطبيق على جهازك الجوّال وبث فيديو. من المفترض أن تظهر لك وحدة التحكّم الموسّعة. انتقل مرة أخرى إلى قائمة الفيديوهات وعندما تنقر على وحدة التحكّم المصغّرة، سيتم تحميل وحدة التحكّم الموسّعة مرة أخرى. يُرجى الانتقال بعيدًا عن التطبيق للاطّلاع على الإشعار. انقر على صورة الإشعار لتحميل وحدة التحكم الموسّعة.

11. إضافة دعم Cast Connect

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

التبعيات

ملاحظة: لتنفيذ Cast Connect، يجب أن يكون حجم play-services-cast-framework 19.0.0 أو أعلى.

LaunchOptions

لتشغيل تطبيق Android TV، الذي يُشار إليه أيضًا باسم "جهاز استقبال Android"، يجب ضبط العلامة setAndroidReceiverCompatible على "صحيح" في الكائن LaunchOptions. يوضّح عنصر LaunchOptions هذا كيفية تشغيل جهاز الاستقبال ويتم تمريره إلى CastOptions التي تعرضها الفئة CastOptionsProvider. عند ضبط العلامة المذكورة أعلاه على false، سيتم تشغيل جهاز استقبال الويب لمعرّف التطبيق المحدّد في Google Cast 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

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

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

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

اختبار Cast Connect

خطوات تثبيت ملف APK Android TV على جهاز "Chromecast مع Google TV"

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

12. تخصيص تطبيقات Google 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 لتكنولوجيا Google Cast على نظام التشغيل Android.

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