Um serviço de entrada de TV representa uma fonte de transmissão de mídia e permite a apresentação de conteúdo de mídia no formato de TV com transmissão linear, com canais e programas. Com um serviço de entrada de TV, você pode oferecer controle dos pais, 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 dispositivos e não pode ser mudado por apps de terceiros. Para saber mais sobre o TV Input Framework (TIF) e seus componentes, 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 fornece implementações extensíveis de recursos comuns de serviços de entrada de TV. Use a TIF Companion Library para criar seu próprio serviço de entrada de TV com rapidez e facilidade, seguindo as práticas recomendadas para o Android TV.
Atualizar seu projeto
Para começar a usar a TIF Companion Library, adicione o seguinte ao arquivo build.gradle
do seu app:
compile 'com.google.android.libraries.tv:companionlibrary:0.2'
No momento, a TIF Companion Library não faz parte do framework do Android. Ela é distribuída como uma dependência do Gradle por meio do jcenter, não com o SDK do Android. No jcenter, você pode encontrar a versão mais recente da tif-companion library (link em inglês).
Declarar seu serviço de entrada de TV no manifesto
Seu app precisa fornecer um serviço compatível com a classe TvInputService
que o sistema usa para acessá-lo. A TIF Companion Library fornece a classe BaseTvInputService
, que oferece 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 possa conectar 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 por meio da 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 no exemplo a seguir:
<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 de serviço precisa incluir uma interface de configuração que descreva a configuração inicial e a procura de canais da entrada de TV. O arquivo de metadados também precisa ter uma sinalização que indique se os usuários podem ou não gravar conteúdo. Para saber mais sobre como oferecer compatibilidade com gravação de conteúdo no seu app, consulte Gravação de TV.
O arquivo de metadados de serviço fica no diretório de recursos XML do seu app e precisa ter o mesmo nome do recurso que você declarou no manifesto. Usando as entradas do 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
Seu serviço de entrada de TV precisa definir pelo menos um canal que os usuários acessem pelo app de TV do sistema. Você precisa registrar seus canais no banco de dados do sistema e fornecer uma atividade de configuração para o sistema invocar quando não encontrar um canal para seu app.
Primeiro, permita que seu app leia e grave no Guia eletrônico de programação (EPG, na sigla em inglês) do sistema, cujos dados incluem os canais e programas disponíveis para o usuário. Para permitir que seu app realize essas ações e seja sincronizado 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 seguinte elemento 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 EpgSyncJobService
. Essa classe abstrata facilita a criação de um serviço de tarefas que cria e atualiza canais no banco de dados do sistema.
Na sua 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 janela de tempo específica no canal. Retorne uma lista de objetos Program
para o canal. Use a classe XmlTvParser
para receber 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 um número limitado de programas que você quer que o canal repita em loop, use o método InternalProviderData.setRepeatable()
com valor true
ao configurar as 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 fornecer uma forma de sincronizar os dados dos canais e programas. O usuário pode fazer isso por meio da IU na atividade. Você também pode programar para que o app faça isso automaticamente quando a atividade começar. Quando a atividade de configuração precisar sincronizar as informações dos canais e programas, será necessário que o app inicie 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. Como o usuário precisa esperar a sincronização terminar, mantenha o período de solicitação relativamente curto.
Use o método setUpPeriodicSync()
para fazer com que o serviço de tarefas sincronize os dados dos canais e programas periodicamente 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 de canais em milissegundos. O método padrão sincroniza o equivalente a uma hora de dados de canais.
A TIF Companion Library também fornece outro método sobrecarregado de setUpPeriodicSync()
, que permite especificar a duração da sincronização dos dados de canais e a frequência com que a sincronização periódica 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 uma Session
, criada pelo app, para sintonizar o canal solicitado e abrir conteúdo. A TIF Companion Library oferece várias classes que você pode estender para processar chamadas de canais e sessões no sistema.
A subclasse BaseTvInputService
cria sessões que processam solicitações de sintonização. Substitua 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 ver um dos seus canais, o sistema chama o método onPlayChannel()
da sua sessão. Modifique esse método se precisar executar qualquer inicialização de canal especial antes de o programa começar a ser reproduzido.
O sistema, então, 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 mostrar o programa.
O código do seu player de mídia precisa implementar TvPlayer
para processar eventos específicos de reprodução. 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 amostra TV Input Service (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 seu 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 TV.TvContract.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.TvContentRating
descreve uma classificação de conteúdo e permite esquemas personalizados de classificação.TvInputManager
fornece uma API ao app de TV do sistema e gerencia a interação com entradas e apps de TV.
Você também precisa fazer o seguinte:
- Declare seu 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 de sistema é o cliente vinculado a ele. A figura 1 mostra os métodos de ciclo de vida do serviço que você precisa implementar.
O método onCreate()
é inicializado 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 intents do sistema disparadas quando o usuário altera as configurações de controle dos pais e quando há uma alteração 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); }

Figura 1. Ciclo de vida da classe TvInputService.
Consulte Controlar conteúdo para ver 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 processar no seu serviço de entrada de TV.
A TvInputService
cria uma TvInputService.Session
que implementa Handler.Callback
para processar as mudanças de estado do player. Com onSetSurface()
, a TvInputService.Session
define a Surface
com o conteúdo de vídeo. Consulte também Integrar o player à superfície para saber mais sobre como trabalhar com Surface
para renderizar vídeos.
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 do conteúdo. Esses métodos notify()
estão descritos nas seções Controlar conteúdo e Processar seleção de faixas deste 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. As atividades de configuração são obrigatórias e precisam fornecer pelo menos um registro de canal para o banco de dados do sistema. O app de TV do sistema invoca as atividades de configuração quando não encontra um canal para a entrada de TV.
As atividades de configuração descrevem para o app de TV do sistema os canais disponibilizados por meio da entrada de TV, conforme demonstrado na próxima lição, Criar e atualizar dados de canais.