Neste codelab, você aprenderá a criar um app que adiciona canais e programas à tela inicial do Android TV usando as bibliotecas Kotlin e AndroidX. A tela inicial tem mais recursos do que os mostrados neste codelab. Leia a documentação para saber mais sobre todos os recursos e funcionalidades da tela inicial.
Conceitos
A tela inicial do Android TV, ou simplesmente a tela inicial, fornece uma IU que exibe o conteúdo recomendado como uma tabela de canais e programas. Cada linha é um canal. Um canal contém cards para todos os programas disponíveis nele. Seu app pode oferecer qualquer número de canais para o usuário adicionar à tela inicial. Um usuário geralmente precisa selecionar e aprovar cada canal antes que ele seja exibido na tela inicial.
Cada app tem a opção de criar um canal padrão. O canal padrão é especial, porque é exibido automaticamente na tela inicial. O usuário não precisa solicitá-lo explicitamente.
Visão geral
Este codelab demonstra como criar, adicionar e atualizar canais e programas na tela inicial. Ele usa um banco de dados simulado de coleções e filmes. Para simplificar, a mesma lista de filmes é usada para todas as assinaturas.
Clonar o repositório inicial do projeto
Este codelab usa o Android Studio, um ambiente de desenvolvimento integrado para o desenvolvimento de apps Android.
Se você ainda não o instalou, faça o download e instale-o.
Você pode fazer o download do código-fonte no repositório do GitHub:
git clone https://github.com/googlecodelabs/tv-recommendations-kotlin.git
Ou então, pode fazer o download dele como um arquivo ZIP.
Abra o Android Studio e clique em File > Open na barra de menus ou em Open an Existing Android Studio Project na tela de apresentação e selecione a pasta clonada recentemente.
Noções básicas sobre o projeto inicial
Há quatro etapas no projeto. Em cada etapa, você adicionará mais código ao app e, depois de concluir todas as instruções em cada seção, poderá comparar o resultado com o código na próxima etapa.
Estes são os principais componentes do app:
MainActivity
é a atividade de entrada do projeto.model/TvMediaBackground
é um objeto para imagens de plano de fundo durante a navegação pelos filmes.model
/TvMediaCollection
é um objeto para coleções de filmes.model/TvMediaMetadata
é um objeto para armazenar informações de filmes.model/TvMediaDatabase
é o suporte do banco de dados e serve como o ponto de acesso principal para os dados do filme.fragments/NowPlayingFragment
reproduz os filmes.fragments
/MediaBrowserFragment
é o fragmento do navegador de mídia.workers/TvMediaSynchronizer
é uma classe de sincronização de dados que contém o código para buscar feeds, construir objetos e atualizar canais.utils/TvLauncherUtils
é uma classe auxiliar para gerenciar canais e exibir prévias de programas usando a biblioteca AndroidX e os provedores de TV.
Executar o projeto inicial
Tente executar o projeto. Se encontrar problemas, consulte a documentação sobre como começar.
- Conecte o Android TV ou inicie o emulador.
- Selecione a configuração step_1, selecione seu dispositivo Android e pressione o botão run na barra de menu.
- Um resumo simples do app de TV com três coleções de vídeos será exibido.
O que você aprendeu
Na introdução, você aprendeu sobre:
- A tela inicial da TV e os canais
- A estrutura de código do projeto e as classes principais neste codelab
Qual é a próxima etapa?
Como adicionar canais à tela inicial
Comece adicionando canais à tela inicial. Depois de adicionar os canais, será possível inserir programas neles. Os usuários podem usar o painel de configuração de canais para descobrir e selecionar quais canais serão exibidos na IU da página inicial. Este codelab cria canais para cada uma das coleções de mídia:
- Longas-metragens históricos
- Longas-metragens da década de 1910
- Coleção Charlie Chaplin
A seção a seguir explica como carregar e usar dados em canais.
O método synchronize()
em TvMediaSynchronizer
:
- busca o feed de mídia, que inclui imagens de plano de fundo, coleções de mídia e metadados de vídeo. Essa informação é definida em
assets/media-feed.json
; - atualiza a instância
TvMediaDatabase
, que armazena imagens de plano de fundo, coleções de mídia e dados de vídeo nos respectivos objetos; - usa
TvLauncherUtils
para criar ou atualizar canais e programas.
Não se preocupe com o carregamento de dados neste codelab. O objetivo deste codelab é entender como usar a biblioteca AndroidX para criar canais. Para isso, você adicionará código a alguns métodos na classe TvLauncherUtils
.
Como criar um canal
Depois de buscar e salvar os dados de mídia em um banco de dados local, o código do projeto converte uma Collection
de mídia em um canal. O código cria e atualiza canais no método upsertChannel()
da classe TvLauncherUtils
.
- Crie uma instância de
PreviewChannel.Builder()
. Para evitar canais duplicados, este codelab verifica a existência de um canal e o atualiza somente se ele já existir. Cada coleção de vídeo tem um ID associado. Você pode usá-lo como ointernalProviderId
de um canal. Para identificar um canal existente, compare ointernalProviderId
dele com o ID da coleção. Copie e cole o seguinte código noupsertChannel()
no comentário do código //TODO: Step 1 create or find an existing channel.
val channelBuilder = if (existingChannel == null) {
PreviewChannel.Builder()
} else {
PreviewChannel.Builder(existingChannel)
}
- Defina atributos no
Builder
de um canal (por exemplo, o nome e o logotipo/ícone do canal). O nome de exibição aparece na tela inicial abaixo do ícone do canal. O Android TV usa oappLinkIntentUri
para navegar os usuários quando eles clicam no ícone de um canal. Este codelab usa esse URI para direcionar os usuários para a coleção correspondente no app. Copie e cole o seguinte código no comentário do código // TODO: Step 2 add collection metadata and build channel object.
val updatedChannel = channelBuilder
.setInternalProviderId(collection.id)
.setLogo(channelLogoUri)
.setAppLinkIntentUri(appUri)
.setDisplayName(collection.title)
.setDescription(collection.description)
.build()
- Chame funções na classe
PreviewChannelHelper
para inserir o canal no provedor de TV ou atualizá-lo. A chamada depublishChannel()
insere os valores de conteúdo do canal no provedor de TV.updatePreviewChannel
atualiza os canais existentes. Insira o seguinte código no comentário // TODO: Step 3.1 update an existing channel.
PreviewChannelHelper(context)
.updatePreviewChannel(existingChannel.id, updatedChannel)
Log.d(TAG, "Updated channel ${existingChannel.id}")
Insira o código abaixo para criar um novo canal no comentário // TODO: Step 3.2 publish a channel.
val channelId = PreviewChannelHelper(context).publishChannel(updatedChannel)
Log.d(TAG, "Published channel $channelId")
channelId
- Revise o método
upsertChannel()
para ver como os canais são criados ou atualizados.
Tornar o canal padrão visível
Quando você adiciona canais ao provedor de TV, eles ficam invisíveis. Um canal não é exibido na tela inicial até que o usuário faça a solicitação. O usuário geralmente precisa selecionar e aprovar cada canal antes que ele seja exibido na tela inicial. Cada app tem a opção de criar um canal padrão. O canal padrão é especial, porque é exibido automaticamente na tela inicial. O usuário não precisa aprová-lo explicitamente.
Adicione o seguinte código ao método upsertChannel()
em TODO: step 4 make default channel visible:
if(allChannels.none { it.isBrowsable }) {
TvContractCompat.requestChannelBrowsable(context, channelId)
}
Se você chamar requestChannelBrowsable()
para canais não padrão, uma caixa de diálogo será exibida solicitando o consentimento do usuário.
Como programar atualizações de canais
Depois de adicionar o código de criação/atualização de canal, os desenvolvedores precisam invocar o método synchronize()
para criar o canal ou atualizá-lo.
O melhor momento para criar os canais do app é logo após a instalação. Você pode criar um broadcast receiver para detectar a mensagem de transmissão android.media.tv.action.INITIALIZE_PROGRAMS
. Essa transmissão será enviada depois que o usuário instalar o app de TV. Os desenvolvedores poderão realizar algumas inicializações do programa nesse momento.
Confira o arquivo AndroidManifest.xml
no exemplo de código e localize a seção "broadcast receiver". Tente localizar o nome de classe correto do broadcast receiver (ele será abordado a seguir).
<action
android:name="android.media.tv.action.INITIALIZE_PROGRAMS" />
Abra a classe TvLauncherReceiver
e consulte o seguinte bloco de código para ver como o app de amostra cria canais na tela inicial.
TvContractCompat.ACTION_INITIALIZE_PROGRAMS -> {
Log.d(TAG, "Handling INITIALIZE_PROGRAMS broadcast")
// Synchronizes all program and channel data
WorkManager.getInstance(context).enqueue(
OneTimeWorkRequestBuilder<TvMediaSynchronizer>().build())
}
Atualize seus canais com frequência. Este codelab cria tarefas em segundo plano usando a biblioteca WorkManager. Na classe MainActivity
, TvMediaSynchronizer
é usado para programar atualizações frequentes de canais.
// Syncs the home screen channels hourly
// NOTE: It's very important to keep our content fresh in the user's home screen
WorkManager.getInstance(baseContext).enqueue(
PeriodicWorkRequestBuilder<TvMediaSynchronizer>(1, TimeUnit.HOURS)
.setInitialDelay(1, TimeUnit.HOURS)
.setConstraints(Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build())
.build())
Executar o app
Execute o app. Acesse a tela inicial. O canal padrão (My TV App Default) será exibido, mas não haverá programas. Se você estiver executando o código em um dispositivo real, e não em um emulador, o canal poderá não ser exibido.
Adicionar mais canais
O feed contém três coleções. Adicione outros canais para essas coleções na classe TvMediaSynchronizer
em TODO: step 5 add more channels.
feed.collections.subList(1, feed.collections.size).forEach {
TvLauncherUtils.upsertChannel(
context, it, database.metadata().findByCollection(it.id))
}
Executar o app novamente
Verifique se os três canais foram criados. Clique no botão Personalizar canais e, em seguida, em TV Classics. Alterne o botão exibir/ocultar no painel de canais para ocultar ou mostrar os canais na tela inicial.
Excluir um canal
Se o app não estiver mais mantendo um canal, você poderá removê-lo da tela inicial.
Procure por "step 6" e localize a função removeChannel
. Adicione a seguinte seção em TODO: step 6 remove a channel. Para ver como esse código funciona, remova a coleção chamada "Coleção Charlie Chaplin" em media-feed.json
(verifique se você removeu toda a coleção). Execute o app novamente. Após alguns segundos, você verá que o canal foi removido.
// First, get all the channels added to the home screen
val allChannels = PreviewChannelHelper(context).allChannels
// Now find the channel with the matching content ID for our collection
val foundChannel = allChannels.find { it.internalProviderId == collection.id }
if (foundChannel == null) Log.e(TAG, "No channel with ID ${collection.id}")
// Use the found channel's ID to delete it from the content resolver
return foundChannel?.let {
PreviewChannelHelper(context).deletePreviewChannel(it.id)
Log.d(TAG, "Channel successfully removed from home screen")
// Remove all of the channel programs as well
val channelPrograms =
TvContractCompat.buildPreviewProgramsUriForChannel(it.id)
context.contentResolver.delete(channelPrograms, null, null)
// Return the ID of the channel removed
it.id
}
Depois de concluir todas as instruções acima, compare o código do app com a step_2.
O que você aprendeu
- Como consultar canais.
- Como adicionar ou excluir canais na tela inicial.
- Como definir um logotipo ou o título de um canal.
- Como tornar um canal padrão visível.
- Como programar um
WorkManager
para atualizar canais.
Qual é a próxima etapa?
A próxima seção demonstra como adicionar programas a um canal.
Adicionar um programa a um canal é semelhante a criar um canal. Use PreviewProgram.Builder
em vez de PreviewChannel.Builder
Você ainda usará o método upsertChannel()
na classe TvLauncherUtils
.
Criar um programa de testes
Adicionaremos código à step_2 na próxima seção. Faça alterações nos arquivos de origem do módulo no projeto do Android Studio.
Depois que o canal estiver visível, crie um objeto PreviewProgram
usando objetos Metadata
com PreviewProgram.Builder
. Relembrando, não é interessante inserir o mesmo programa duas vezes em um canal, então a amostra atribui um metadata.id
ao contentId
do PreviewProgram
para eliminar duplicações. Adicione o seguinte código em TODO: Step 7 create or find an existing preview program.
val existingProgram = existingProgramList.find { it.contentId == metadata.id }
val programBuilder = if (existingProgram == null) {
PreviewProgram.Builder()
} else {
PreviewProgram.Builder(existingProgram)
}
Crie o builder usando metadados de mídia e publique/atualize-o no canal (TODO: Step 8 build preview program and publish).
val updatedProgram = programBuilder.also { metadata.copyToBuilder(it) }
// Set the same channel ID in all programs
.setChannelId(channelId)
// This must match the desired intent filter in the manifest for VIEW action
.setIntentUri(Uri.parse("https://$host/program/${metadata.id}"))
// Build the program at once
.build()
Veja a seguir alguns detalhes a serem considerados:
- O exemplo de código vincula os metadados ao programa de testes com o
contentId
. - O programa de testes é inserido em um canal ao chamar
setChannelId()
emPreviewProgram.Builder()
. - O sistema do Android TV iniciará o
intentUri
do programa quando o usuário selecioná-lo em um canal. OUri
precisa incluir o ID do programa para que o app possa encontrar e reproduzir a mídia do banco de dados quando o usuário selecionar o programa.
Como adicionar programas
Aqui, o codelab usa o PreviewChannelHelper
da biblioteca AndroidX para inserir programas em canais.
Use PreviewChannelHelper.publishPreviewProgram()
ou PreviewChannelHelper.updatePreviewProgram()
em TODO: Step 9 add preview program to channel para salvar o programa no canal.
try {
if (existingProgram == null) {
PreviewChannelHelper(context).publishPreviewProgram(updatedProgram)
Log.d(TAG, "Inserted program into channel: $updatedProgram")
} else {
PreviewChannelHelper(context)
.updatePreviewProgram(existingProgram.id, updatedProgram)
Log.d(TAG, "Updated program in channel: $updatedProgram")
}
} catch (exc: IllegalArgumentException) {
Log.e(TAG, "Unable to add program: $updatedProgram", exc)
}
Muito bem! O app agora pode adicionar programas a canais. Você pode comparar o código com step_3.
Executar o app
Selecione step_2 na configuração e execute o app.
Quando o app for executado, clique no botão Personalizar canais na parte inferior da tela inicial e procure nosso app "TV Classics". Alterne entre os três canais e veja os registros para saber o que está acontecendo. A criação de canais e programas acontece em segundo plano, então fique à vontade para adicionar mais log statements para ajudar a rastrear os eventos acionados.
O que você aprendeu
- Como adicionar programas a um canal.
- Como atualizar os atributos de um programa.
Qual é a próxima etapa?
Como adicionar programas ao canal "Assistir a seguir".
O canal "Assistir a seguir" fica na parte superior da tela inicial. Ele é exibido abaixo dos Apps e acima de todos os outros canais.
Conceitos
O canal "Assistir a seguir" permite que seu app gere engajamento com o usuário. Seu app poderá adicionar os seguintes programas ao canal "Assistir a seguir": programas que o usuário marcou como interessantes, programas que ele parou de assistir no meio ou programas relacionados ao conteúdo que ele está assistindo (como o próximo episódio de uma série ou a próxima temporada de um programa). Há quatro tipos de casos de uso para o canal "Assistir a seguir":
- Continuar assistindo a um vídeo que o usuário não terminou.
- Sugerir o próximo vídeo para o usuário assistir. Por exemplo, se o usuário tiver terminado de assistir o episódio 1, você poderá sugerir o episódio 2.
- Mostrar novos conteúdos para estimular o engajamento.
- Manter uma lista de interesses de vídeos adicionados pelo usuário.
Esta lição demonstra como usar o canal "Assistir a seguir" para continuar assistindo um vídeo, especificamente, como incluir um vídeo no canal "Assistir a seguir" quando o usuário pausá-lo. O vídeo precisa ser removido do canal "Assistir a seguir" quando a exibição chegar ao fim.
Atualizar a posição de reprodução
Existem algumas maneiras de monitorar a posição de reprodução do conteúdo em exibição. Este codelab usa uma linha de execução para salvar regularmente a última posição de reprodução no banco de dados e para atualizar os metadados do programa "Assistir a seguir". Abra step_3 e siga as instruções abaixo para adicionar esse código.
Em NowPlayingFragment
, adicione o seguinte código no método run()
de updateMetadataTask.
em TODO: step 10 update progress:
val contentDuration = player.duration
val contentPosition = player.currentPosition
// Updates metadata state
val metadata = args.metadata.apply {
playbackPositionMillis = contentPosition
}
O código só salvará metadados quando a posição de reprodução for inferior a 95% da duração total.
Adicione o seguinte código em TODO: step 11 update metadata to database.
val programUri = TvLauncherUtils.upsertWatchNext(requireContext(), metadata)
lifecycleScope.launch(Dispatchers.IO) {
database.metadata().update(
metadata.apply { if (programUri != null) watchNext = true })
}
Se a posição de reprodução for além de 95% do vídeo, o programa será removido do "Assistir a seguir" para permitir que outro conteúdo seja priorizado.
Em NowPlayingFragment
, adicione o seguinte código em TODO: step 12 remove watch next para remover o vídeo concluído da linha "Assistir a seguir".
val programUri = TvLauncherUtils.removeFromWatchNext(requireContext(), metadata)
if (programUri != null) lifecycleScope.launch(Dispatchers.IO) {
database.metadata().update(metadata.apply { watchNext = false })
}
A updateMetadataTask
é programada para ocorrer a cada 10 segundos para garantir que a posição de reprodução mais recente seja monitorada. Ela está programada em onResume()
e é interrompida em onPause()
no NowPlayingFragment
. Assim, os dados são atualizados apenas quando o usuário está assistindo a um vídeo.
Como adicionar/atualizar um programa no Assistir a seguir
TvLauncherUtils
interage com o provedor de TV. Na etapa anterior, removeFromWatchNext
e upsertWatchNext
são chamados em TvLauncherUtils
. Agora é preciso implementar esses dois métodos. A biblioteca do AndroidX fornece a classe PreviewChannelHelper
, o que simplifica muito essa tarefa.
Primeiro, crie ou encontre uma instância já existente do WatchNextProgram.Builder
e atualize o objeto com os metadata
mais recentes da reprodução. Adicione o seguinte código ao método upsertWatchNext()
em TODO: step 13 build watch next program:
programBuilder.setLastEngagementTimeUtcMillis(System.currentTimeMillis())
programBuilder.setWatchNextType(metadata.playbackPositionMillis?.let { position ->
if (position > 0 && metadata.playbackDurationMillis?.let { it > 0 } == true) {
Log.d(TAG, "Inferred watch next type: CONTINUE")
TvContractCompat.WatchNextPrograms.WATCH_NEXT_TYPE_CONTINUE
} else {
Log.d(TAG, "Inferred watch next type: UNKNOWN")
WatchNextProgram.WATCH_NEXT_TYPE_UNKNOWN
}
} ?: TvContractCompat.WatchNextPrograms.WATCH_NEXT_TYPE_NEXT)
// This must match the desired intent filter in the manifest for VIEW intent action
programBuilder.setIntentUri(Uri.parse(
"https://${context.getString(R.string.host_name)}/program/${metadata.id}"))
// Build the program with all the metadata
val updatedProgram = programBuilder.build()
Depois de chamar o método build(
) em um WatchNextProgram.Builder
, um WatchNextProgam
será criado. Você pode publicá-lo na linha "Assistir a seguir" usando PreviewChannelHelper
.
Adicione o seguinte código em TODO: step 14.1 create watch next program:
val programId = PreviewChannelHelper(context)
.publishWatchNextProgram(updatedProgram)
Log.d(TAG, "Added program to watch next row: $updatedProgram")
programId
Ou, se o programa já existir, basta atualizá-lo em TODO: step 14.2 update watch next program.
PreviewChannelHelper(context)
.updateWatchNextProgram(updatedProgram, existingProgram.id)
Log.d(TAG, "Updated program in watch next row: $updatedProgram")
existingProgram.id
Como remover um programa do Assistir a seguir
Quando um usuário terminar um vídeo, limpe o canal "Assistir a seguir". Isso é quase igual a remover um PreviewProgram
.
Use buildWatchNextProgramUri()
para criar um Uri
que realiza a exclusão. Não há uma API que possa ser usada em PreviewChannelHelper
para remover um programa do "Assistir a seguir".
Substitua o código existente no método removeFromWatchNext()
da classe TvLauncherUtils
pelas instruções abaixo em TODO: step 15 remove program:
val programUri = TvContractCompat.buildWatchNextProgramUri(it.id)
val deleteCount = context.contentResolver.delete(
programUri, null, null)
Executar o app
Selecione step_3 na configuração e execute o app.
Assista um vídeo de qualquer uma das coleções por alguns segundos e pause o player (use a barra de espaço se estiver usando o emulador). Quando voltar à tela inicial, verá que o filme foi adicionado ao canal "Assistir a seguir". Selecione o mesmo filme no canal "Assistir a seguir" e ele continuará de onde você o pausou. Depois que você assistir o filme até o final, ele será removido do canal. Faça testes com o canal "Assistir a seguir" com diferentes casos para o usuário.
O que você aprendeu
- Como adicionar programas ao canal "Assistir a seguir" para impulsionar o engajamento.
- Como atualizar um programa no canal "Assistir a seguir".
- Como remover um programa do canal "Assistir a seguir".
Qual é a próxima etapa?
Depois de concluir o codelab, personalize o app. Substitua o modelo de mídia e de dados pelos seus canais e os converta em canais e programas para o provedor de TV.
Para saber mais, consulte a documentação.