تطوير خدمة إدخال تلفزيون

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

إنشاء خدمة إدخال التلفزيون باستخدام TIF Companion Library

‫TIF Companion Library هو إطار عمل يوفّر عمليات تنفيذ قابلة للتوسيع لميزات خدمة إدخال التلفزيون الشائعة. من المفترض أن يستخدمه مصنّعو المعدات الأصلية لإنشاء قنوات لنظام Android 5.0 (المستوى 21 من واجهة برمجة التطبيقات) إلى Android 7.1 (المستوى 25 من واجهة برمجة التطبيقات) فقط.

تعديل مشروعك

تتوفّر TIF Companion Library للاستخدام القديم من قِبل مصنّعي المعدات الأصلية في مستودع androidtv-sample-inputs. يحتوي هذا المستودع على مثال يوضّح كيفية تضمين المكتبة في أحد التطبيقات.

تعريف خدمة إدخال التلفزيون في البيان

يجب أن يوفّر تطبيقك خدمة متوافقة مع TvInputService يستخدمها النظام للوصول إلى تطبيقك. توفّر TIF Companion Library الفئة BaseTvInputService التي توفّر عملية تنفيذ تلقائية لـ TvInputService يمكنك تخصيصها. أنشئ فئة فرعية من BaseTvInputService وعرِّف الفئة الفرعية في البيان كخدمة.

ضمن تعريف البيان، حدِّد الإذن BIND_TV_INPUT للسماح للخدمة بربط إدخال التلفزيون بالنظام. تنفّذ إحدى خدمات النظام عملية الربط ولديها الإذن BIND_TV_INPUT. يرسل تطبيق بث تلفزيوني على النظام طلبات إلى خدمات إدخال التلفزيون من خلال واجهة TvInputManager.

في تعريف الخدمة، ضِّمن intent filter يحدّد TvInputService كإجراء يتم تنفيذه باستخدام intent. عرِّف أيضًا البيانات الوصفية للخدمة كمورد XML منفصل. يظهر تعريف الخدمة وintent filter وتعريف البيانات الوصفية للخدمة في المثال التالي:

<service android:name=".rich.RichTvInputService"
    android:label="@string/rich_input_label"
    android:permission="android.permission.BIND_TV_INPUT">
    <!-- Required filter used by the system to launch our account service. -->
    <intent-filter>
        <action android:name="android.media.tv.TvInputService" />
    </intent-filter>
    <!-- An XML file which describes this input. This provides pointers to
    the RichTvInputSetupActivity to the system/TV app. -->
    <meta-data
        android:name="android.media.tv.input"
        android:resource="@xml/richtvinputservice" />
</service>

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

يقع ملف البيانات الوصفية للخدمة في دليل موارد XML لتطبيقك ويجب أن يتطابق مع اسم المورد الذي عرَّفته في البيان. باستخدام إدخالات البيان من المثال السابق، يمكنك إنشاء ملف XML في res/xml/richtvinputservice.xml، مع المحتويات التالية:

<?xml version="1.0" encoding="utf-8"?>
<tv-input xmlns:android="http://schemas.android.com/apk/res/android"
  android:canRecord="true"
  android:setupActivity="com.example.android.sampletvinput.rich.RichTvInputSetupActivity" />

تعريف القنوات وإنشاء نشاط الإعداد

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

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

<uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED "/>

أضِف العنصر التالي للتأكّد من ظهور تطبيقك في "متجر Google Play" كتطبيق يوفّر قنوات محتوى في Android TV:

<uses-feature
    android:name="android.software.live_tv"
    android:required="true" />

بعد ذلك، أنشئ فئة توسّع الفئة EpgSyncJobService. تتيح لك هذه الفئة المجردة إنشاء خدمة مهام تنشئ القنوات وتعدّلها في قاعدة بيانات النظام.

في الفئة الفرعية، أنشئ قائمة القنوات الكاملة وأرجِعها في getChannels. إذا كانت قنواتك تأتي من ملف XMLTV، استخدِم الفئة XmlTvParser. وإلا، أنشئ القنوات آليًا باستخدام الفئة Channel.Builder.

بالنسبة إلى كل قناة، يستدعي النظام getProgramsForChannel عندما يحتاج إلى قائمة بالبرامج التي يمكن مشاهدتها خلال فترة زمنية معيّنة على القناة. أرجِع قائمة بكائنات Program للقناة. استخدِم الفئة XmlTvParser للحصول على البرامج من ملف XMLTV، أو أنشئها آليًا باستخدام الفئة Program.Builder.

بالنسبة إلى كل كائن Program، استخدِم كائن InternalProviderData لضبط معلومات البرنامج، مثل نوع الفيديو الخاص بالبرنامج. إذا كان لديك عدد محدود فقط من البرامج التي تريد أن تكرّرها القناة في حلقة متكرّرة، استخدِم الطريقة InternalProviderData.setRepeatable مع القيمة true عند ضبط معلومات عن برنامجك.

بعد تنفيذ خدمة المهام، أضِفها إلى بيان تطبيقك:

<service
    android:name=".sync.SampleJobService"
    android:permission="android.permission.BIND_JOB_SERVICE"
    android:exported="true" />

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

Kotlin

val inputId = getActivity().intent.getStringExtra(TvInputInfo.EXTRA_INPUT_ID)
EpgSyncJobService.cancelAllSyncRequests(getActivity())
EpgSyncJobService.requestImmediateSync(
        getActivity(),
        inputId,
        ComponentName(getActivity(), SampleJobService::class.java)
)

Java

String inputId = getActivity().getIntent().getStringExtra(TvInputInfo.EXTRA_INPUT_ID);
EpgSyncJobService.cancelAllSyncRequests(getActivity());
EpgSyncJobService.requestImmediateSync(getActivity(), inputId,
        new ComponentName(getActivity(), SampleJobService.class));

استخدِم الطريقة requestImmediateSync لمزامنة خدمة المهام. على المستخدم الانتظار حتى تنتهي المزامنة، لذا يجب أن تكون فترة الطلب قصيرة نسبيًا.

استخدِم الطريقة setUpPeriodicSync لكي تتم مزامنة بيانات القناة والبرنامج بشكل دوري في الخلفية:

Kotlin

EpgSyncJobService.setUpPeriodicSync(
        context,
        inputId,
        ComponentName(context, SampleJobService::class.java)
)

Java

EpgSyncJobService.setUpPeriodicSync(context, inputId,
        new ComponentName(context, SampleJobService.class));

توفّر TIF Companion Library طريقة إضافية محملة بشكل زائد من requestImmediateSync تتيح لك تحديد مدة بيانات القناة التي تتم مزامنتها بالملّي ثانية. تتم مزامنة بيانات القناة لمدة ساعة واحدة باستخدام الطريقة التلقائية.

توفّر TIF Companion Library أيضًا طريقة إضافية محملة بشكل زائد من setUpPeriodicSync تتيح لك تحديد مدة بيانات القناة التي تتم مزامنتها، وعدد مرات حدوث المزامنة الدورية. تتم مزامنة بيانات القناة لمدة 48 ساعة كل 12 ساعة باستخدام الطريقة التلقائية.

لمزيد من التفاصيل عن بيانات القناة ودليل البرامج الإلكتروني، يُرجى الاطّلاع على مقالة استخدام بيانات القناة.

التعامل مع طلبات الضبط وتشغيل الوسائط

عندما يختار المستخدم قناة معيّنة، يستخدم تطبيق بث تلفزيوني على النظام Session، الذي أنشأه تطبيقك، للضبط على القناة المطلوبة وتشغيل المحتوى. توفّر TIF Companion Library عدة فئات يمكنك توسيعها للتعامل مع طلبات القناة والجلسة من النظام.

تنشئ الفئة الفرعية BaseTvInputService جلسات تتعامل مع طلبات الضبط. ألغِ الطريقة onCreateSession، وأنشئ جلسة ممتدة من الفئة BaseTvInputService.Session، واستخدِم super.sessionCreated مع جلستك الجديدة. في المثال التالي، تُرجِع onCreateSession كائن RichTvInputSessionImpl يوسّع BaseTvInputService.Session:

Kotlin

override fun onCreateSession(inputId: String): Session =
        RichTvInputSessionImpl(this, inputId).apply {
            setOverlayViewEnabled(true)
        }

Java

@Override
public final Session onCreateSession(String inputId) {
    RichTvInputSessionImpl session = new RichTvInputSessionImpl(this, inputId);
    session.setOverlayViewEnabled(true);
    return session;
}

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

بعد ذلك، يحصل النظام على البرنامج المجدول حاليًا ويستدعي الطريقة onPlayProgram في جلستك، مع تحديد معلومات البرنامج ووقت البدء بالملّي ثانية. استخدِم واجهة TvPlayer لبدء تشغيل البرنامج.

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

في الطريقة getTvPlayer في جلستك، أرجِع مشغّل الوسائط الذي ينفّذ TvPlayer. ينفّذ تطبيق TV Input Service النموذجي مشغّل وسائط يستخدم ExoPlayer.

إنشاء خدمة إدخال التلفزيون باستخدام TV Input Framework

إذا تعذّر على خدمة إدخال التلفزيون استخدام TIF Companion Library، عليك تنفيذ المكوّنات التالية:

  • TvInputService توفّر إمكانية الوصول إلى إدخال التلفزيون لفترة طويلة وفي الخلفية لـ
  • TvInputService.Session تحتفظ بحالة إدخال التلفزيون وتتواصل مع التطبيق المضيف
  • TvContract يصف القنوات والبرامج المتاحة لإدخال التلفزيون
  • تمثّل TvContract.Channels معلومات عن قناة تلفزيونية
  • TvContract.Programs يصف برنامجًا تلفزيونيًا يتضمّن بيانات، مثل عنوان البرنامج ووقت البدء
  • يمثّل TvTrackInfo مسارًا صوتيًا أو فيديو أو ترجمة
  • TvContentRating يصف التقييم حسب الفئة العمرية للمحتوى، ويسمح بمخططات تقييم محتوى مخصّصة
  • TvInputManager توفّر واجهة برمجة تطبيقات لتطبيق بث تلفزيوني على النظام وتدير التفاعل مع إدخالات التلفزيون والتطبيقات

عليك أيضًا تنفيذ ما يلي:

  1. عرِّف خدمة إدخال التلفزيون في البيان، كما هو موضّح في تعريف خدمة إدخال التلفزيون في البيان.
  2. أنشئ ملف البيانات الوصفية للخدمة.
  3. أنشئ معلومات القناة والبرنامج وسجِّلها.
  4. أنشئ نشاط الإعداد.

تعريف خدمة إدخال التلفزيون

بالنسبة إلى خدمتك، يمكنك توسيع الفئة TvInputService. إنّ عملية تنفيذ A TvInputService هي خدمة مرتبطة يكون فيها النظام هو العميل الذي يرتبط بها. تظهر طرق دورة حياة الخدمة التي عليك تنفيذها في الشكل 1.

تهيّئ الطريقة onCreate وتُشغّل HandlerThread الذي يوفّر سلسلة عمليات منفصلة عن سلسلة واجهة المستخدم للتعامل مع الإجراءات التي يحركها النظام. في المثال التالي، تهيّئ الطريقة onCreate السمة CaptioningManager وتستعد للتعامل مع الإجراءَين ACTION_BLOCKED_RATINGS_CHANGED وACTION_PARENTAL_CONTROLS_ENABLED_CHANGED. تصف هذه الإجراءات أهداف النظام التي يتم إطلاقها عندما يغيّر المستخدم إعدادات التحكّم الأبوي، وعند حدوث تغيير في قائمة التقييمات المحظورة.

Kotlin

override fun onCreate() {
    super.onCreate()
    handlerThread = HandlerThread(javaClass.simpleName).apply {
        start()
    }
    dbHandler = Handler(handlerThread.looper)
    handler = Handler()
    captioningManager = getSystemService(Context.CAPTIONING_SERVICE) as CaptioningManager

    setTheme(android.R.style.Theme_Holo_Light_NoActionBar)

    sessions = mutableListOf<BaseTvInputSessionImpl>()
    val intentFilter = IntentFilter().apply {
        addAction(TvInputManager.ACTION_BLOCKED_RATINGS_CHANGED)
        addAction(TvInputManager.ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED)
    }
    registerReceiver(broadcastReceiver, intentFilter)
}

Java

@Override
public void onCreate() {
    super.onCreate();
    handlerThread = new HandlerThread(getClass()
      .getSimpleName());
    handlerThread.start();
    dbHandler = new Handler(handlerThread.getLooper());
    handler = new Handler();
    captioningManager = (CaptioningManager)
      getSystemService(Context.CAPTIONING_SERVICE);

    setTheme(android.R.style.Theme_Holo_Light_NoActionBar);

    sessions = new ArrayList<BaseTvInputSessionImpl>();
    IntentFilter intentFilter = new IntentFilter();
    intentFilter.addAction(TvInputManager
      .ACTION_BLOCKED_RATINGS_CHANGED);
    intentFilter.addAction(TvInputManager
      .ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED);
    registerReceiver(broadcastReceiver, intentFilter);
}

الشكل 1. دورة حياة TvInputService

يُرجى الاطّلاع على مقالة التحكّم في المحتوى لمزيد من المعلومات عن استخدام المحتوى المحظور وتوفير التحكّم الأبوي. يُرجى الاطّلاع على TvInputManager لمزيد من الإجراءات التي يحركها النظام والتي قد تريد التعامل معها في خدمة إدخال التلفزيون.

تنشئ TvInputService كائنًا TvInputService.Session ينفّذ Handler.Callback للتعامل مع تغييرات حالة المشغّل. باستخدام onSetSurface, يضبط TvInputService.Session السمة Surface مع محتوى الفيديو. يُرجى الاطّلاع على مقالة دمج المشغّل مع السطح لمزيد من المعلومات عن استخدام Surface لعرض الفيديو.

يتعامل TvInputService.Session مع الحدث onTune عندما يختار المستخدم قناة، ويُعلم تطبيق بث تلفزيوني على النظام بالتغييرات في المحتوى والبيانات الوصفية للمحتوى. تتوفّر تفاصيل طرق notify هذه في التحكّم في المحتوى والتعامل مع اختيار المسار لاحقًا في هذا التدريب.

تعريف نشاط الإعداد

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

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

مراجع إضافية