استخدام بيانات القناة

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

جرِّب نموذج تطبيق خدمة إدخال التلفزيون.

الحصول على الإذن

لكي يعمل إدخال التلفزيون مع بيانات EPG، يجب أن يتضمّن التقرير إذن الكتابة في ملف بيان Android الخاص به على النحو التالي:

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

تسجيل القنوات في قاعدة البيانات

تحتفظ قاعدة بيانات نظام Android TV بسجلات بيانات القناة لإدخالات TV. خلال نشاط الإعداد، يجب ربط بيانات قناتك بالحقول التالية لفئة TvContract.Channels، وذلك لكل قناة:

على الرغم من أنّ إطار عمل إدخال التلفزيون عام بما يكفي للتعامل مع كلّ من محتوى البث التقليدي ومحتوى البث المباشر بدون أي تمييز، قد تحتاج إلى تحديد الأعمدة التالية بالإضافة إلى الأعمدة الواردة أعلاه لتحديد قنوات البث التقليدية بشكل أفضل:

إذا كنت تريد تقديم تفاصيل رابط التطبيق لقنواتك، فستحتاج إلى تحديث بعض الحقول الإضافية. لمزيد من المعلومات عن حقول "رابط التطبيق"، اطّلِع على إضافة معلومات "رابط التطبيق".

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

اسحب البيانات الوصفية لقناتك (بتنسيق 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 هو كائن يحتوي على البيانات الوصفية للقناة من خادم الخلفية.

تقديم معلومات عن القناة والبرنامج

يعرض تطبيق System TV معلومات القناة والبرنامج للمستخدمين أثناء تنقّلهم بين القنوات، كما هو موضّح في الشكل 1. للتأكّد من توافق معلومات القناة والبرنامج مع مقدّم معلومات القناة والبرنامج في تطبيق YouTube TV، اتّبِع الإرشادات أدناه.

  1. رقم القناة (COLUMN_DISPLAY_NUMBER)
  2. الرمز (android:icon في بيان إدخال التلفزيون)
  3. وصف البرنامج (COLUMN_SHORT_DESCRIPTION)
  4. عنوان البرنامج (COLUMN_TITLE)
  5. شعار القناة (TvContract.Channels.Logo)
    • استخدام اللون #EEEEEE" لمطابقة النص المحيط
    • لا تحتوي على مساحة متروكة.
  6. صورة الملصق (COLUMN_POSTER_ART_URI)
    • نسبة العرض إلى الارتفاع بين 16:9 و4:3

الشكل 1. قناة تطبيق YouTube TV ومقدّم معلومات البرنامج.

يوفر تطبيق System TV المعلومات نفسها من خلال دليل البرنامج، بما في ذلك صورة الملصق، كما هو موضح في الشكل 2.

الشكل 2. دليل برنامج تطبيق YouTube TV لأجهزة التلفزيون.

تعديل بيانات القناة

عند تعديل بيانات القناة الحالية، استخدِم طريقة update() بدلاً من حذف البيانات وإعادة إضافتها. يمكنك تحديد الإصدار الحالي من البيانات باستخدام Channels.COLUMN_VERSION_NUMBER وPrograms.COLUMN_VERSION_NUMBER عند اختيار السجلات لتحديثها.

ملاحظة: قد تستغرق إضافة بيانات القناة إلى ContentProvider بعض الوقت. أضِف البرامج الحالية (تلك التي تكون ضمن ساعتين من الوقت الحالي) فقط عند إعداد EpgSyncJobService لتعديل بقية بيانات القناة في الخلفية. يمكنك الاطّلاع على نموذج تطبيق Android TV Live TV للحصول على مثال.

تحميل بيانات القناة بشكل مجمّع

عند تحديث قاعدة بيانات النظام باستخدام كمية كبيرة من بيانات القناة، استخدِم الطريقة ContentResolver applyBatch() أو bulkInsert(). إليك مثال على استخدام السمة 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();
            }
        }
    }
}

إذا كنت بحاجة إلى تعديل بيانات EPG بشكل منتظم، يمكنك استخدام 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، وتحويله إلى هدفه الأصلي باستخدام parseUri().
  • COLUMN_APP_LINK_POSTER_ART_URI - معرّف الموارد المنتظم (URI) لصورة الملصق المستخدَمة كخلفية لرابط التطبيق في هذه القناة بالنسبة إلى مثال لصورة ملصق، انظر الشكل 2، وسيلة الشرح 1.
  • COLUMN_APP_LINK_TEXT - نص الرابط الوصفي لرابط التطبيق الخاص بهذه القناة للحصول على مثال لوصف رابط التطبيق، راجِع النص في الشكل 2، وسيلة الشرح 3.

الشكل 2. تفاصيل رابط التطبيق

وإذا لم تحدّد بيانات القناة معلومات رابط التطبيق، ينشئ النظام رابط تطبيق تلقائيًا. يختار النظام التفاصيل التلقائية على النحو التالي:

  • بالنسبة إلى معرّف الموارد المنتظم (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() حسب الحاجة. للاطّلاع على مزيد من التفاصيل حول تعديل بيانات القناة، يمكنك الاطّلاع على مقالة تعديل بيانات القناة.