Сервис ввода ТВ представляет собой источник медиапотока и позволяет представлять медиаконтент в виде каналов и программ, как на обычном телевидении. С помощью сервиса ввода ТВ вы можете предоставлять родительский контроль, информацию о телепрограмме и рейтинги контента. Сервис ввода ТВ работает с системным ТВ-приложением Android. Это приложение управляет контентом каналов и показывает его на телевизоре. Системное ТВ-приложение разработано специально для устройства и не может быть изменено сторонними приложениями. Подробную информацию об архитектуре TV Input Framework (TIF) и ее компонентах можно найти в статье TV Input Framework.
Как создать сервис ввода ТВ с помощью библиотеки TIF Companion Library
TIF Companion Library – это фреймворк, который предоставляет расширяемые реализации распространенных функций сервиса ввода ТВ. Он предназначен для OEM-производителей, которые создают каналы для Android 5.0 (уровень API 21) – Android 7.1 (уровень API 25).
Обновите проект
Сопутствующая библиотека TIF доступна для использования OEM-производителями в репозитории androidtv-sample-inputs. В этом репозитории вы найдете пример того, как включить библиотеку в приложение.
Объявите сервис ввода ТВ в манифесте
Приложение должно предоставлять совместимый с TvInputService сервис, который система использует для доступа к приложению. В библиотеке TIF Companion Library есть класс BaseTvInputService, который предоставляет реализацию TvInputService по умолчанию. Ее можно настроить. Создайте подкласс BaseTvInputService и объявите его в манифесте как сервис.
В декларации манифеста укажите разрешение BIND_TV_INPUT, чтобы сервис мог подключить ТВ-вход к системе. Привязку выполняет системный сервис, у которого есть разрешение BIND_TV_INPUT.
Системное ТВ-приложение отправляет запросы сервисам ТВ-входа через интерфейс TvInputManager.
В декларацию сервиса включите фильтр интентов, который указывает, что действие TvInputService должно выполняться с интентом. Также объявите метаданные сервиса как отдельный ресурс XML. Пример объявления сервиса, фильтра интентов и метаданных сервиса:
<service android:name=".rich.RichTvInputService" android:label="@string/rich_input_label" android:permission="android.permission.BIND_TV_INPUT"> <!-- Required filter used by the system to launch our account service. --> <intent-filter> <action android:name="android.media.tv.TvInputService" /> </intent-filter> <!-- An XML file which describes this input. This provides pointers to the RichTvInputSetupActivity to the system/TV app. --> <meta-data android:name="android.media.tv.input" android:resource="@xml/richtvinputservice" /> </service>
Определите метаданные сервиса в отдельном XML-файле. XML-файл метаданных сервиса должен включать интерфейс настройки, который описывает начальную конфигурацию ТВ-входа и сканирование каналов. В файле метаданных также должен быть флаг, указывающий, могут ли пользователи записывать контент. Подробнее о том, как включить поддержку записи контента в приложении…
Файл метаданных сервиса находится в каталоге ресурсов XML вашего приложения и должен соответствовать названию ресурса, объявленного в манифесте. Используя записи манифеста из предыдущего примера, вы создадите XML-файл в res/xml/richtvinputservice.xml со следующим содержимым:
<?xml version="1.0" encoding="utf-8"?> <tv-input xmlns:android="http://schemas.android.com/apk/res/android" android:canRecord="true" android:setupActivity="com.example.android.sampletvinput.rich.RichTvInputSetupActivity" />
Определите каналы и создайте действие по настройке
Сервис ввода ТВ должен определять хотя бы один канал, к которому пользователи получают доступ через системное ТВ-приложение. Вам следует зарегистрировать свои каналы в системной базе данных и предоставить операцию настройки, которую система вызывает, когда не может найти канал для вашего приложения.
Сначала разрешите приложению читать и записывать данные в системную программу передач (EPG), которая содержит информацию о доступных пользователю каналах и программах. Чтобы приложение могло выполнять эти действия и синхронизироваться с программой передач после перезапуска устройства, добавьте в манифест приложения следующие элементы:
<uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED "/>
Добавьте следующий элемент, чтобы ваше приложение показывалось в Google Play как приложение, предоставляющее каналы контента в Android TV:
<uses-feature android:name="android.software.live_tv" android:required="true" />
Затем создайте класс, расширяющий класс EpgSyncJobService. Этот абстрактный класс позволяет создать сервис заданий, который создает и обновляет каналы в системной базе данных.
В подклассе создайте и верните полный список каналов в getChannels. Если каналы поступают из файла XMLTV, используйте класс XmlTvParser. В противном случае создайте каналы программно с помощью класса Channel.Builder.
Для каждого канала система вызывает getProgramsForChannel, когда ей нужен список программ, которые можно посмотреть на канале в течение заданного периода времени. Возвращает список объектов Program для канала. Используйте класс XmlTvParser, чтобы получить программы из файла XMLTV, или создайте их программно с помощью класса Program.Builder.
Для каждого объекта Program используйте объект InternalProviderData, чтобы задать информацию о программе, например тип видео. Если вы хотите, чтобы на канале повторялось ограниченное количество программ, используйте метод InternalProviderData.setRepeatable со значением true при настройке информации о программе.
После реализации сервиса заданий добавьте его в манифест приложения:
<service android:name=".sync.SampleJobService" android:permission="android.permission.BIND_JOB_SERVICE" android:exported="true" />
Наконец, создайте действие по настройке. В действии настройки должна быть предусмотрена возможность синхронизации данных о каналах и программах. Один из способов – сделать это через интерфейс в действии. Вы также можете настроить приложение так, чтобы оно делало это автоматически в начале тренировки. Когда приложению нужно синхронизировать информацию о каналах и программах, оно должно запустить сервис заданий:
Kotlin
val inputId = getActivity().intent.getStringExtra(TvInputInfo.EXTRA_INPUT_ID) EpgSyncJobService.cancelAllSyncRequests(getActivity()) EpgSyncJobService.requestImmediateSync( getActivity(), inputId, ComponentName(getActivity(), SampleJobService::class.java) )
Java
String inputId = getActivity().getIntent().getStringExtra(TvInputInfo.EXTRA_INPUT_ID); EpgSyncJobService.cancelAllSyncRequests(getActivity()); EpgSyncJobService.requestImmediateSync(getActivity(), inputId, new ComponentName(getActivity(), SampleJobService.class));
Для синхронизации сервиса заданий используйте метод requestImmediateSync. Пользователю придется ждать окончания синхронизации, поэтому период запроса должен быть относительно коротким.
Используйте метод setUpPeriodicSync, чтобы сервис заданий периодически синхронизировал данные о каналах и программах в фоновом режиме.
Kotlin
EpgSyncJobService.setUpPeriodicSync( context, inputId, ComponentName(context, SampleJobService::class.java) )
Java
EpgSyncJobService.setUpPeriodicSync(context, inputId, new ComponentName(context, SampleJobService.class));
В библиотеке TIF Companion Library есть дополнительный перегруженный метод requestImmediateSync, который позволяет указать продолжительность синхронизации данных канала в миллисекундах. По умолчанию синхронизируются данные за один час.
В библиотеке TIF Companion Library также есть дополнительный перегруженный метод setUpPeriodicSync, который позволяет указать продолжительность синхронизации данных канала и частоту периодической синхронизации. По умолчанию синхронизируются данные за 48 часов каждые 12 часов.
Подробнее о данных каналов и программе передач см. Работа с данными каналов.
Обработка запросов на настройку и воспроизведение медиаконтента
Когда пользователь выбирает определенный канал, системное ТВ-приложение использует Session, созданный вашим приложением, чтобы настроиться на нужный канал и воспроизвести контент. В библиотеке TIF Companion Library есть несколько классов, которые можно расширить для обработки вызовов каналов и сеансов из системы.
Подкласс BaseTvInputService создает сеансы, которые обрабатывают запросы на настройку. Переопределите метод onCreateSession, создайте сеанс, расширенный из класса BaseTvInputService.Session, и вызовите super.sessionCreated с новым сеансом. В примере ниже onCreateSession возвращает объект RichTvInputSessionImpl, который расширяет BaseTvInputService.Session:
Kotlin
override fun onCreateSession(inputId: String): Session = RichTvInputSessionImpl(this, inputId).apply { setOverlayViewEnabled(true) }
Java
@Override public final Session onCreateSession(String inputId) { RichTvInputSessionImpl session = new RichTvInputSessionImpl(this, inputId); session.setOverlayViewEnabled(true); return session; }
Когда пользователь запускает просмотр одного из ваших каналов в системном ТВ-приложении, система вызывает метод onPlayChannel сеанса. Переопределите этот метод, если вам нужно выполнить специальную инициализацию канала перед началом воспроизведения программы.
Затем система получает текущую запланированную программу и вызывает метод onPlayProgram вашего сеанса, указывая информацию о программе и время начала в миллисекундах. Запустите программу в интерфейсе TvPlayer.
В коде проигрывателя должен быть реализован класс TvPlayer, который обрабатывает определенные события воспроизведения. Класс TvPlayer обрабатывает такие функции, как управление сдвигом по времени, не усложняя реализацию BaseTvInputService.
В методе getTvPlayer сеанса верните медиапроигрыватель, который реализует TvPlayer. В примере приложения
TV Input Service реализован медиапроигрыватель, использующий ExoPlayer.
Создайте сервис ТВ-входа с помощью фреймворка TV Input Framework.
Если ваш сервис ТВ-входа не может использовать сопутствующую библиотеку TIF, вам необходимо реализовать следующие компоненты:
TvInputServiceобеспечивает длительную работу и доступность в фоновом режиме для ТВ-входа.TvInputService.Sessionподдерживает состояние ТВ-входа и взаимодействует с приложением, в котором он размещен.TvContractописывает каналы и программы, доступные для ТВ-входа.TvContract.Channels– информация о телеканале.TvContract.Programsописывает телепрограмму с такими данными, как название программы и время начала.TvTrackInfo– аудио- или видеодорожка либо дорожка субтитров.TvContentRating– описывает возрастное ограничение, позволяет использовать собственные схемы возрастных ограничений.TvInputManagerпредоставляет API для системного ТВ-приложения и управляет взаимодействием с источниками ТВ-сигнала и приложениями.
Также необходимо сделать следующее:
- Объявите сервис в манифесте, как описано в разделе Объявление сервиса в манифесте.
- Создайте файл метаданных сервиса.
- Создайте и зарегистрируйте информацию о канале и программе.
- Создайте действие по настройке.
Определите сервис источника ТВ-сигнала
Для своего сервиса вы расширяете класс TvInputService. Реализация TvInputService – это привязанная служба, в которой системная служба является клиентом, привязанным к ней. Методы жизненного цикла сервиса, которые необходимо реализовать, показаны на рисунке 1.
Метод onCreate инициализирует и запускает HandlerThread, который предоставляет поток процесса, отдельный от потока UI, для обработки действий, управляемых системой. В примере ниже метод onCreate инициализирует CaptioningManager и подготавливает обработку действий ACTION_BLOCKED_RATINGS_CHANGED и ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED. Эти действия описывают системные намерения, которые запускаются, когда пользователь меняет настройки родительского контроля и когда меняется список заблокированных возрастных ограничений.
Kotlin
override fun onCreate() { super.onCreate() handlerThread = HandlerThread(javaClass.simpleName).apply { start() } dbHandler = Handler(handlerThread.looper) handler = Handler() captioningManager = getSystemService(Context.CAPTIONING_SERVICE) as CaptioningManager setTheme(android.R.style.Theme_Holo_Light_NoActionBar) sessions = mutableListOf<BaseTvInputSessionImpl>() val intentFilter = IntentFilter().apply { addAction(TvInputManager.ACTION_BLOCKED_RATINGS_CHANGED) addAction(TvInputManager.ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED) } registerReceiver(broadcastReceiver, intentFilter) }
Java
@Override public void onCreate() { super.onCreate(); handlerThread = new HandlerThread(getClass() .getSimpleName()); handlerThread.start(); dbHandler = new Handler(handlerThread.getLooper()); handler = new Handler(); captioningManager = (CaptioningManager) getSystemService(Context.CAPTIONING_SERVICE); setTheme(android.R.style.Theme_Holo_Light_NoActionBar); sessions = new ArrayList<BaseTvInputSessionImpl>(); IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(TvInputManager .ACTION_BLOCKED_RATINGS_CHANGED); intentFilter.addAction(TvInputManager .ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED); registerReceiver(broadcastReceiver, intentFilter); }
Рис. 1. Жизненный цикл TvInputService.
Подробнее о том,
как управлять заблокированным контентом и использовать родительский контроль. Дополнительные действия, управляемые системой, которые вы можете обрабатывать в сервисе ТВ-входа, описаны в TvInputManager.
TvInputService создает TvInputService.Session, который реализует Handler.Callback для обработки изменений состояния проигрывателя. При использовании onSetSurface TvInputService.Session задает Surface с видеоконтентом. Подробнее о том, как использовать Surface для воспроизведения видео, читайте в разделе Интеграция проигрывателя с поверхностью.
TvInputService.Session обрабатывает событие onTune, когда пользователь выбирает канал, и уведомляет системное ТВ-приложение об изменениях в контенте и его метаданных. Эти методы notify описаны в разделах
Управление контентом и Выбор дорожки далее в этом курсе.
Определите действие по настройке
Системное ТВ-приложение работает с действием настройки, которое вы определяете для своего ТВ-входа. Действие по настройке является обязательным и должно предоставлять по крайней мере одну запись канала для системной базы данных. Системное ТВ-приложение вызывает действие настройки, когда не может найти канал для ТВ-входа.
В действии настройки системному ТВ-приложению передаются сведения о каналах, доступных через ТВ-вход. Подробнее об этом рассказывается в следующем уроке – Создание и обновление данных о каналах.