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

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

برنامه نمونه «خدمات ورودی تلویزیون» را امتحان کنید.

دریافت اجازه

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

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

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

پایگاه داده سیستم 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. شماره کانال (COLUMN_DISPLAY_NUMBER)
  2. نماد (android:icon در مانیفست ورودی تلویزیون)
  3. شرح برنامه (COLUMN_SHORT_DESCRIPTION)
  4. عنوان برنامه (COLUMN_TITLE)
  5. نشان‌واره کانال (TvContract.Channels.Logo)
    • از رنگ #EEEEEE برای مطابقت با نوشتار اطراف استفاده کنید
    • حاشیه اضافه نشود
  6. هنر پوستر (COLUMN_POSTER_ART_URI)
    • نسبت ابعادی بین ۱۶:۹ و ۴:۳
شکل ۱. ارائه‌دهنده اطلاعات برنامه و کانال برنامه تلویزیون سیستم.

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

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

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

هنگام به‌روزرسانی داده‌های کانال موجود، از روش 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.newPullPars>er().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 برای اجرای فرایند به‌روزرسانی در زمان‌های بیکاری، مثلاً هر روز ساعت ۳:۰۰ بامداد، استفاده کنید.

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

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

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

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

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

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

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

‫Android TV به‌طور خودکار برای هر کانال پیوند برنامه ایجاد می‌کند و از اطلاعات داده‌های کانال استفاده می‌کند. برای ارائه اطلاعات پیوند برنامه، جزئیات زیر را در TvContract.Channels فیلد مشخص کنید:

  • ‫COLUMN_APP_LINK_COLOR - رنگ تأکیدی پیوند برنامه برای این کانال. برای نمونه رنگ ثانویه، شکل ۲، شماره ۳ را ببینید.
  • COLUMN_APP_LINK_ICON_URI - نشانی وب نماد نشان برنامه پیوند برنامه برای این کانال. برای نمونه نماد نشان برنامه، به شکل ۲، شماره ۲ مراجعه کنید.
  • ‫COLUMN_APP_LINK_INTENT_URI - نشانی وب هدف پیوند برنامه برای این کانال. می‌توانید نشانی وب را بااستفاده از toUri(int) با URI_INTENT_SCHEME ایجاد کنید و نشانی وب را با parseUri به هدف اصلی تبدیل کنید.
  • COLUMN_APP_LINK_POSTER_ART_URI - نشانی وب منبع (URI) برای جلد آلبوم استفاده‌شده به‌عنوان پس‌زمینه پیوند برنامه برای این کانال. برای نمونه تصویر پوستر، به شکل ۲، شماره ۱ مراجعه کنید.
  • COLUMN_APP_LINK_TEXT - نوشتار پیوند توصیفی پیوند برنامه برای این کانال. برای نمونه شرح پیوند برنامه، نوشتار را در شکل ۲، توضیح ۳ ببینید.
شکل ۲. جزئیات پیوند برنامه.

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

  • برای نشانی وب هدف (COLUMN_APP_LINK_INTENT_URI)، سیستم از فعالیت ACTION_MAIN برای دسته CATEGORY_LEANBACK_LAUNCHER استفاده می‌کند که معمولاً در مانیفست برنامه تعریف می‌شود. اگر این فعالیت تعریف نشده باشد، پیوند برنامه غیرفعالی نمایش داده می‌شود—اگر کاربر روی آن کلیک کند، هیچ اتفاقی نمی‌افتد.
  • برای نوشتار توصیفی (COLUMN_APP_LINK_TEXT)، سیستم از «باز کردن app-name» استفاده می‌کند. اگر نشانی وب هدف پیوند برنامه مناسبی تعریف نشده باشد، سیستم از «پیوندی دردسترس نیست» استفاده می‌کند.
  • برای رنگ ثانویه (COLUMN_APP_LINK_COLOR)، سیستم از رنگ پیش‌فرض برنامه استفاده می‌کند.
  • برای تصویر پوستر (COLUMN_APP_LINK_POSTER_ART_URI)، سیستم از برنمای صفحه اصلی برنامه استفاده می‌کند. اگر برنامه برنمایی ارائه نکند، سیستم از تصویر برنامه تلویزیون پیش‌فرض استفاده می‌کند.
  • برای نماد نشان (COLUMN_APP_LINK_ICON_URI)، سیستم از نشانی استفاده می‌کند که نام برنامه را نشان می‌دهد. اگر سیستم از برنمای برنامه یا تصویر برنامه پیش‌فرض برای تصویر پوستر استفاده کند، نشان برنامه نشان داده نمی‌شود.

جزئیات پیوند برنامه را برای کانال‌هایتان در فعالیت راه‌اندازی برنامه‌تان مشخص می‌کنید. هرزمان بخواهید می‌توانید جزئیات این پیوند برنامه را به‌روز کنید، بنابراین اگر پیوند برنامه باید با تغییرات کانال مطابقت داشته باشد، جزئیات پیوند برنامه را به‌روز کنید و درصورت نیاز با ContentResolver.update تماس بگیرید. برای جزئیات بیشتر درباره به‌روزرسانی داده‌های کانال، به به‌روزرسانی داده‌های کانال مراجعه کنید.