يجب أن يوفّر مصدر البث التلفزيوني بيانات "الدليل الإلكتروني للبرامج" (EPG) لقناة واحدة على الأقل في نشاط الإعداد. عليك أيضًا تعديل هذه البيانات بشكل دوري، مع مراعاة حجم التعديل وسلسلة المعالجة التي تتعامل معه. بالإضافة إلى ذلك، يمكنك توفير روابط التطبيقات للقنوات التي توجّه المستخدم إلى المحتوى والأنشطة ذات الصلة. يناقش هذا الدرس إنشاء بيانات القنوات والبرامج وتعديلها في قاعدة بيانات النظام مع مراعاة هذه الاعتبارات.
يمكنك تجربة تطبيق TV Input Service النموذجي.
الحصول على إذن
لكي يعمل مصدر البث التلفزيوني مع بيانات "الدليل الإلكتروني للبرامج"، يجب أن يعلن عن إذن الكتابة في ملف بيان Android على النحو التالي:
<uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />
تسجيل القنوات في قاعدة البيانات
تحتفظ قاعدة بيانات Android TV بسجلات لبيانات القنوات لمصادر البث التلفزيوني. في نشاط الإعداد، يجب ربط بيانات القناة بالحقول التالية لفئة TvContract.Channels لكل قناة من قنواتك:
COLUMN_DISPLAY_NAME: الاسم المعروض للقناةCOLUMN_DISPLAY_NUMBER: رقم القناة المعروضCOLUMN_INPUT_ID: رقم تعريف خدمة مصدر البث التلفزيونيCOLUMN_SERVICE_TYPE: نوع خدمة القناةCOLUMN_TYPE: نوع معيار بث القناةCOLUMN_VIDEO_FORMAT: تنسيق الفيديو التلقائي للقناة
على الرغم من أنّ إطار عمل مصدر البث التلفزيوني عام بما يكفي للتعامل مع كلٍّ من المحتوى التقليدي الذي يتم بثه والمحتوى الذي يتم بثه عبر الإنترنت بدون أي تمييز، قد تحتاج إلى تحديد الأعمدة التالية بالإضافة إلى تحديد قنوات البث التقليدية بشكل أفضل:
COLUMN_ORIGINAL_NETWORK_ID: رقم تعريف الشبكة التلفزيونيةCOLUMN_SERVICE_ID: رقم تعريف الخدمةCOLUMN_TRANSPORT_STREAM_ID: رقم تعريف بث النقل
إذا أردت توفير تفاصيل رابط التطبيق لقنواتك، عليك تعديل بعض الحقول الإضافية. لمزيد من المعلومات عن حقول رابط التطبيق، يُرجى الاطّلاع على مقالة إضافة معلومات رابط التطبيق.
بالنسبة إلى مصادر البث التلفزيوني المستندة إلى البث عبر الإنترنت، عليك تعيين القيم الخاصة بك وفقًا لذلك حتى يمكن تحديد كل قناة بشكل فريد.
عليك سحب البيانات الوصفية لقناتك (بتنسيق XML أو JSON أو أي تنسيق آخر) من خادمك الخلفي، وفي نشاط الإعداد، عليك ربط القيم بقاعدة بيانات النظام على النحو التالي:
Kotlin
val values = ContentValues().apply { put(TvContract.Channels.COLUMN_DISPLAY_NUMBER, channel.number) put(TvContract.Channels.COLUMN_DISPLAY_NAME, channel.name) put(TvContract.Channels.COLUMN_ORIGINAL_NETWORK_ID, channel.originalNetworkId) put(TvContract.Channels.COLUMN_TRANSPORT_STREAM_ID, channel.transportStreamId) put(TvContract.Channels.COLUMN_SERVICE_ID, channel.serviceId) put(TvContract.Channels.COLUMN_VIDEO_FORMAT, channel.videoFormat) } val uri = context.contentResolver.insert(TvContract.Channels.CONTENT_URI, values)
Java
ContentValues values = new ContentValues(); values.put(Channels.COLUMN_DISPLAY_NUMBER, channel.number); values.put(Channels.COLUMN_DISPLAY_NAME, channel.name); values.put(Channels.COLUMN_ORIGINAL_NETWORK_ID, channel.originalNetworkId); values.put(Channels.COLUMN_TRANSPORT_STREAM_ID, channel.transportStreamId); values.put(Channels.COLUMN_SERVICE_ID, channel.serviceId); values.put(Channels.COLUMN_VIDEO_FORMAT, channel.videoFormat); Uri uri = context.getContentResolver().insert(TvContract.Channels.CONTENT_URI, values);
في هذا المثال، channel هو عنصر يحتوي على بيانات وصفية للقناة من الخادم الخلفي.
عرض معلومات القناة والبرنامج
يعرض تطبيق التلفزيون على النظام معلومات القناة والبرنامج للمستخدمين أثناء تصفّحهم القنوات، كما هو موضّح في الشكل 1. لضمان عمل معلومات القناة والبرنامج مع أداة عرض معلومات القناة والبرنامج في تطبيق التلفزيون على النظام، اتّبِع الإرشادات التالية:
- رقم القناة (
COLUMN_DISPLAY_NUMBER) - الرمز
(
android:iconفي بيان مصدر البث التلفزيوني ) - وصف البرنامج (
COLUMN_SHORT_DESCRIPTION) - عنوان البرنامج (
COLUMN_TITLE) - شعار القناة (
TvContract.Channels.Logo)- استخدِم اللون #EEEEEE ليطابق النص المحيط
- لا تُدرِج مساحة ترك
- صورة الملصق (
COLUMN_POSTER_ART_URI) - نسبة العرض إلى الارتفاع بين 16:9 و4:3
يوفّر تطبيق بث تلفزيوني على النظام المعلومات نفسها من خلال دليل البرامج، بما في ذلك صورة الملصق، كما هو موضّح في الشكل 2.
تعديل بيانات القناة
عند تعديل بيانات القناة الحالية، استخدِم طريقة update بدلاً من حذف البيانات وإعادة إضافتها. يمكنك تحديد الإصدار الحالي من البيانات باستخدام Channels.COLUMN_VERSION_NUMBER وPrograms.COLUMN_VERSION_NUMBER عند اختيار السجلات التي تريد تعديلها.
ملاحظة: قد تستغرق إضافة بيانات القناة إلى ContentProvider بعض الوقت. أضِف البرامج الحالية (التي يتم عرضها في غضون ساعتين من الوقت الحالي) فقط عند ضبط EpgSyncJobService لتعديل بقية بيانات القناة في الخلفية. يمكنك الاطّلاع على
تطبيق Android TV Live TV النموذجي للحصول على مثال.
تحميل بيانات القناة على دفعات
عند تعديل قاعدة بيانات النظام بكمية كبيرة من بيانات القناة، استخدِم طريقة applyBatch أو bulkInsert في ContentResolver. في ما يلي مثال على استخدام applyBatch:
Kotlin
val ops = ArrayList<ContentProviderOperation>() val programsCount = channelInfo.mPrograms.size channelInfo.mPrograms.forEachIndexed { index, program -> ops += ContentProviderOperation.newInsert( TvContract.Programs.CONTENT_URI).run { withValues(programs[index]) withValue(TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS, programStartSec * 1000) withValue( TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS, (programStartSec + program.durationSec) * 1000 ) build() } programStartSec += program.durationSec if (index % 100 == 99 || index == programsCount - 1) { try { contentResolver.applyBatch(TvContract.AUTHORITY, ops) } catch (e: RemoteException) { Log.e(TAG, "Failed to insert programs.", e) return } catch (e: OperationApplicationException) { Log.e(TAG, "Failed to insert programs.", e) return } ops.clear() } }
Java
ArrayList<ContentProviderOperation> ops = new ArrayList<>(); int programsCount = channelInfo.mPrograms.size(); for (int j = 0; j < programsCount; ++j) { ProgramInfo program = channelInfo.mPrograms.get(j); ops.add(ContentProviderOperation.newInsert( TvContract.Programs.CONTENT_URI) .withValues(programs.get(j)) .withValue(Programs.COLUMN_START_TIME_UTC_MILLIS, programStartSec * 1000) .withValue(Programs.COLUMN_END_TIME_UTC_MILLIS, (programStartSec + program.durationSec) * 1000) .build()); programStartSec = programStartSec + program.durationSec; if (j % 100 == 99 || j == programsCount - 1) { try { getContentResolver().applyBatch(TvContract.AUTHORITY, ops); } catch (RemoteException | OperationApplicationException e) { Log.e(TAG, "Failed to insert programs.", e); return; } ops.clear(); } }
معالجة بيانات القناة بشكل غير متزامن
يجب ألا تعيق معالجة البيانات، مثل جلب بث من الخادم أو الوصول إلى قاعدة البيانات، سلسلة واجهة المستخدم. يُعد استخدام AsyncTask إحدى طرق إجراء التعديلات بشكل غير متزامن. على سبيل المثال، عند تحميل معلومات القناة من خادم خلفي، يمكنك استخدام AsyncTask على النحو التالي:
Kotlin
private class LoadTvInputTask(val context: Context) : AsyncTask<Uri, Unit, Unit>() { override fun doInBackground(vararg uris: Uri) { try { fetchUri(uris[0]) } catch (e: IOException) { Log.d("LoadTvInputTask", "fetchUri error") } } @Throws(IOException::class) private fun fetchUri(videoUri: Uri) { context.contentResolver.openInputStream(videoUri).use { inputStream -> Xml.newPullParser().also { parser -> try { parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false) parser.setInput(inputStream, null) sTvInput = ChannelXMLParser.parseTvInput(parser) sSampleChannels = ChannelXMLParser.parseChannelXML(parser) } catch (e: XmlPullParserException) { e.printStackTrace() } } } } }
Java
private static class LoadTvInputTask extends AsyncTask<Uri, Void, Void> { private Context mContext; public LoadTvInputTask(Context context) { mContext = context; } @Override protected Void doInBackground(Uri... uris) { try { fetchUri(uris[0]); } catch (IOException e) { Log.d("LoadTvInputTask", "fetchUri error"); } return null; } private void fetchUri(Uri videoUri) throws IOException { InputStream inputStream = null; try { inputStream = mContext.getContentResolver().openInputStream(videoUri); XmlPullParser parser = Xml.newPullParser(); try { parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false); parser.setInput(inputStream, null); sTvInput = ChannelXMLParser.parseTvInput(parser); sSampleChannels = ChannelXMLParser.parseChannelXML(parser); } catch (XmlPullParserException e) { e.printStackTrace(); } } finally { if (inputStream != null) { inputStream.close(); } } } }
إذا كنت بحاجة إلى تعديل بيانات "الدليل الإلكتروني للبرامج" بشكل منتظم، ننصحك باستخدام
WorkManager
لتشغيل عملية التعديل خلال وقت عدم النشاط، مثلاً كل يوم في الساعة 3:00 صباحًا.
تشمل الطرق الأخرى لفصل مهام تعديل البيانات عن سلسلة واجهة المستخدم استخدام فئة
HandlerThread، أو يمكنك تنفيذ فئة خاصة بك باستخدام Looper
و Handler فئتَي. لمزيد من المعلومات، يُرجى الاطّلاع على مقالة
العمليات وسلاسل التعليمات.
إضافة معلومات رابط التطبيق
يمكن للقنوات استخدام روابط التطبيقات للسماح للمستخدمين بتشغيل نشاط ذي صلة أثناء مشاهدة محتوى القناة. تستخدِم تطبيقات القنوات روابط التطبيقات لتوسيع نطاق تفاعل المستخدمين من خلال تشغيل الأنشطة التي تعرض معلومات ذات صلة أو محتوى إضافيًا. على سبيل المثال، يمكنك استخدام روابط التطبيقات لإجراء ما يلي:
- توجيه المستخدم لاكتشاف المحتوى ذي الصلة وشرائه
- توفير معلومات إضافية عن المحتوى الذي يتم تشغيله حاليًا
- بدء مشاهدة الحلقة التالية في مسلسل أثناء مشاهدة محتوى الحلقة
- السماح للمستخدم بالتفاعل مع المحتوى، مثلاً تقييمه أو مراجعته، بدون مقاطعة تشغيل المحتوى
تظهر روابط التطبيقات عندما يضغط المستخدم على اختيار لعرض قائمة التلفزيون أثناء مشاهدة محتوى القناة.
الشكل 1: مثال على رابط تطبيق معروض في صف القنوات أثناء عرض محتوى القناة
عندما يختار المستخدم رابط التطبيق، يبدأ النظام نشاطًا باستخدام معرّف URI للغرض محدّد من قِبل تطبيق القناة. ويستمر تشغيل محتوى القناة أثناء تفعيل نشاط رابط التطبيق. يمكن للمستخدم العودة إلى محتوى القناة بالضغط على رجوع.
توفير بيانات القناة لرابط التطبيق
ينشئ Android TV تلقائيًا رابط تطبيق لكل قناة، باستخدام معلومات من بيانات القناة. لتوفير معلومات رابط التطبيق، حدِّد التفاصيل التالية في حقول TvContract.Channels:
COLUMN_APP_LINK_COLOR- لون التمييز لرابط التطبيق لهذه القناة. للاطّلاع على مثال على لون التمييز، يُرجى مراجعة الشكل 2، وسيلة الشرح 3.COLUMN_APP_LINK_ICON_URI- معرّف URI لرمز شارة التطبيق لرابط التطبيق لهذه القناة للاطّلاع على مثال على رمز شارة التطبيق، يُرجى مراجعة الشكل 2، الشرح 2.COLUMN_APP_LINK_INTENT_URI- معرّف URI للغرض لرابط التطبيق لهذه القناة يمكنك إنشاء معرّف URI باستخدامtoUri(int)معURI_INTENT_SCHEMEوتحويل معرّف URI مرة أخرى إلى الغرض الأصلي باستخدامparseUri.COLUMN_APP_LINK_POSTER_ART_URI: معرّف URI لصورة الملصق المستخدَمة كخلفية لرابط التطبيق لهذه القناة للاطّلاع على مثال على صورة الملصق، يُرجى مراجعة الشكل 2، الشرح 1.COLUMN_APP_LINK_TEXT- النص الوصفي لرابط التطبيق لهذه القناة للاطّلاع على مثال على وصف رابط التطبيق، يُرجى مراجعة النص في الشكل 2، الشرح 3.
إذا لم تحدّد بيانات القناة معلومات رابط التطبيق، ينشئ النظام رابط تطبيق تلقائيًا. يختار النظام التفاصيل التلقائية على النحو التالي:
- بالنسبة إلى معرّف URI للغرض (
COLUMN_APP_LINK_INTENT_URI)، يستخدم النظام نشاطACTION_MAINلفئةCATEGORY_LEANBACK_LAUNCHER، التي يتم تحديدها عادةً في بيان التطبيق. إذا لم يتم تحديد هذا النشاط، يظهر رابط تطبيق غير صالح، وإذا نقر عليه المستخدم، لن يحدث أي شيء. - بالنسبة إلى النص الوصفي
(
COLUMN_APP_LINK_TEXT)، يستخدم النظام "فتح app-name". إذا لم يتم تحديد معرّف URI صالح للغرض لرابط التطبيق، يستخدم النظام "ما مِن رابط متاح". - بالنسبة إلى اللون الأساسي (
COLUMN_APP_LINK_COLOR)، يستخدم النظام اللون التلقائي للتطبيق. - بالنسبة إلى صورة الملصق (
COLUMN_APP_LINK_POSTER_ART_URI)، يستخدم النظام بانر الشاشة الرئيسية للتطبيق. إذا لم يوفّر التطبيق بانر، يستخدم النظام صورة تلقائية لتطبيق بث تلفزيوني. - بالنسبة إلى رمز الشارة (
COLUMN_APP_LINK_ICON_URI)، يستخدم النظام شارة تعرض اسم التطبيق. إذا كان النظام يستخدم أيضًا بانر التطبيق أو صورة التطبيق التلقائية لصورة الملصق، لن يتم عرض أي شارة للتطبيق.
يمكنك تحديد تفاصيل رابط التطبيق لقنواتك في نشاط الإعداد لتطبيقك. يمكنك تعديل تفاصيل رابط التطبيق هذه في أي وقت، لذا إذا كان رابط التطبيق بحاجة إلى مطابقة تغييرات القناة، عليك تعديل تفاصيل رابط التطبيق واستدعاء ContentResolver.update حسب الحاجة. لمزيد من التفاصيل حول تعديل
بيانات القناة، يُرجى الاطّلاع على مقالة تعديل بيانات القناة.