با داده های کانال کار کنید

ورودی تلویزیون شما باید داده های راهنمای برنامه الکترونیکی (EPG) را برای حداقل یک کانال در فعالیت راه اندازی خود ارائه دهد. همچنین باید به‌طور دوره‌ای آن داده‌ها را با در نظر گرفتن اندازه به‌روزرسانی و رشته پردازشی که آن را مدیریت می‌کند، به‌روزرسانی کنید. علاوه بر این، می‌توانید پیوندهای برنامه را برای کانال‌هایی ارائه دهید که کاربر را به محتوا و فعالیت‌های مرتبط راهنمایی می‌کند. این درس ایجاد و به روز رسانی داده های کانال و برنامه در پایگاه داده سیستم را با در نظر گرفتن این ملاحظات مورد بحث قرار می دهد.

برنامه نمونه سرویس ورودی تلویزیون را امتحان کنید.

اجازه بگیرید

برای اینکه ورودی تلویزیون شما با داده های EPG کار کند، باید مجوز نوشتن را در فایل مانیفست اندروید خود به صورت زیر اعلام کند:

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

کانال ها را در پایگاه داده ثبت کنید

پایگاه داده سیستم Android TV سوابق داده های کانال را برای ورودی های تلویزیون نگهداری می کند. در فعالیت راه اندازی خود، برای هر یک از کانال های خود، باید داده های کانال خود را به فیلدهای زیر از کلاس TvContract.Channels نگاشت کنید:

اگرچه چارچوب ورودی تلویزیون به اندازه کافی عمومی است که بتواند هم پخش سنتی و هم محتوای بیش از حد (OTT) را بدون هیچ تمایزی انجام دهد، ممکن است بخواهید ستون‌های زیر را علاوه بر ستون‌های بالا برای شناسایی بهتر کانال‌های پخش سنتی تعریف کنید:

اگر می‌خواهید جزئیات پیوند برنامه را برای کانال‌های خود ارائه دهید، باید چند فیلد اضافی را به‌روزرسانی کنید. برای اطلاعات بیشتر در مورد فیلدهای پیوند برنامه، به افزودن اطلاعات پیوند برنامه مراجعه کنید.

برای ورودی‌های تلویزیون مبتنی بر پخش آنلاین، مقادیر خود را بر اساس آن به موارد بالا اختصاص دهید تا هر کانال به طور منحصر به فرد شناسایی شود.

ابرداده کانال خود (در XML، JSON یا هر چیز دیگری) را از سرور پشتیبان خود بیرون بکشید و در فعالیت راه اندازی، مقادیر را به صورت زیر به پایگاه داده سیستم ترسیم کنید:

کاتلین

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)

جاوا

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 نشان داده شده است، هنگام ورق زدن کانال ها، اطلاعات کانال و برنامه را به کاربران ارائه می دهد. برای اطمینان از کارکرد اطلاعات کانال و برنامه با کانال و ارائه کننده اطلاعات برنامه تلویزیون سیستم، دستورالعمل های زیر را دنبال کنید.

  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. کانال برنامه تلویزیون سیستم و ارائه کننده اطلاعات برنامه.

برنامه تلویزیون سیستم همان اطلاعات را از طریق راهنمای برنامه، از جمله هنر پوستر، همانطور که در شکل 2 نشان داده شده است، ارائه می دهد.

شکل 2. راهنمای برنامه برنامه تلویزیون سیستم.

به روز رسانی داده های کانال

هنگام به روز رسانی داده های کانال موجود، به جای حذف و اضافه کردن مجدد داده ها، از متد update() استفاده کنید. هنگام انتخاب سوابق برای به‌روزرسانی، می‌توانید نسخه فعلی داده‌ها را با استفاده از Channels.COLUMN_VERSION_NUMBER و Programs.COLUMN_VERSION_NUMBER شناسایی کنید.

توجه: افزودن داده های کانال به ContentProvider ممکن است زمان بر باشد. برنامه‌های فعلی (آنهایی که در عرض دو ساعت از زمان فعلی هستند) را فقط زمانی اضافه کنید که EpgSyncJobService خود را برای به‌روزرسانی بقیه داده‌های کانال در پس‌زمینه پیکربندی کنید. برای مثال به برنامه نمونه تلویزیون زنده Android TV مراجعه کنید.

داده های کانال بارگیری دسته ای

هنگام به روز رسانی پایگاه داده سیستم با حجم زیادی از داده های کانال، از روش ContentResolver applyBatch() یا bulkInsert() استفاده کنید. در اینجا یک مثال با استفاده از applyBatch() آورده شده است:

کاتلین

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()
    }
}

جاوا

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 به صورت زیر استفاده کنید:

کاتلین

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()
                }
            }
        }
    }
}

جاوا

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 صبح استفاده کنید.

تکنیک‌های دیگر برای جدا کردن وظایف به‌روزرسانی داده از رشته UI شامل استفاده از کلاس HandlerThread است، یا می‌توانید خودتان را با استفاده از کلاس‌های Looper و Handler پیاده‌سازی کنید. برای اطلاعات بیشتر به فرآیندها و موضوعات مراجعه کنید.

کانال‌ها می‌توانند از پیوندهای برنامه استفاده کنند تا به کاربران اجازه دهند به راحتی یک فعالیت مرتبط را در حین تماشای محتوای کانال اجرا کنند. برنامه‌های کانال از پیوندهای برنامه برای گسترش تعامل کاربر با راه‌اندازی فعالیت‌هایی که اطلاعات مرتبط یا محتوای اضافی را نشان می‌دهند، استفاده می‌کنند. به عنوان مثال، می توانید از پیوندهای برنامه برای انجام کارهای زیر استفاده کنید:

  • کاربر را برای کشف و خرید محتوای مرتبط راهنمایی کنید.
  • اطلاعات بیشتری درباره محتوای در حال پخش ارائه دهید.
  • در حین مشاهده محتوای اپیزودیک، شروع به مشاهده قسمت بعدی در یک سریال کنید.
  • به کاربر اجازه دهید با محتوا تعامل داشته باشد - به عنوان مثال، محتوا را رتبه بندی یا بررسی کند - بدون وقفه در پخش محتوا.

پیوندهای برنامه زمانی نمایش داده می شوند که کاربر انتخاب را فشار می دهد تا منوی تلویزیون در حین تماشای محتوای کانال نشان داده شود.

شکل 1. یک نمونه پیوند برنامه که در ردیف کانال ها در حالی که محتوای کانال نشان داده می شود، نمایش داده می شود.

هنگامی که کاربر پیوند برنامه را انتخاب می کند، سیستم با استفاده از یک URI هدف مشخص شده توسط برنامه کانال، فعالیتی را شروع می کند. تا زمانی که فعالیت پیوند برنامه فعال است، محتوای کانال به پخش ادامه می‌دهد. کاربر می تواند با فشار دادن Back به محتوای کانال برگردد.

داده های کانال پیوند برنامه را ارائه دهید

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() URI را به قصد اصلی تبدیل کنید.
  • 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() تماس بگیرید. برای جزئیات بیشتر در مورد به‌روزرسانی داده‌های کانال، به‌روزرسانی داده‌های کانال را ببینید.