В настройках вашего телевизионного входа должны отображаться данные электронной программы передач (EPG) как минимум для одного канала. Также следует периодически обновлять эти данные, учитывая размер обновления и поток обработки. Кроме того, можно добавить ссылки на приложения для каналов, которые помогут пользователю перейти к связанному контенту и действиям. В этом уроке рассматривается создание и обновление данных о каналах и программах в системной базе данных с учетом этих соображений.
Попробуйте демонстрационное приложение TV Input Service .
Получите разрешение
Для того чтобы ваш телевизор работал с данными электронной программы передач (EPG), необходимо указать разрешение на запись в файле манифеста 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— формат видео по умолчанию для канала.
Хотя структура ввода для телевизора достаточно универсальна и позволяет обрабатывать как традиционный эфирный контент, так и контент, передаваемый через OTT-платформы, без каких-либо различий, для более точной идентификации традиционных эфирных каналов может потребоваться определить следующие столбцы:
-
COLUMN_ORIGINAL_NETWORK_ID- идентификатор телевизионной сети -
COLUMN_SERVICE_ID- идентификатор услуги -
COLUMN_TRANSPORT_STREAM_ID- идентификатор транспортного потока
Если вы хотите добавить ссылки на приложения для своих каналов, вам необходимо обновить несколько дополнительных полей. Дополнительную информацию о полях для ссылок на приложения см. в разделе «Добавление информации о ссылках на приложения» .
Для телевизионных входов, использующих интернет-трансляцию, задайте соответствующие значения, чтобы каждый канал можно было однозначно идентифицировать.
Получите метаданные вашего канала (в формате 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)
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. Чтобы убедиться, что информация о каналах и программах работает с функцией отображения информации о каналах и программах в приложении System TV, следуйте этим рекомендациям:
- Номер канала (
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 :
Котлин
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 следующим образом:
Котлин
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, и преобразовать 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) система использует "Open app-name ". Если не определен подходящий URI для ссылки на приложение, система использует "No link available". - Для акцентного цвета (
COLUMN_APP_LINK_COLOR) система использует цвет приложения по умолчанию. - Для изображения-постера (
COLUMN_APP_LINK_POSTER_ART_URI) система использует баннер главного экрана приложения. Если приложение не предоставляет баннер, система использует изображение приложения для ТВ по умолчанию. - Для значка приложения (
COLUMN_APP_LINK_ICON_URI) система использует значок, отображающий название приложения. Если в качестве изображения для постера также используется баннер приложения или изображение приложения по умолчанию, значок приложения не отображается.
В настройках приложения вы указываете сведения о ссылках на каналы. Эти сведения можно обновить в любой момент, поэтому, если ссылка на приложение должна соответствовать изменениям канала, обновите сведения о ссылке и вызовите ContentResolver.update по мере необходимости. Дополнительные сведения об обновлении данных канала см. в разделе «Обновление данных канала» .
В настройках вашего телевизионного входа должны отображаться данные электронной программы передач (EPG) как минимум для одного канала. Также следует периодически обновлять эти данные, учитывая размер обновления и поток обработки. Кроме того, можно добавить ссылки на приложения для каналов, которые помогут пользователю перейти к связанному контенту и действиям. В этом уроке рассматривается создание и обновление данных о каналах и программах в системной базе данных с учетом этих соображений.
Попробуйте демонстрационное приложение TV Input Service .
Получите разрешение
Для того чтобы ваш телевизор работал с данными электронной программы передач (EPG), необходимо указать разрешение на запись в файле манифеста 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— формат видео по умолчанию для канала.
Хотя структура ввода для телевизора достаточно универсальна и позволяет обрабатывать как традиционный эфирный контент, так и контент, передаваемый через OTT-платформы, без каких-либо различий, для более точной идентификации традиционных эфирных каналов может потребоваться определить следующие столбцы:
-
COLUMN_ORIGINAL_NETWORK_ID- идентификатор телевизионной сети -
COLUMN_SERVICE_ID- идентификатор услуги -
COLUMN_TRANSPORT_STREAM_ID- идентификатор транспортного потока
Если вы хотите добавить ссылки на приложения для своих каналов, вам необходимо обновить несколько дополнительных полей. Дополнительную информацию о полях для ссылок на приложения см. в разделе «Добавление информации о ссылках на приложения» .
Для телевизионных входов, использующих интернет-трансляцию, задайте соответствующие значения, чтобы каждый канал можно было однозначно идентифицировать.
Получите метаданные вашего канала (в формате 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)
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. Чтобы убедиться, что информация о каналах и программах работает с функцией отображения информации о каналах и программах в приложении System TV, следуйте этим рекомендациям:
- Номер канала (
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 :
Котлин
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 следующим образом:
Котлин
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, и преобразовать 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) система использует "Open app-name ". Если не определен подходящий URI для ссылки на приложение, система использует "No link available". - Для акцентного цвета (
COLUMN_APP_LINK_COLOR) система использует цвет приложения по умолчанию. - Для изображения-постера (
COLUMN_APP_LINK_POSTER_ART_URI) система использует баннер главного экрана приложения. Если приложение не предоставляет баннер, система использует изображение приложения для ТВ по умолчанию. - Для значка приложения (
COLUMN_APP_LINK_ICON_URI) система использует значок, отображающий название приложения. Если в качестве изображения для постера также используется баннер приложения или изображение приложения по умолчанию, значок приложения не отображается.
В настройках приложения вы указываете сведения о ссылках на каналы. Эти сведения можно обновить в любой момент, поэтому, если ссылка на приложение должна соответствовать изменениям канала, обновите сведения о ссылке и вызовите ContentResolver.update по мере необходимости. Дополнительные сведения об обновлении данных канала см. в разделе «Обновление данных канала» .
В настройках вашего телевизионного входа должны отображаться данные электронной программы передач (EPG) как минимум для одного канала. Также следует периодически обновлять эти данные, учитывая размер обновления и поток обработки. Кроме того, можно добавить ссылки на приложения для каналов, которые помогут пользователю перейти к связанному контенту и действиям. В этом уроке рассматривается создание и обновление данных о каналах и программах в системной базе данных с учетом этих соображений.
Попробуйте демонстрационное приложение TV Input Service .
Получите разрешение
Для того чтобы ваш телевизор работал с данными электронной программы передач (EPG), необходимо указать разрешение на запись в файле манифеста 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— формат видео по умолчанию для канала.
Хотя структура ввода для телевизора достаточно универсальна и позволяет обрабатывать как традиционный эфирный контент, так и контент, передаваемый через OTT-платформы, без каких-либо различий, для более точной идентификации традиционных эфирных каналов может потребоваться определить следующие столбцы:
-
COLUMN_ORIGINAL_NETWORK_ID- идентификатор телевизионной сети -
COLUMN_SERVICE_ID- идентификатор услуги -
COLUMN_TRANSPORT_STREAM_ID- идентификатор транспортного потока
Если вы хотите добавить ссылки на приложения для своих каналов, вам необходимо обновить несколько дополнительных полей. Дополнительную информацию о полях для ссылок на приложения см. в разделе «Добавление информации о ссылках на приложения» .
Для телевизионных входов, использующих интернет-трансляцию, задайте соответствующие значения, чтобы каждый канал можно было однозначно идентифицировать.
Получите метаданные вашего канала (в формате 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)
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. Чтобы убедиться, что информация о каналах и программах работает с функцией отображения информации о каналах и программах в приложении System TV, следуйте этим рекомендациям:
- Номер канала (
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 :
Котлин
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 следующим образом:
Котлин
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, и преобразовать 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) система использует "Open app-name ". Если не определен подходящий URI для ссылки на приложение, система использует "No link available". - Для акцентного цвета (
COLUMN_APP_LINK_COLOR) система использует цвет приложения по умолчанию. - Для изображения-постера (
COLUMN_APP_LINK_POSTER_ART_URI) система использует баннер главного экрана приложения. Если приложение не предоставляет баннер, система использует изображение приложения для ТВ по умолчанию. - Для значка приложения (
COLUMN_APP_LINK_ICON_URI) система использует значок, отображающий название приложения. Если в качестве изображения для постера также используется баннер приложения или изображение приложения по умолчанию, значок приложения не отображается.
В настройках приложения вы указываете сведения о ссылках на каналы. Эти сведения можно обновить в любой момент, поэтому, если ссылка на приложение должна соответствовать изменениям канала, обновите сведения о ссылке и вызовите ContentResolver.update по мере необходимости. Дополнительные сведения об обновлении данных канала см. в разделе «Обновление данных канала» .
В настройках вашего телевизионного входа должны отображаться данные электронной программы передач (EPG) как минимум для одного канала. Также следует периодически обновлять эти данные, учитывая размер обновления и поток обработки. Кроме того, можно добавить ссылки на приложения для каналов, которые помогут пользователю перейти к связанному контенту и действиям. В этом уроке рассматривается создание и обновление данных о каналах и программах в системной базе данных с учетом этих соображений.
Попробуйте демонстрационное приложение TV Input Service .
Получите разрешение
Для того чтобы ваш телевизор работал с данными электронной программы передач (EPG), необходимо указать разрешение на запись в файле манифеста 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— формат видео по умолчанию для канала.
Хотя структура ввода для телевизора достаточно универсальна и позволяет обрабатывать как традиционный эфирный контент, так и контент, передаваемый через OTT-платформы, без каких-либо различий, для более точной идентификации традиционных эфирных каналов может потребоваться определить следующие столбцы:
-
COLUMN_ORIGINAL_NETWORK_ID- идентификатор телевизионной сети -
COLUMN_SERVICE_ID- идентификатор услуги -
COLUMN_TRANSPORT_STREAM_ID- идентификатор транспортного потока
Если вы хотите добавить ссылки на приложения для своих каналов, вам необходимо обновить несколько дополнительных полей. Дополнительную информацию о полях для ссылок на приложения см. в разделе «Добавление информации о ссылках на приложения» .
Для телевизионных входов, использующих интернет-трансляцию, задайте соответствующие значения, чтобы каждый канал можно было однозначно идентифицировать.
Получите метаданные вашего канала (в формате 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)
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. Чтобы убедиться, что информация о каналах и программах работает с функцией отображения информации о каналах и программах в приложении System TV, следуйте этим рекомендациям:
- Номер канала (
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 :
Котлин
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 следующим образом:
Котлин
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, и преобразовать URI обратно в исходный интент с помощьюparseUri. -
COLUMN_APP_LINK_POSTER_ART_URI— URI изображения постера, используемого в качестве фона ссылки на приложение для этого канала. Пример изображения постера см. на рисунке 2, выноска 1. -
COLUMN_APP_LINK_TEXT- The descriptive link text of the app link for this channel. For an example app link description, see the text in figure 2, callout 3.

If the channel data doesn't specify app link information, the system creates a default app link. The system chooses default details as follows:
- For the intent URI (
COLUMN_APP_LINK_INTENT_URI), the system uses theACTION_MAINactivity for theCATEGORY_LEANBACK_LAUNCHERcategory, typically defined in the app manifest. If this activity is not defined, a non-functioning app link appears—if the user clicks it, nothing happens. - For the descriptive text (
COLUMN_APP_LINK_TEXT), the system uses "Open app-name ". If no viable app link intent URI is defined, the system uses "No link available". - For the accent color (
COLUMN_APP_LINK_COLOR), the system uses the default app color. - For the poster image (
COLUMN_APP_LINK_POSTER_ART_URI), the system uses the app's home screen banner. If the app doesn't provide a banner, the system uses a default TV app image. - For the badge icon (
COLUMN_APP_LINK_ICON_URI), the system uses a badge that shows the app name. If the system is also using the app banner or default app image for the poster image, no app badge is shown.
You specify app link details for your channels in your app's setup activity. You can update these app link details at any point, so if an app link needs to match channel changes, update app link details and call ContentResolver.update as needed. For more details on updating channel data, see Update channel data .