Sua entrada de TV precisa fornecer dados ao Guia de programação eletrônico (EPG, na sigla em inglês) por pelo menos um canal na atividade de configuração. Você também deve atualizar periodicamente os dados, considerando o tamanho da atualização e a linha de execução que cuida disso. Além disso, você pode fornecer links do app para canais. que direcionam o usuário para conteúdos e atividades relacionados. Esta lição aborda a criação e a atualização de dados de canais e programas no banco de dados do sistema com essas considerações em mente.
Experimente o App de exemplo do serviço de entrada de TV (link em inglês).
Conseguir permissão
Para que sua entrada de TV funcione com dados de EPG, ela precisa declarar o gravação no arquivo de manifesto do Android da seguinte forma:
<uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />
Registrar canais no banco de dados
O banco de dados do sistema da Android TV mantém registros de dados de canais para entradas de TV. Na sua configuração
atividade, para cada um de seus canais, você deve mapear seus dados de canal para os seguintes campos da
Classe TvContract.Channels
:
COLUMN_DISPLAY_NAME
: o nome exibido do canalCOLUMN_DISPLAY_NUMBER
: o canal exibido númeroCOLUMN_INPUT_ID
: ID do serviço de entrada de TVCOLUMN_SERVICE_TYPE
: tipo de serviço do canalCOLUMN_TYPE
: padrão de transmissão do canal tipoCOLUMN_VIDEO_FORMAT
: o formato de vídeo padrão. para o canal
Embora o framework de entrada de TV seja genérico o suficiente para lidar com a transmissão tradicional e conteúdo over-the-top (OTT) sem distinção, convém definir as seguintes colunas em além dos acima para identificar melhor os canais de transmissão tradicionais:
COLUMN_ORIGINAL_NETWORK_ID
: a televisão ID da redeCOLUMN_SERVICE_ID
: ID do serviçoCOLUMN_TRANSPORT_STREAM_ID
: stream de transporte ID
Se você quiser fornecer detalhes do link do app para seus canais, vai precisar atualizar alguns campos adicionais. Para mais informações sobre os campos de links de apps, consulte Adicionar informações sobre o link do app
Para entradas de TV baseadas em streaming de Internet, atribua seus próprios valores ao código acima para que e cada canal pode ser identificado de forma exclusiva.
Extraia os metadados do seu canal (em XML, JSON ou qualquer outro) do servidor de back-end e na configuração A atividade mapeia os valores para o banco de dados do sistema da seguinte forma:
Kotlin
val values = ContentValues().apply { put(TvContract.Channels.COLUMN_DISPLAY_NUMBER, channel.number) put(TvContract.Channels.COLUMN_DISPLAY_NAME, channel.name) put(TvContract.Channels.COLUMN_ORIGINAL_NETWORK_ID, channel.originalNetworkId) put(TvContract.Channels.COLUMN_TRANSPORT_STREAM_ID, channel.transportStreamId) put(TvContract.Channels.COLUMN_SERVICE_ID, channel.serviceId) put(TvContract.Channels.COLUMN_VIDEO_FORMAT, channel.videoFormat) } val uri = context.contentResolver.insert(TvContract.Channels.CONTENT_URI, values)
Java
ContentValues values = new ContentValues(); values.put(Channels.COLUMN_DISPLAY_NUMBER, channel.number); values.put(Channels.COLUMN_DISPLAY_NAME, channel.name); values.put(Channels.COLUMN_ORIGINAL_NETWORK_ID, channel.originalNetworkId); values.put(Channels.COLUMN_TRANSPORT_STREAM_ID, channel.transportStreamId); values.put(Channels.COLUMN_SERVICE_ID, channel.serviceId); values.put(Channels.COLUMN_VIDEO_FORMAT, channel.videoFormat); Uri uri = context.getContentResolver().insert(TvContract.Channels.CONTENT_URI, values);
No exemplo acima, channel
é um objeto que contém metadados de canal do
servidor de back-end.
Apresentar informações de canais e programas
O app de TV do sistema apresenta informações de canais e programas aos usuários conforme eles navegam pelos canais. como mostrado na figura 1. Para garantir que as informações do canal e do programa funcionem com o app de TV do sistema apresentador de informações do canal e do programa, siga as orientações abaixo.
- Número do canal (
COLUMN_DISPLAY_NUMBER
) - Ícone
(
android:icon
no manifesto da entrada de TV) - Descrição do programa (
COLUMN_SHORT_DESCRIPTION
) - Título do programa (
COLUMN_TITLE
) - Logotipo do canal (
TvContract.Channels.Logo
)- Use a cor #EEEEEE para corresponder ao texto ao redor.
- Não inclua padding.
- Arte do pôster (
COLUMN_POSTER_ART_URI
)- Use uma proporção entre 16:9 e 4:3.
![](https://developer.android.google.cn/static/images/tv/channel-info.png?authuser=9&hl=pt)
Figura 1. Apresentador de informações de canais e programas do app de TV do sistema.
O app de TV do sistema fornece as mesmas informações no guia da programação, incluindo a arte do pôster, como mostrado na figura 2.
![](https://developer.android.google.cn/static/images/tv/prog-guide.png?authuser=9&hl=pt)
Figura 2. Guia da programação do app de TV do sistema.
Atualizar dados de canais
Ao atualizar dados de canais existentes, use o
update()
em vez de excluir e adicionar novamente os dados. Você pode identificar a versão atual dos dados
usando Channels.COLUMN_VERSION_NUMBER
e Programs.COLUMN_VERSION_NUMBER
ao escolher os registros a serem atualizados.
Observação:adicionar dados de canais ao ContentProvider
pode levar algum tempo. Adicionar programas atuais (aqueles dentro do intervalo de duas horas do horário atual)
somente quando você configura seu EpgSyncJobService
para atualizar o restante
dos dados do canal em segundo plano. Consulte
o
App de exemplo de TV ao vivo do Android TV (link em inglês).
Carregar dados de canais em lote
Ao atualizar o banco de dados do sistema com uma grande quantidade de dados de canais, use o ContentResolver
applyBatch()
ou
bulkInsert()
. Veja um exemplo com applyBatch()
:
Kotlin
val ops = ArrayList<ContentProviderOperation>() val programsCount = channelInfo.mPrograms.size channelInfo.mPrograms.forEachIndexed { index, program -> ops += ContentProviderOperation.newInsert( TvContract.Programs.CONTENT_URI).run { withValues(programs[index]) withValue(TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS, programStartSec * 1000) withValue( TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS, (programStartSec + program.durationSec) * 1000 ) build() } programStartSec += program.durationSec if (index % 100 == 99 || index == programsCount - 1) { try { contentResolver.applyBatch(TvContract.AUTHORITY, ops) } catch (e: RemoteException) { Log.e(TAG, "Failed to insert programs.", e) return } catch (e: OperationApplicationException) { Log.e(TAG, "Failed to insert programs.", e) return } ops.clear() } }
Java
ArrayList<ContentProviderOperation> ops = new ArrayList<>(); int programsCount = channelInfo.mPrograms.size(); for (int j = 0; j < programsCount; ++j) { ProgramInfo program = channelInfo.mPrograms.get(j); ops.add(ContentProviderOperation.newInsert( TvContract.Programs.CONTENT_URI) .withValues(programs.get(j)) .withValue(Programs.COLUMN_START_TIME_UTC_MILLIS, programStartSec * 1000) .withValue(Programs.COLUMN_END_TIME_UTC_MILLIS, (programStartSec + program.durationSec) * 1000) .build()); programStartSec = programStartSec + program.durationSec; if (j % 100 == 99 || j == programsCount - 1) { try { getContentResolver().applyBatch(TvContract.AUTHORITY, ops); } catch (RemoteException | OperationApplicationException e) { Log.e(TAG, "Failed to insert programs.", e); return; } ops.clear(); } }
Processar dados de canais de forma assíncrona
A manipulação de dados, como buscar um stream do servidor ou acessar o banco de dados, deve
não bloquear a linha de execução de IU. Usar um AsyncTask
é um
de realizar atualizações de forma assíncrona. Por exemplo, ao carregar informações de canal de um servidor de back-end,
use AsyncTask
desta maneira:
Kotlin
private class LoadTvInputTask(val context: Context) : AsyncTask<Uri, Unit, Unit>() { override fun doInBackground(vararg uris: Uri) { try { fetchUri(uris[0]) } catch (e: IOException) { Log.d("LoadTvInputTask", "fetchUri error") } } @Throws(IOException::class) private fun fetchUri(videoUri: Uri) { context.contentResolver.openInputStream(videoUri).use { inputStream -> Xml.newPullParser().also { parser -> try { parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false) parser.setInput(inputStream, null) sTvInput = ChannelXMLParser.parseTvInput(parser) sSampleChannels = ChannelXMLParser.parseChannelXML(parser) } catch (e: XmlPullParserException) { e.printStackTrace() } } } } }
Java
private static class LoadTvInputTask extends AsyncTask<Uri, Void, Void> { private Context mContext; public LoadTvInputTask(Context context) { mContext = context; } @Override protected Void doInBackground(Uri... uris) { try { fetchUri(uris[0]); } catch (IOException e) { Log.d("LoadTvInputTask", "fetchUri error"); } return null; } private void fetchUri(Uri videoUri) throws IOException { InputStream inputStream = null; try { inputStream = mContext.getContentResolver().openInputStream(videoUri); XmlPullParser parser = Xml.newPullParser(); try { parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false); parser.setInput(inputStream, null); sTvInput = ChannelXMLParser.parseTvInput(parser); sSampleChannels = ChannelXMLParser.parseChannelXML(parser); } catch (XmlPullParserException e) { e.printStackTrace(); } } finally { if (inputStream != null) { inputStream.close(); } } } }
Se você precisar atualizar os dados do EPG regularmente, considere usar
WorkManager
para executar o processo de atualização durante o tempo inativo, por exemplo, todos os dias às 3h.
Outras técnicas para separar as tarefas de atualização de dados da linha de execução de IU incluem o uso do
Classe HandlerThread
ou você pode implementar a sua própria usando Looper
e Handler
. Consulte
Processos e linhas de execução para mais informações.
Adicionar informações de links de apps
Os canais podem usar links de apps para permitir que os usuários iniciem facilmente um anúncio relacionado atividade enquanto assistem ao conteúdo do canal. Os apps do canal usam links do app para aumentar o engajamento do usuário iniciando atividades que mostram informações relacionadas ou conteúdo adicional. Por exemplo, você pode usar links de apps faça o seguinte:
- Orientar o usuário para encontrar e adquirir conteúdo relacionado.
- Oferecer mais informações sobre o conteúdo em exibição no momento.
- Enquanto assiste episódios, comece a assistir o próximo episódio de uma Google Workspace.
- Permitir que o usuário interaja com o conteúdo. Por exemplo, avaliar ou comentar conteúdo sem interromper a reprodução.
Links de apps aparecem quando o usuário pressiona Select para mostrar o Menu da TV enquanto assiste o conteúdo do canal.
![](https://developer.android.google.cn/static/images/training/tv/tif/app-link.png?authuser=9&hl=pt)
Figura 1. Um exemplo de link de app exibido na linha Canais enquanto o conteúdo do canal é mostrado.
Quando o usuário seleciona o link do aplicativo, o sistema inicia uma atividade usando um URI de intent especificado pelo app do canal. O conteúdo do canal continua tocando enquanto a atividade do link de app estiver ativa. O usuário pode retornar ao canal conteúdo pressionando Voltar.
Fornecer dados de canal do link do app
O Android TV cria automaticamente um link do app para cada canal,
usando informações dos dados do canal. Para fornecer informações sobre links de apps,
especifique os seguintes detalhes em seu
Campos TvContract.Channels
:
COLUMN_APP_LINK_COLOR
: o cor de destaque do link de app desse canal. Por exemplo, a cor de destaque, veja a figura 2, destaque 3.COLUMN_APP_LINK_ICON_URI
– O URI do ícone do selo do app do link do app desse canal. Para um exemplo de ícone de selo de app, consulte a figura 2, destaque 2.COLUMN_APP_LINK_INTENT_URI
– O URI de intent do link de app para esse canal. É possível criar o URI usandotoUri(int)
comURI_INTENT_SCHEME
e converta o URI de volta à intent original comparseUri()
COLUMN_APP_LINK_POSTER_ART_URI
- O URI da arte do pôster usada como plano de fundo do link do app para este canal. Para ver um exemplo de imagem de pôster, consulte a figura 2, destaque 1.COLUMN_APP_LINK_TEXT
– O texto descritivo do link do app para este canal. Para obter um exemplo descrição do link do app, consulte o texto na figura 2, frase de destaque 3.
![](https://developer.android.google.cn/static/images/training/tv/tif/app-link-diagram.png?authuser=9&hl=pt)
Figura 2. Detalhes do link de app.
Se os dados do canal não especificarem informações do link do app, o sistema cria um link de app padrão. O sistema escolhe os detalhes padrão da seguinte maneira:
- Para o URI da intent
(
COLUMN_APP_LINK_INTENT_URI
), o sistema usa oACTION_MAIN
Atividade para a categoriaCATEGORY_LEANBACK_LAUNCHER
, normalmente definida no manifesto do app. Se essa atividade não estiver definida, um link de aplicativo que não funciona será exibido. o usuário clicar nele, nada acontece. - Para o texto descritivo
(
COLUMN_APP_LINK_TEXT
), o sistema usa "Abrir app-name". Se nenhum URI de intent viável para o link de app for definido, o sistema usa a mensagem "Nenhum link disponível". - Para a cor de destaque
(
COLUMN_APP_LINK_COLOR
), o sistema usa a cor padrão do app. - Para a imagem do pôster
(
COLUMN_APP_LINK_POSTER_ART_URI
), o sistema usa o banner da tela inicial do app. Se o app não fornecer um o sistema usa uma imagem padrão de app de TV. - Para o ícone do selo
(
COLUMN_APP_LINK_ICON_URI
), o sistema usa um selo que mostra o nome do app. Se o sistema também estiver usando o banner do app ou imagem padrão do app para a imagem do pôster, nenhum selo do app vai ser exibido.
Você especifica detalhes do link do app para seus canais na
atividade de configuração. Você pode atualizar esses detalhes do link do app a qualquer momento. Por isso,
se um link de app precisar corresponder às mudanças de canal, atualize o app
detalhes do link e ligar
ContentResolver.update()
conforme necessário. Para mais detalhes sobre como atualizar
os dados de canal, consulte Atualizar dados de canal.