1. نظرة عامة
يشرح لك هذا الدرس التطبيقي حول الترميز كيفية تعديل تطبيق فيديو حالي على Android لبث محتوى إلى جهاز يعمل بتكنولوجيا Google Cast.
ما المقصود بتكنولوجيا Google Cast؟
تسمح تكنولوجيا Google Cast للمستخدمين ببث المحتوى من جهاز جوّال إلى تلفزيون. يمكن للمستخدمين بعد ذلك استخدام أجهزتهم الجوّالة كوحدة تحكم عن بُعد لتشغيل الوسائط على التلفزيون.
تتيح لك حزمة تطوير البرامج (SDK) لتكنولوجيا Google Cast إمكانية توسيع نطاق تطبيقك للتحكم في التلفزيون أو نظام الصوت. تسمح لك حزمة تطوير البرامج (SDK) لتكنولوجيا Cast بإضافة مكونات واجهة المستخدم اللازمة بناءً على قائمة التحقق من تصميم Google Cast.
يتم توفير قائمة التحقّق من تصميم Google Cast لجعل تجربة مستخدم Google Cast بسيطة ويمكن توقُّعها على جميع الأنظمة الأساسية المتوافقة.
ما الذي سنبنيه؟
عند الانتهاء من هذا الدرس التطبيقي، سيكون لديك تطبيق فيديو على Android يمكنه إرسال فيديوهات إلى جهاز يعمل بتكنولوجيا Google Cast.
المُعطيات
- كيفية إضافة حزمة تطوير البرامج (SDK) لتكنولوجيا Google Cast إلى نموذج فيديو
- كيفية إضافة زر البث لاختيار جهاز Google Cast
- كيفية الاتصال بجهاز بث وتشغيل جهاز استقبال وسائط
- كيفية إرسال فيديو
- كيفية إضافة وحدة تحكم مصغّرة للبث إلى تطبيقك.
- كيفية إتاحة إشعارات الوسائط وعناصر التحكّم في شاشة القفل
- كيفية إضافة وحدة تحكُّم موسّعة.
- كيفية توفير تراكب تمهيدي
- طريقة تخصيص التطبيقات المصغّرة للبث
- كيفية الدمج مع Cast Connect
المتطلبات
- أحدث إصدار من حزمة تطوير البرامج (SDK) لنظام التشغيل Android
- استوديو Android الإصدار 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. الحصول على رمز النموذج
يمكنك تنزيل نموذج الرمز بالكامل على جهاز الكمبيوتر...
وفك ضغط ملف zip الذي تم تنزيله.
3- تشغيل نموذج التطبيق
أولاً، لنرى كيف يبدو نموذج التطبيق المكتمل. هذا التطبيق هو مشغّل فيديو أساسي. يمكن للمستخدم اختيار فيديو من قائمة، ثمّ تشغيل الفيديو على الجهاز أو إرساله على جهاز Google Cast.
بعد تنزيل الرمز، توضّح التعليمات التالية كيفية فتح نموذج التطبيق المكتمل وتشغيله في استوديو Android:
اختَر استيراد مشروع في شاشة الترحيب أو انقر على ملف > جديد > استيراد مشروع...، خيارات القائمة
اختَر الدليل app-done
من مجلد "نموذج الرموز" وانقر على "حسنًا".
انقر على ملف > مزامنة المشروع مع ملفات Gradle
فعِّل تصحيح أخطاء USB على جهاز Android، علمًا بأنّ شاشة "خيارات المطوّرين" تكون مخفية تلقائيًا في الإصدار 4.2 من نظام التشغيل Android والإصدارات الأحدث. لإظهارها، انتقِل إلى الإعدادات > لمحة عن الهاتف ثم النقر على رقم الإصدار سبع مرات ارجع إلى الشاشة السابقة، ثم انتقل إلى النظام > إعدادات متقدّمة وانقر على خيارات المطوّرين بالقرب من أسفل الصفحة، ثم انقر على تصحيح أخطاء USB لتفعيله.
يمكنك توصيل جهاز Android بالطاقة والنقر على الزر تشغيل في "استوديو Android". من المفترض أن يظهر تطبيق الفيديو المُسمّى Cast Videos بعد بضع ثوانٍ.
انقر على زر البث في تطبيق الفيديو واختَر جهاز Google Cast.
حدد فيديو وانقر على زر التشغيل.
سيبدأ تشغيل الفيديو على جهاز Google Cast.
سيتم عرض وحدة التحكّم الموسّعة. يمكنك استخدام زر التشغيل أو الإيقاف المؤقت للتحكم في التشغيل.
انتقِل مرة أخرى إلى قائمة الفيديوهات.
تظهر الآن وحدة تحكُّم مصغّرة في أسفل الشاشة.
انقر على زر الإيقاف المؤقت في وحدة التحكّم المصغّرة لإيقاف الفيديو مؤقتًا على جهاز الاستقبال. انقر على زر التشغيل في وحدة التحكّم المصغّرة لمواصلة تشغيل الفيديو مرة أخرى.
انقر على زر الشاشة الرئيسية للجهاز الجوّال. اسحب الإشعارات للأسفل وسترى الآن إشعارًا لجلسة البث.
يمكنك قفل هاتفك وعندما تفتح قفله، من المفترض أن يظهر لك إشعار على شاشة القفل للتحكّم في تشغيل الوسائط أو إيقاف البث.
ارجع إلى تطبيق الفيديو وانقر على زر البث لإيقاف البث على جهاز Google Cast.
الأسئلة الشائعة
4. تجهيز مشروع البدء
نحتاج إلى إمكانية استخدام Google Cast في تطبيق البدء الذي نزّلته. في ما يلي بعض مصطلحات Google Cast التي سنستخدمها في هذا الدرس التطبيقي حول الترميز:
- تشغيل تطبيق المرسِل على جهاز جوّال أو كمبيوتر محمول،
- تشغيل تطبيق مستلِم على جهاز Google Cast
أصبحت جاهزًا الآن للانطلاق في هذا المشروع الأوّلي باستخدام "استوديو Android":
- اختَر الدليل
app-start
من تنزيل نموذج الرمز (اختَر استيراد مشروع في شاشة الترحيب أو خيار القائمة ملف > جديد > استيراد مشروع...). - انقر على زرّ مزامنة المشروع مع ملفات Gradle.
- انقر على الزر تشغيل لتشغيل التطبيق واستكشاف واجهة المستخدم.
تصميم التطبيقات
يجلب التطبيق قائمة فيديوهات من خادم ويب بعيد ويعرض قائمة للمستخدم لتصفّحها. ويمكن للمستخدمين اختيار فيديو للاطّلاع على التفاصيل أو تشغيله محليًا على الجهاز الجوّال.
يتكون التطبيق من نشاطين رئيسيين: 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- إضافة زر البث
يعرض تطبيق يعمل بتكنولوجيا Google Cast زر البث في كل نشاط من أنشطته. يؤدي النقر على زر البث إلى عرض قائمة بأجهزة البث التي يمكن للمستخدم اختيارها. إذا كان المستخدم يشغِّل المحتوى محليًا على جهاز المُرسِل، سيؤدي اختيار جهاز بث إلى بدء التشغيل أو استئناف تشغيله على جهاز البث هذا. يمكن للمستخدم النقر على زر البث وإيقاف بث تطبيقك على جهاز البث في أي وقت أثناء جلسة البث. يجب أن يكون المستخدم قادرًا على الاتصال بجهاز البث أو إلغاء ربطه أثناء ممارسة أي نشاط لتطبيقك، كما هو موضح في قائمة التحقق من تصميم Google Cast.
التبعيات
حدِّث ملف create.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"
}
قم بمزامنة المشروع للتأكد من إنشاء المشروع بدون أخطاء.
الإعداد
يحتوي إطار عمل Google Cast على كائن مفردات عام، وهو CastContext
، الذي ينسق جميع تفاعلات البث.
يجب تنفيذ واجهة OptionsProvider
لتوفير CastOptions
اللازم لإعداد CastContext
المفردة. وأهم خيار هو رقم تعريف تطبيق جهاز الاستقبال الذي يُستخدم لتصفية نتائج اكتشاف جهاز البث وتشغيل تطبيق الاستقبال عند بدء جلسة البث.
عند تطوير تطبيقك الخاص الذي يعمل بتكنولوجيا Google Cast، عليك التسجيل كمطوِّر لتكنولوجيا Google 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
بإطار عمل البث:
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
بطريقة مماثلة.
انقر على الزر تشغيل لتشغيل التطبيق على جهازك الجوّال. من المفترض أن يظهر لك زر البث في شريط الإجراءات الخاص بالتطبيق، وعند النقر عليه، سيتم عرض قائمة بأجهزة البث على شبكتك المحلية. تتم إدارة ميزة "اكتشاف الجهاز" تلقائيًا من خلال "CastContext
". اختَر جهاز البث وسيتم تحميل نموذج تطبيق الاستقبال على جهاز البث. ويمكنك التنقل بين نشاط التصفّح ونشاط المشغّل المحلي بحيث تبقى حالة زر البث متزامنة.
لم يتم توفير أي دعم لتشغيل الوسائط، لذلك لا يمكنك تشغيل فيديوهات على جهاز البث بعد. انقر على زر البث لإلغاء الربط.
6- بث محتوى الفيديو
سيتم توسيع نطاق نموذج التطبيق لتشغيل الفيديوهات عن بُعد أيضًا على جهاز بث. لتنفيذ ذلك، نحتاج إلى الاستماع إلى الأحداث المختلفة التي أنشأها إطار العمل Cast.
بث الوسائط
على المستوى العالي، إذا كنت تريد تشغيل وسائط على جهاز بث، يجب تنفيذ الإجراءات التالية:
- أنشئ عنصر
MediaInfo
لإنشاء نموذج لعنصر وسائط. - اتصِل بجهاز البث وشغِّل تطبيق الاستقبال.
- ما عليك سوى تحميل كائن
MediaInfo
إلى جهاز الاستقبال وتشغيل المحتوى. - تتبُّع حالة الوسائط
- إرسال أوامر التشغيل إلى المستلِم استنادًا إلى تفاعلات المستخدم
لقد أكملنا الخطوة 2 في القسم السابق. من السهل تنفيذ الخطوة الثالثة باستخدام إطار عمل 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.
إدارة جلسة البث
بالنسبة إلى إطار عمل Google 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()
. يتم إنشاء الجلسات وإزالتها تلقائيًا استجابةً لتفاعلات المستخدم مع مربّعات حوار البث.
نحتاج إلى تسجيل مستمع الجلسة الخاص بنا وتهيئة بعض المتغيرات التي سنستخدمها في النشاط. تغيير طريقة onCreate
LocalPlayerActivity
إلى:
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))
...
}
انقر الآن على الزر تشغيل لتشغيل التطبيق على جهازك الجوّال. اتصل بجهاز البث وابدأ في تشغيل الفيديو. من المفترض أن ترى الفيديو قيد التشغيل على جهاز الاستقبال.
7. وحدة تحكُّم صغيرة
تتطلب قائمة التحقّق من تصميم البثّ أن توفّر جميع تطبيقات البثّ وحدة تحكّم مصغّرة تظهر عندما ينتقل المستخدم من صفحة المحتوى الحالية. وتوفِّر وحدة التحكّم الصغيرة إمكانية الوصول الفوري وتذكيرًا مرئيًا بجلسة البث الحالية.
توفّر حزمة تطوير البرامج (SDK) لتكنولوجيا Cast طريقة عرض مخصّصة 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"/>
انقر على الزر تشغيل لتشغيل التطبيق وإرسال فيديو. عند بدء التشغيل على جهاز الاستقبال، من المفترض أن تظهر وحدة التحكّم المصغَّرة أسفل كل نشاط. يمكنك التحكّم في التشغيل عن بُعد باستخدام وحدة التحكّم الصغيرة. إذا انتقلت بين نشاط التصفّح ونشاط المشغّل المحلي، يجب أن تبقى حالة وحدة التحكّم المصغّرة متزامنة مع حالة تشغيل الوسائط لدى المُستلِم.
8. الإشعار وشاشة القفل
تتطلب قائمة التحقق من تصميم Google Cast أن ينفذ تطبيق المرسل عناصر التحكم في الوسائط من الإشعار وشاشة القفل.
توفّر حزمة تطوير البرامج (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()
}
انقر على الزر تشغيل لتشغيل التطبيق على جهازك الجوّال. يمكنك إرسال فيديو والانتقال من التطبيق النموذجي. من المفترض أن يكون هناك إشعار بشأن الفيديو الذي يتم تشغيله حاليًا على جهاز الاستقبال. يمكنك قفل جهازك الجوّال ومن المفترض أن تعرض شاشة القفل الآن عناصر التحكّم في تشغيل الوسائط على جهاز البث.
9. تراكب تمهيدي
تتطلب قائمة التحقق من تصميم Google Cast من أحد تطبيقات المرسل تقديم زر البث للمستخدمين الحاليين لإبلاغهم بأن تطبيق المرسل يتيح الآن البث ويساعد أيضًا المستخدمين الجدد على Google Cast.
توفّر حزمة تطوير البرامج (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
عند توفُّر جهاز البث من خلال تعديل طريقة 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!!)
}
يمكنك محو بيانات التطبيق أو إزالته من جهازك. بعد ذلك، انقر على الزر تشغيل لتشغيل التطبيق على جهازك الجوّال ومن المفترض أن يظهر لك التراكب التمهيدي (امسح بيانات التطبيق إذا لم يتم عرض التراكب).
10. تم توسيع وحدة التحكُّم
تتطلب قائمة التحقق من تصميم Google Cast أن يوفر تطبيق المرسل وحدة تحكم موسّعة للوسائط التي يتم بثها. وحدة التحكم الموسعة هي إصدار ملء الشاشة من وحدة التحكم المصغَّرة.
توفّر حزمة تطوير البرامج (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()
}
يمكنك تعديل طريقة 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())
}
انقر على الزر تشغيل لتشغيل التطبيق على جهازك الجوّال وبث فيديو. من المفترض أن تظهر لك وحدة التحكّم الموسّعة. انتقل مرة أخرى إلى قائمة الفيديوهات وعند النقر على وحدة التحكم المصغَّرة، سيتم تحميل وحدة التحكم الموسعة مرة أخرى. انتقِل إلى مكان آخر خارج التطبيق للاطّلاع على الإشعار. انقر على صورة الإشعار لتحميل وحدة التحكّم الموسّعة.
11. إضافة دعم Cast Connect
تسمح مكتبة Cast Connect لتطبيقات المُرسِلين الحالية بالتواصل مع تطبيقات Android TV عبر بروتوكول Cast. تعتمد خدمة Cast Connect على البنية الأساسية للبث، ويعمل تطبيق Android TV كوحدة استقبال.
التبعيات
ملاحظة: لتنفيذ Cast Connect، يجب أن يكون play-services-cast-framework
19.0.0
أو أعلى.
LaunchOptions
لتشغيل تطبيق Android TV، الذي يُشار إليه أيضًا باسم "مستقبل Android"، نحتاج إلى ضبط علامة setAndroidReceiverCompatible
على "صحيح" في كائن LaunchOptions
. يوضح عنصر LaunchOptions
هذا كيفية تشغيل جهاز الاستقبال وتمريره إلى CastOptions
التي تعرضها الفئة CastOptionsProvider
. يؤدي ضبط العلامة المذكورة أعلاه على false
إلى تشغيل جهاز استقبال الويب لمعرّف التطبيق المحدّد في Play 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())
استنادًا إلى تطبيق المُستلِم الذي يرسِل إليه المُرسِل، ستتعامل حزمة SDK الآن تلقائيًا مع بيانات الاعتماد التي سيتم استخدامها للجلسة الحالية.
اختبار الاتصال بالبث
خطوات تثبيت حزمة APK Android TV على جهاز "Chromecast مع Google TV"
- ابحث عن عنوان IP لجهاز Android TV. وعادةً ما تكون متاحة ضمن الإعدادات > الشبكة الإنترنت > (اسم الشبكة التي يتصل بها جهازك) على يمين الشاشة، ستعرض هذه النافذة التفاصيل وعنوان IP لجهازك على الشبكة.
- استخدِم عنوان IP لجهازك للاتصال به عبر ADB باستخدام الوحدة الطرفية:
$ adb connect <device_ip_address>:5555
- من النافذة الطرفية، انتقِل إلى مجلد المستوى الأعلى لعيّنات الدروس التطبيقية حول الترميز التي نزّلتها في بداية هذا الدرس التطبيقي حول الترميز. على سبيل المثال:
$ cd Desktop/android_codelab_src
- ثبِّت ملف apk .في هذا المجلد على Android TV من خلال تنفيذ ما يلي:
$ adb -s <device_ip_address>:5555 install android-tv-app.apk
- من المفترض أن تتمكّن الآن من رؤية أحد التطبيقات باسم بث الفيديوهات في قائمة تطبيقاتك على جهاز Android TV.
- ارجع إلى مشروع "استوديو Android" وانقر على الزر "تشغيل" لتثبيت تشغيل تطبيق المرسِل على جهازك الجوّال الفعلي. في أعلى يسار الشاشة، انقر على رمز البث واختَر جهاز Android TV من الخيارات المتاحة. من المفترض أن ترى الآن أنّه تم إطلاق تطبيق Android TV على جهاز Android TV، ومن المفترض أن يتيح لك تشغيل فيديو التحكّم في تشغيل الفيديو باستخدام جهاز التحكّم عن بُعد في Android TV.
12. تخصيص التطبيقات المصغّرة للبث
يمكنك تخصيص أدوات البث من خلال ضبط الألوان وتصميم الأزرار والنص ومظهر الصورة المصغّرة واختيار أنواع الأزرار التي سيتم عرضها.
تحديث 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. تهانينا
تعرفت الآن على كيفية تفعيل ميزة Google Cast في تطبيق فيديو باستخدام أدوات Cast SDK على نظام التشغيل Android.
لمزيد من التفاصيل، يُرجى الاطّلاع على دليل المطوِّر لمرسلي Android.