Разработать сервис ТВ-ввода

Служба ТВ-входа представляет собой источник медиапотока и позволяет вам представлять медиаконтент в линейной, широковещательной телевизионной форме в виде каналов и программ. С помощью услуги ТВ-входа вы можете обеспечить родительский контроль, информацию о программе передач и рейтинги контента. Служба ТВ-входа работает с системным приложением Android TV. Это приложение в конечном итоге контролирует и отображает контент канала на телевизоре. Приложение системного ТВ разработано специально для устройства и недоступно для сторонних приложений. Дополнительные сведения об архитектуре TV Input Framework (TIF) и ее компонентах см. в разделе TV Input Framework .

Создайте службу телевизионного ввода с помощью сопутствующей библиотеки TIF.

Сопутствующая библиотека TIF — это платформа, которая обеспечивает расширяемую реализацию общих функций службы телевизионного ввода. Он предназначен для использования OEM-производителями для создания каналов только для Android 5.0 (уровень API 21) по Android 7.1 (уровень API 25).

Обновите свой проект

Сопутствующая библиотека TIF доступна для устаревшего использования OEM-производителями в репозитории androidtv-sample-inputs . См. этот репозиторий, где приведен пример включения библиотеки в приложение.

Объявите службу ТВ-ввода в манифесте.

Ваше приложение должно предоставлять службу, совместимую с TvInputService , которую система использует для доступа к вашему приложению. Сопутствующая библиотека TIF предоставляет класс 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), данные которого включают каналы и программы, доступные пользователю. Чтобы приложение могло выполнять эти действия и синхронизироваться с EPG после перезагрузки устройства, добавьте в манифест приложения следующие элементы:

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

Добавьте следующий элемент, чтобы ваше приложение отображалось в Google Play Store как приложение, предоставляющее каналы контента на 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" />

Наконец, создайте действие настройки. Действия по настройке должны обеспечивать возможность синхронизации данных канала и программы. Один из способов сделать это — предоставить пользователю возможность сделать это через пользовательский интерфейс в действии. Вы также можете настроить приложение на автоматическое выполнение этого действия при запуске активности. Когда в процессе настройки необходимо синхронизировать информацию о канале и программе, приложение должно запустить службу заданий:

Котлин

val inputId = getActivity().intent.getStringExtra(TvInputInfo.EXTRA_INPUT_ID)
EpgSyncJobService.cancelAllSyncRequests(getActivity())
EpgSyncJobService.requestImmediateSync(
        getActivity(),
        inputId,
        ComponentName(getActivity(), SampleJobService::class.java)
)

Ява

String inputId = getActivity().getIntent().getStringExtra(TvInputInfo.EXTRA_INPUT_ID);
EpgSyncJobService.cancelAllSyncRequests(getActivity());
EpgSyncJobService.requestImmediateSync(getActivity(), inputId,
        new ComponentName(getActivity(), SampleJobService.class));

Используйте метод requestImmediateSync() для синхронизации службы заданий. Пользователь должен дождаться завершения синхронизации, поэтому период запроса должен быть относительно коротким.

Используйте метод setUpPeriodicSync() , чтобы служба заданий периодически синхронизировала данные канала и программы в фоновом режиме:

Котлин

EpgSyncJobService.setUpPeriodicSync(
        context,
        inputId,
        ComponentName(context, SampleJobService::class.java)
)

Ява

EpgSyncJobService.setUpPeriodicSync(context, inputId,
        new ComponentName(context, SampleJobService.class));

Сопутствующая библиотека TIF предоставляет дополнительный перегруженный метод requestImmediateSync() , который позволяет указать продолжительность синхронизации данных канала в миллисекундах. Метод по умолчанию синхронизирует данные канала за один час.

Сопутствующая библиотека TIF также предоставляет дополнительный перегруженный метод setUpPeriodicSync() , который позволяет указать продолжительность синхронизации данных канала и частоту периодической синхронизации. Метод по умолчанию синхронизирует 48 часов данных канала каждые 12 часов.

Дополнительные сведения о данных канала и EPG см. в разделе Работа с данными канала .

Обработка запросов на настройку и воспроизведение мультимедиа

Когда пользователь выбирает определенный канал, системное ТВ-приложение использует Session , созданный вашим приложением, для настройки на запрошенный канал и воспроизведения контента. Сопутствующая библиотека TIF предоставляет несколько классов, которые можно расширить для обработки вызовов каналов и сеансов из системы.

Ваш подкласс BaseTvInputService создает сеансы, которые обрабатывают запросы настройки. Переопределите метод onCreateSession() , создайте сеанс, расширенный из класса BaseTvInputService.Session , и вызовите super.sessionCreated() с новым сеансом. В следующем примере onCreateSession() возвращает объект RichTvInputSessionImpl , который расширяет BaseTvInputService.Session :

Котлин

override fun onCreateSession(inputId: String): Session =
        RichTvInputSessionImpl(this, inputId).apply {
            setOverlayViewEnabled(true)
        }

Ява

@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 .

Создайте службу ТВ-ввода, используя структуру ТВ-ввода.

Если ваша служба телевизионного ввода не может использовать сопутствующую библиотеку TIF, вам необходимо реализовать следующие компоненты:

  • TvInputService обеспечивает длительную и фоновую доступность телевизионного входа.
  • TvInputService.Session поддерживает состояние входа телевизора и взаимодействует с приложением хостинга.
  • TvContract описывает каналы и программы, доступные на входе телевизора.
  • TvContract.Channels представляет информацию о телеканале.
  • TvContract.Programs описывает телепрограмму с такими данными, как название программы и время начала.
  • TvTrackInfo представляет собой дорожку аудио, видео или субтитров.
  • TvContentRating описывает рейтинг контента и позволяет использовать собственные схемы рейтинга контента.
  • TvInputManager предоставляет API для системного ТВ-приложения и управляет взаимодействием с ТВ-входами и приложениями.

Вам также необходимо сделать следующее:

  1. Объявите службу телевизионного ввода в манифесте, как описано в разделе «Объявление службы телевизионного ввода в манифесте» .
  2. Создайте файл метаданных службы.
  3. Создайте и зарегистрируйте свой канал и информацию о программе.
  4. Создайте действие по настройке.

Определите службу телевизионного входа

Для вашего сервиса вы расширяете класс TvInputService . Реализация TvInputService — это привязанная служба , где системная служба — это клиент, который привязывается к ней. Методы жизненного цикла сервиса, которые вам необходимо реализовать, показаны на рисунке 1.

Метод onCreate() инициализирует и запускает HandlerThread , который предоставляет поток процесса, отдельный от потока пользовательского интерфейса, для обработки действий, управляемых системой. В следующем примере метод onCreate() инициализирует CaptioningManager и готовится к обработке действий ACTION_BLOCKED_RATINGS_CHANGED и ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED . Эти действия описывают намерения системы, которые активируются, когда пользователь меняет настройки родительского контроля и когда происходит изменение в списке заблокированных оценок.

Котлин

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

Ява

@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 для рендеринга видео см. в разделе Интеграция проигрывателя с Surface .

TvInputService.Session обрабатывает событие onTune() , когда пользователь выбирает канал, и уведомляет системное ТВ-приложение об изменениях в контенте и метаданных контента. Эти методы notify() описаны в разделах «Управление содержимым и управление выбором дорожки» далее в этом обучении.

Определите действия по настройке

Системное ТВ-приложение работает с действиями по настройке, которые вы определяете для своего ТВ-входа. Действие настройки является обязательным и должно предоставить хотя бы одну запись канала для системной базы данных. Системное ТВ-приложение запускает операцию настройки, когда не может найти канал для ТВ-входа.

Действие настройки описывает системному ТВ-приложению каналы, доступные через вход телевизора, как показано в следующем уроке « Создание и обновление данных канала» .

Дополнительные ссылки