Integração de conteúdo com canais na tela inicial do Android TV (Kotlin)

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.

aa0471dc91b5f815.png

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.

Fazer o download do 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.

c0e57864138c1248.png

Noções básicas sobre o projeto inicial

bd4f805254260df7.png

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.

  1. Conecte o Android TV ou inicie o emulador.
  1. Selecione a configuração step_1, selecione seu dispositivo Android e pressione o botão run na barra de menu. ba443677e48e0f00.png
  2. Um resumo simples do app de TV com três coleções de vídeos será exibido.

364574330c4e90a5.png

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:

  1. 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;
  2. 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;
  3. 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.

  1. 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 o internalProviderId de um canal. Para identificar um canal existente, compare o internalProviderId dele com o ID da coleção. Copie e cole o seguinte código no upsertChannel() 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)
}
  1. 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 o appLinkIntentUri 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()
  1. Chame funções na classe PreviewChannelHelper para inserir o canal no provedor de TV ou atualizá-lo. A chamada de publishChannel() 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
  1. 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.

f14e903b0505a281.png

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.

faac02714aa36ab6.png

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.

e096c4d12a3d0a01.png

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:

  1. O exemplo de código vincula os metadados ao programa de testes com o contentId.
  2. O programa de testes é inserido em um canal ao chamar setChannelId() em PreviewProgram.Builder().
  3. O sistema do Android TV iniciará o intentUri do programa quando o usuário selecioná-lo em um canal. O Uri 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.

200e69351ce6a530.png

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.

44b6a6f24e4420e3.png

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.

6e43dc24a1ef0273.png

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.