Desenvolver um serviço de entrada para TV

Um serviço de entrada de TV representa uma fonte de stream de mídia e permite apresentar seu conteúdo de mídia em um de TV com transmissão linear, como canais e programas. Com um serviço de entrada para TV, é possível fornecer controle dos pais, informações sobre o guia da programação e classificações do conteúdo. O serviço de entrada de TV funciona com o app de TV do sistema Android. Este aplicativo controla e apresenta o conteúdo do canal na TV. O app de TV do sistema foi desenvolvido especificamente para dispositivos e não pode ser modificado por apps de terceiros. Para mais informações sobre o 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 de recursos comuns do serviço de entrada de TV. Ele foi feito para ser usado por OEMs na criação Canais somente para Android 5.0 (API de nível 21) ao Android 7.1 (API de nível 25).

Atualizar seu projeto

A TIF Companion Library está disponível para uso legado por OEMs no androidtv-sample-inputs (em inglês) repositório de dados. Consulte esse repositório para conferir um exemplo de como incluir a biblioteca em um app.

Declarar seu serviço de entrada de TV no manifesto

Seu app precisa fornecer um endereço de e-mail compatível com TvInputService serviço que o sistema usa para acessar seu aplicativo. TIF A biblioteca complementar oferece a classe BaseTvInputService, que fornece uma implementação padrão de TvInputService que você pode personalizar. Crie uma subclasse de BaseTvInputService. e declarar a subclasse no manifesto como um serviço.

Na declaração do manifesto, especifique BIND_TV_INPUT para permitir que o para conectar a entrada da TV ao sistema. Um serviço do sistema executa a vinculaçã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 do serviço, inclua um filtro de intent que especifique TvInputService como a ação a ser realizada com o intenção. Além disso, declare os metadados de serviço com um recurso XML separado. A declaração de serviço, filtro de intent e 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 serviço O arquivo XML de metadados deve incluir uma interface de configuração que descreva a entrada da TV configuração inicial e busca de canais. O arquivo de metadados também deve conter um que indica se os usuários podem ou não gravar o 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 de serviço está localizado no diretório de recursos XML. para seu app e precisa corresponder ao nome do recurso declarado na manifesto do aplicativo. Usando as entradas de manifesto do exemplo anterior, você usaria crie o arquivo XML em res/xml/richtvinputservice.xml, com o 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 pelo app de TV do sistema. Você deve registrar seus canais no banco de dados do sistema e fornecem uma atividade de configuração invoca quando não encontra um canal para seu app.

Primeiro, permita que seu app leia e grave no sistema Guia de programação (EPG), cujos dados incluem canais e programas disponíveis para o usuário. Para permitir que seu aplicativo realize 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 seguinte elemento para garantir que seu aplicativo 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 o EpgSyncJobService . Essa classe abstrata facilita a criação de um serviço de vagas que Cria e atualiza canais no banco de dados do sistema.

Na subclasse, crie e retorne sua lista completa de canais em getChannels(): Se os canais vêm 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 acessar programas de uma XMLTV ou gerá-los de forma programática usando a Program.Builder.

Para cada objeto Program, use uma Objeto InternalProviderData para definir informações do programa, como o tipo de vídeo do programa. Se você tem um número limitado de programas quiser que o canal se repita em loop, use o Método InternalProviderData.setRepeatable() com um valor de true ao definir informações sobre o 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 deve fornecer uma maneira para sincronizar dados de canais e programas. Uma maneira de fazer isso é que o usuário faça isso por meio da interface na atividade. O app também pode fazer isso automaticamente quando a atividade iniciar. Quando a atividade de configuração precisar sincronizar os canais e informações do programa, o app deve 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));

Usar o método requestImmediateSync() para sincronizar serviço de trabalho. Como o usuário precisa aguardar o término da sincronização, você deve manter o período de solicitação relativamente curto.

Use o método setUpPeriodicSync() para que o serviço de jobs sincronize periodicamente os dados dos canais e dos 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 fornece um método adicional sobrecarregado de requestImmediateSync(), que permite especificar a duração dados de canais sejam sincronizados em milissegundos. O método padrão sincroniza em dados de canais.

A TIF Companion Library também fornece um método adicional sobrecarregado de setUpPeriodicSync(), que permite especificar a duração dados de canais a serem sincronizados e com que frequência a sincronização periódica deve ocorrer. A O método padrão sincroniza 48 horas de dados de canais a cada 12 horas.

Para 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, criado pelo seu app, para sintonizar o canal solicitado e reproduzir conteúdo. A TIF Companion Library oferece vários que podem ser estendidas para lidar com chamadas de canal e sessão do sistema.

A subclasse BaseTvInputService cria sessões que processam solicitações de ajuste. Substitua o onCreateSession(), crie uma sessão estendida de a classe BaseTvInputService.Session e chame super.sessionCreated() pela nova sessão. Nos seguintes exemplo, 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 chamará o método onPlayChannel() da sessão. Substituir este método se for necessário fazer qualquer inicialização de canal especial antes que o começar a reproduzir.

O sistema recebe o programa que está agendado e chama seu método onPlayProgram() da sessão, especificando o programa e o horário de início em milissegundos. Use o TvPlayer para começar a jogar 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 BaseTvInputService.

No método getTvPlayer() da sua sessão, retorne seu player de mídia que implemente TvPlayer. A O app de exemplo Serviço de entrada de TV implementa um player de mídia que usa ExoPlayer (links em inglês).

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á para implementar os seguintes componentes:

  • O TvInputService oferece disponibilidade de longa duração e em segundo plano para a entrada da TV
  • O TvInputService.Session mantém o estado da entrada de TV e se comunica com o app de hospedagem
  • TvContract descreve os canais e programas disponíveis para a TV entrada
  • TvContract.Channels representa informações sobre um canal de TV.
  • TvContract.Programs descreve um programa de TV com dados como programa título e horário de início
  • TvTrackInfo representa uma faixa de áudio, vídeo ou legendas.
  • TvContentRating descreve uma classificação do conteúdo e permite conteúdo personalizado esquemas de classificação
  • 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:

  1. Declare o serviço de entrada de TV no manifesto como descrito em Declarar seu serviço de entrada de TV no manifesto do app.
  2. Crie o arquivo de metadados do serviço.
  3. Crie e registre suas informações de canais e programas.
  4. Crie suas atividades de configuração.

Definir o serviço de entrada de TV

Estenda a classe TvInputService para seu serviço. Um A implementação de TvInputService é uma serviço vinculado em que o serviço do sistema é o cliente que é vinculado a ela. Os métodos do ciclo de vida do serviço que precisam ser implementados estão ilustrados na figura 1.

O método onCreate() inicializa e inicia a HandlerThread, que fornece uma linha de execução de processo separada da linha de execução de IU para e lidar com ações do sistema. No exemplo abaixo, o onCreate() inicializa o CaptioningManager e se prepara para processar a ACTION_BLOCKED_RATINGS_CHANGED e ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED. Esses as ações descrevem intents do sistema disparadas quando o usuário altera as configurações de controle dos pais e quando houver 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);
}

Figura 1. Ciclo de vida da classe TvInputService.

Consulte Controle de conteúdo para saber mais sobre como trabalhar com conteúdo bloqueado e fornecer o controle da família. Acesse TvInputManager para conferir mais ações do sistema que que você queira processar no serviço de entrada de TV.

O TvInputService cria um TvInputService.Session que implementa Handler.Callback para lidar com alterações no estado do player. Com onSetSurface(), o TvInputService.Session define a Surface com o conteúdo em vídeo. Consulte Integrar o player à plataforma para mais informações sobre como trabalhar com Surface para renderizar vídeos.

O TvInputService.Session processa onTune() quando o usuário seleciona um canal e notifica o aplicativo de TV do sistema sobre alterações no conteúdo e metadados de conteúdo. Esses métodos notify() são descritos em "Controlar conteúdo" e Gerenciar a seleção de músicas 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 a atividade de configuração é necessária e deve fornecer pelo menos um registro de canal para o banco de dados do sistema. A o app de TV do sistema invoca a atividade de configuração quando não encontra um canal para a entrada da TV.

A atividade de configuração descreve para o app de TV do sistema os canais disponibilizados pela TV entrada, como demonstrado na próxima lição, Criar e atualizar os dados do canal.

Outras referências