Um serviço de entrada de TV representa uma fonte de streaming de mídia e permite que você apresente seu conteúdo de mídia de maneira linear, como canais e programas. Com um serviço de entrada de TV, é possível fornecer controle da família, informações sobre o guia de programação e classificações de conteúdo. O serviço de entrada de TV funciona com o app de TV do sistema Android. Esse app controla e apresenta o conteúdo de canais na TV. O app de TV do sistema foi desenvolvido especificamente para o dispositivo e não pode ser modificado por apps de terceiros. Para saber mais sobre a arquitetura do TV Input Framework (TIF) e os componentes dela, consulte TV Input Framework (link em inglês).
Criar um serviço de entrada de TV usando a TIF Companion Library
A TIF Companion Library é um framework que oferece implementações extensíveis de recursos comuns de serviços de entrada de TV. Ela precisa ser usada por OEMs para criar canais do Android 5.0 (nível 21 da API) ao Android 7.1 (nível 25 da API).
Atualizar seu projeto
A TIF Companion Library está disponível para uso legado por OEMs no repositório androidtv-sample-inputs. Confira nesse repositório um exemplo de como incluir a biblioteca em um app.
Declarar seu serviço de entrada de TV no manifesto
Seu app precisa oferecer um serviço compatível
com TvInputService
que o sistema use para acessar o app. A Biblioteca complementar
TIF fornece a classe BaseTvInputService
, que
fornece uma implementação padrão de TvInputService
que pode ser personalizada. Crie uma subclasse de BaseTvInputService
e declare-a no seu manifesto como um serviço.
Na declaração do manifesto, especifique a
permissão BIND_TV_INPUT
para que o
serviço conecte a entrada de TV ao sistema. Um serviço do sistema
executa a vinculação e tem a
permissão BIND_TV_INPUT
.
O app de TV do sistema envia solicitações aos serviços de entrada de TV
pela interface TvInputManager
.
Na declaração de serviço, inclua um filtro de intent que especifique
TvInputService
como a ação a ser executada com a
intent. Além disso, declare os metadados de serviço com um recurso XML separado. A declaração de serviço, o filtro de intent e a declaração de metadados de serviço são mostrados neste exemplo:
<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>
Defina os metadados de serviço em um arquivo XML separado. O arquivo XML de metadados do serviço precisa incluir uma interface de configuração que descreva a configuração inicial e a busca de canais da entrada de TV. O arquivo de metadados também precisa conter uma sinalização que indique se os usuários podem ou não registrar conteúdo. Para mais informações sobre como oferecer suporte à gravação de conteúdo no seu app, consulte Suporte à gravação de conteúdo.
O arquivo de metadados do serviço está localizado no diretório de recursos XML
do seu app e precisa corresponder ao nome do recurso que você declarou no
manifesto. Usando as entradas de manifesto do exemplo anterior, você criaria
o arquivo XML em res/xml/richtvinputservice.xml
, com o
seguinte conteúdo:
<?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" />
Definir canais e criar atividades de configuração
O serviço de entrada de TV precisa definir pelo menos um canal que os usuários acessem pelo app de TV do sistema. Registre seus canais no banco de dados do sistema e forneça uma atividade de configuração que o sistema invoca quando não consegue encontrar um canal para o app.
Primeiro, permita que o app leia e grave no Guia Eletrônico de programação (EPG, na sigla em inglês) do sistema, cujos dados incluem canais e programas disponíveis para o usuário. Para permitir que seu app execute essas ações e sincronize com o EPG após a reinicialização do dispositivo, adicione os seguintes elementos ao manifesto do app:
<uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED "/>
Adicione o elemento abaixo para garantir que seu app apareça na Google Play Store como um app que oferece canais de conteúdo no Android TV:
<uses-feature android:name="android.software.live_tv" android:required="true" />
Em seguida, crie uma classe que estenda a
classe EpgSyncJobService
. Essa classe abstrata facilita a criação de um serviço de job que
cria e atualiza canais no banco de dados do sistema.
Na subclasse, crie e retorne sua lista completa de canais em
getChannels()
. Caso seus canais venham de um arquivo XMLTV,
use a classe XmlTvParser
. Caso contrário, gere
canais de forma programática usando a classe Channel.Builder
.
Para cada canal, o sistema chama getProgramsForChannel()
quando precisa de uma lista de programas que podem ser visualizados em uma determinada janela de tempo
no canal. Retorne uma lista de objetos Program
para o
canal. Use a classe XmlTvParser
para extrair programas de um
arquivo XMLTV ou gere-os de forma programática usando a
classe Program.Builder
.
Para cada objeto Program
, use um objeto
InternalProviderData
para definir informações do programa, como o
tipo de vídeo dele. Se você tiver apenas um número limitado de programas que
você quer que o canal repita em loop, use o método
InternalProviderData.setRepeatable()
com o valor de
true
ao definir informações sobre seu programa.
Depois de implementar o serviço de tarefas, adicione-o ao manifesto do seu app:
<service android:name=".sync.SampleJobService" android:permission="android.permission.BIND_JOB_SERVICE" android:exported="true" />
Para terminar, crie uma atividade de configuração. Sua atividade de configuração precisa oferecer uma maneira de sincronizar os dados dos canais e programas. Uma maneira de fazer isso é o usuário fazer isso usando a IU na atividade. O app também pode fazer isso automaticamente quando a atividade começar. Quando a atividade de configuração precisa sincronizar informações do canal e do programa, o app precisa iniciar o serviço de tarefas:
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));
Use o método requestImmediateSync()
para sincronizar o serviço de tarefas. O usuário precisa esperar a sincronização terminar. Portanto, mantenha o período de solicitação relativamente curto.
Use o método setUpPeriodicSync()
para que o serviço de tarefas
sincronize periodicamente os dados dos canais e programas em segundo plano:
Kotlin
EpgSyncJobService.setUpPeriodicSync( context, inputId, ComponentName(context, SampleJobService::class.java) )
Java
EpgSyncJobService.setUpPeriodicSync(context, inputId, new ComponentName(context, SampleJobService.class));
A TIF Companion Library oferece outro método sobrecarregado de
requestImmediateSync()
, que permite especificar a duração da
sincronização dos dados do canal em milissegundos. O método padrão sincroniza o equivalente a uma hora de dados de canais.
A biblioteca TIF Companion também oferece outro método sobrecarregado de
setUpPeriodicSync()
, que permite especificar a duração da
sincronização dos dados do canal e a frequência com que a sincronização periódica vai ocorrer. O
método padrão sincroniza 48 horas de dados de canais a cada 12 horas.
Para ver mais detalhes sobre dados de canais e o EPG, consulte Trabalhar com dados de canais.
Processar solicitações de sintonização e reprodução de mídia
Quando um usuário seleciona um canal específico, o app de TV do sistema usa um
Session
, criado pelo app, para sintonizar o canal solicitado
e abrir conteúdo. A TIF Companion Library oferece várias
classes que você pode estender para lidar com chamadas de canais e sessões do sistema.
A subclasse BaseTvInputService
cria sessões que processam
solicitações de ajuste. Modifique o
método onCreateSession()
, crie uma sessão estendida da
classe BaseTvInputService.Session
e chame
super.sessionCreated()
com a nova sessão. No exemplo
a seguir, onCreateSession()
retorna um
objeto RichTvInputSessionImpl
que estende
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; }
Quando o usuário usa o app de TV do sistema para começar a visualizar um dos seus canais,
o sistema chama o método onPlayChannel()
da sessão. Modifique esse método se você precisar fazer qualquer inicialização de canal especial antes que o programa comece a ser reproduzido.
Em seguida, o sistema recebe o programa que está agendado e chama o método onPlayProgram()
da sua sessão, especificando as informações do programa e o horário de início em milissegundos. Use a
interface TvPlayer
para começar a reproduzir o programa.
O código do seu player de mídia precisa implementar TvPlayer
para processar
eventos de reprodução específicos. A classe TvPlayer
processa recursos
como controles de time-shifting sem aumentar a complexidade da sua
implementação de BaseTvInputService
.
No método getTvPlayer()
da sua sessão, retorne
o player de mídia que implementa TvPlayer
. O app de exemplo
Serviço de entrada de TV (link em inglês) implementa um player de mídia que usa o
ExoPlayer.
Criar um serviço de entrada de TV usando o framework de entrada de TV
Se o serviço de entrada de TV não puder usar a TIF Companion Library, você precisará implementar os seguintes componentes:
TvInputService
oferece disponibilidade de longa duração e em segundo plano para a entrada de TV.TvInputService.Session
mantém o estado da entrada de TV e se comunica com o app host.TvContract
descreve os canais e programas disponíveis para a entrada de TVTvContract.Channels
representa informações sobre um canal de TV.TvContract.Programs
descreve um programa de TV com dados como título do programa e horário de início.TvTrackInfo
representa uma faixa de áudio, vídeo ou legendas.- O
TvContentRating
descreve uma classificação de conteúdo e permite esquemas personalizados de classificações de conteúdo. TvInputManager
fornece uma API para o app de TV do sistema e gerencia a interação com entradas e apps de TV.
Você também precisa fazer o seguinte:
- Declare o serviço de entrada de TV no manifesto, conforme descrito em Declarar o serviço de entrada de TV no manifesto.
- Crie o arquivo de metadados do serviço.
- Crie e registre suas informações de canais e programas.
- Crie suas atividades de configuração.
Definir o serviço de entrada de TV
Estenda a classe TvInputService
para seu serviço. Uma
implementação de TvInputService
é um
serviço vinculado em que o serviço do sistema
é o cliente que se vincula a ele. Os métodos de ciclo de vida do serviço
que você precisa implementar estão ilustrados na Figura 1.
O método onCreate()
inicializa e inicia a
HandlerThread
, que fornece uma linha de execução do processo separada da linha de execução de IU para
processar ações do sistema. No exemplo a seguir, o método onCreate()
inicializa o CaptioningManager
e se prepara para processar
as ações ACTION_BLOCKED_RATINGS_CHANGED
e ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED
. Essas
ações descrevem as intents do sistema disparadas quando o usuário muda as configurações do controle da família e quando
há uma mudança na lista de classificações bloqueadas.
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); }
![](https://developer.android.com/static/images/tv/tvinput-life.png?authuser=5&hl=pt)
Figura 1. Ciclo de vida da classe TvInputService.
Consulte
Controlar conteúdo para mais informações sobre como trabalhar com conteúdo bloqueado e oferecer
controle dos pais. Consulte TvInputManager
para conhecer mais ações do sistema que
você pode querer processar no seu serviço de entrada de TV.
O TvInputService
cria um
TvInputService.Session
que implementa Handler.Callback
para processar as mudanças de estado do player. Com
onSetSurface()
,
o TvInputService.Session
define Surface
com o
conteúdo de vídeo. Consulte Integrar o player com a superfície
para ver mais informações sobre como trabalhar com Surface
para renderizar vídeos.
O TvInputService.Session
processa o evento
onTune()
quando o usuário seleciona um canal e notifica o app de TV do sistema sobre mudanças no conteúdo e
nos metadados de conteúdo. Esses métodos notify()
são descritos em
Controlar conteúdo e Processar a seleção de faixas
mais adiante neste treinamento.
Definir atividades de configuração
O app de TV do sistema trabalha com as atividades de configuração definidas para sua entrada de TV. A atividade de configuração é necessária e precisa fornecer pelo menos um registro de canal para o banco de dados do sistema. O app de TV do sistema invoca a atividade de configuração quando não consegue encontrar um canal para a entrada de TV.
A atividade de configuração descreve ao app de TV do sistema os canais disponibilizados pela entrada de TV, conforme demonstrado na próxima lição, Criar e atualizar dados de canais.
Outras referências
- android.media.tv (link em inglês)
- TV Input Framework