Canais na tela inicial

A tela inicial do Android TV, ou simplesmente a tela inicial, oferece 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:

Tela inicial da TV

Este documento demonstra como adicionar canais e programas à tela inicial, atualizar o conteúdo, lidar com ações do usuário e fornecer a melhor experiência para seus usuários. Se você quiser se aprofundar na API, confira o codelab da tela inicial e assista a sessão do Android TV do I/O 2017 (links em inglês).

Observação:os canais de recomendações estão disponíveis apenas no Android 8.0 (nível 26 da API) e versões mais recentes. Você precisa usá-los para fornecer recomendações para apps em execução no Android 8.0 (nível 26 da API) e versões mais recentes. Para fornecer recomendações para apps executados em versões anteriores do Android, seu app precisa usar a linha de recomendações.

A IU da tela inicial

Os apps podem criar novos canais, adicionar, remover e atualizar os programas em um canal e controlar a ordem dos programas em um canal. Por exemplo, um app pode criar um canal chamado "Novidades" e mostrar cards de programas recém-disponibilizados.

Os apps não podem controlar a ordem em que os canais aparecem na tela inicial. Quando seu app cria um novo canal, a tela inicial o adiciona à parte inferior da lista de canais. O usuário pode reordenar, ocultar e mostrar canais.

O canal "Assistir a seguir"

O canal "Assistir a seguir" é a segunda linha que aparece na tela inicial, depois da linha de apps. O sistema cria e mantém esse canal. Seu app pode adicionar programas ao canal "Assistir a seguir". Para mais informações, consulte Adicionar programas ao canal "Assistir a seguir".

Canais do app

Todos os canais criados pelo seu app seguem este ciclo de vida:

  1. O usuário descobre um canal no seu app e solicita que ele seja adicionado à tela inicial.
  2. O app cria o canal e o adiciona a TvProvider (nesse ponto, o canal não está visível).
  3. O app solicita que o sistema exiba o canal.
  4. O sistema pede que o usuário aprove o novo canal.
  5. O novo canal aparece na última linha da tela inicial.

O canal padrão

Seu app pode oferecer qualquer número de canais para o usuário adicionar à tela inicial. O usuário geralmente precisa selecionar e aprovar cada canal antes que ele apareça na tela inicial. Cada app tem a opção de criar um canal padrão. O canal padrão é especial porque aparece automaticamente na tela inicial. O usuário não precisa solicitá-lo explicitamente.

Pré-requisitos

A tela inicial da Android TV usa as APIs TvProvider do Android para gerenciar os canais e programas criados pelo seu app. Para acessar os dados do provedor, adicione a seguinte permissão ao manifesto do seu app:

<uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />

A Biblioteca de Suporte TvProvider facilita o uso do provedor. Adicione-a às dependências no seu arquivo build.gradle:

Groovy

implementation 'androidx.tvprovider:tvprovider:1.0.0'

Kotlin

implementation("androidx.tvprovider:tvprovider:1.0.0")

Para trabalhar com canais e programas, não se esqueça de incluir estas importações da Biblioteca de Suporte no seu programa:

Kotlin

import android.support.media.tv.Channel
import android.support.media.tv.TvContractCompat
import android.support.media.tv.ChannelLogoUtils
import android.support.media.tv.PreviewProgram
import android.support.media.tv.WatchNextProgram

Java

import android.support.media.tv.Channel;
import android.support.media.tv.TvContractCompat;
import android.support.media.tv.ChannelLogoUtils;
import android.support.media.tv.PreviewProgram;
import android.support.media.tv.WatchNextProgram;

Canais

O primeiro canal criado pelo app se torna o canal padrão. O canal padrão é exibido automaticamente na tela inicial. Todos os outros canais que você criar precisam ser selecionados e aceitos pelo usuário antes de serem exibidos na tela inicial.

Como criar um canal

Seu app pedirá ao sistema para mostrar os canais adicionados recentemente apenas quando ele estiver sendo executado em primeiro plano. Isso impede que o app exiba uma caixa de diálogo solicitando aprovação para adicionar seu canal enquanto o usuário estiver usando um app diferente. Se você tentar adicionar um canal durante a execução em segundo plano, o método onActivityResult() da atividade retornará o código de status RESULT_CANCELED.

Para criar um canal, siga estas etapas:

  1. Crie um gerador de canal e defina os atributos dele. O tipo de canal precisa ser TYPE_PREVIEW. Adicione mais atributos conforme necessário.

    Kotlin

    val builder = Channel.Builder()
    // Every channel you create must have the type TYPE_PREVIEW
    builder.setType(TvContractCompat.Channels.TYPE_PREVIEW)
            .setDisplayName("Channel Name")
            .setAppLinkIntentUri(uri)
    

    Java

    Channel.Builder builder = new Channel.Builder();
    // Every channel you create must have the type TYPE_PREVIEW
    builder.setType(TvContractCompat.Channels.TYPE_PREVIEW)
            .setDisplayName("Channel Name")
            .setAppLinkIntentUri(uri);
    
  2. Insira o canal no provedor:

    Kotlin

    var channelUri = context.contentResolver.insert(
            TvContractCompat.Channels.CONTENT_URI, builder.build().toContentValues())
    

    Java

    Uri channelUri = context.getContentResolver().insert(
            TvContractCompat.Channels.CONTENT_URI, builder.build().toContentValues());
    
  3. É necessário salvar o ID do canal para adicionar programas ao canal mais tarde. Extraia o ID do canal do URI retornado:

    Kotlin

    var channelId = ContentUris.parseId(channelUri)
    

    Java

    long channelId = ContentUris.parseId(channelUri);
    
  4. Você precisa adicionar um logotipo para seu canal. Use um Uri ou Bitmap. O ícone do logotipo precisa ter 80 dp x 80 dp e ser opaco. Ele é exibido sob uma máscara circular:

    Máscara de ícone da tela inicial da TV

    Kotlin

    // Choose one or the other
    storeChannelLogo(context: Context, channelId: Long, logoUri: Uri) // also works if logoUri is a URL
    storeChannelLogo(context: Context, channelId: Long, logo: Bitmap)
    

    Java

    // Choose one or the other
    storeChannelLogo(Context context, long channelId, Uri logoUri); // also works if logoUri is a URL
    storeChannelLogo(Context context, long channelId, Bitmap logo);
    
  5. Criar o canal padrão (opcional): quando seu app cria o primeiro canal, você pode torná-lo o canal padrão para que ele apareça na tela inicial imediatamente, sem nenhuma ação do usuário. Nenhum outro canal criado ficará visível até que o usuário selecione explicitamente.

    Kotlin

    TvContractCompat.requestChannelBrowsable(context, channelId)
    

    Java

    TvContractCompat.requestChannelBrowsable(context, channelId);
    

  6. Faça com que seu canal padrão apareça antes que o app seja aberto. Você pode fazer esse comportamento acontecer adicionando um BroadcastReceiver que detecta a ação android.media.tv.action.INITIALIZE_PROGRAMS, que a tela inicial envia depois que o app é instalado:
    <receiver
      android:name=".RunOnInstallReceiver"
      android:exported="true">
        <intent-filter>
          <action android:name="android.media.tv.action.INITIALIZE_PROGRAMS" />
          <category android:name="android.intent.category.DEFAULT" />
        </intent-filter>
    </receiver>
    
    Ao transferir seu app por sideload durante o desenvolvimento, você pode testar essa etapa acionando a intent pelo adb, em que your.package.name/.YourReceiverName é o BroadcastReceiver do app:

    adb shell am broadcast -a android.media.tv.action.INITIALIZE_PROGRAMS -n \
        your.package.name/.YourReceiverName
    

    Em casos raros, o app pode receber a transmissão ao mesmo tempo que o usuário inicia o app. Verifique se o código não tenta adicionar o canal padrão mais de uma vez.

Como atualizar um canal

Atualizar canais é muito semelhante a criá-los.

Use outro Channel.Builder para definir os atributos que precisam ser mudados.

Use ContentResolver para atualizar o canal. Use o ID do canal que você salvou quando o canal foi adicionado originalmente:

Kotlin

context.contentResolver.update(
        TvContractCompat.buildChannelUri(channelId),
        builder.build().toContentValues(),
        null,
        null
)

Java

context.getContentResolver().update(TvContractCompat.buildChannelUri(channelId),
    builder.build().toContentValues(), null, null);

Para atualizar o logotipo de um canal, use storeChannelLogo().

Como excluir um canal

Kotlin

context.contentResolver.delete(TvContractCompat.buildChannelUri(channelId), null, null)

Java

context.getContentResolver().delete(TvContractCompat.buildChannelUri(channelId), null, null);

Programas

Como adicionar programas a um canal do app

Crie um PreviewProgram.Builder e defina os atributos:

Kotlin

val builder = PreviewProgram.Builder()
builder.setChannelId(channelId)
        .setType(TvContractCompat.PreviewPrograms.TYPE_CLIP)
        .setTitle("Title")
        .setDescription("Program description")
        .setPosterArtUri(uri)
        .setIntentUri(uri)
        .setInternalProviderId(appProgramId)

Java

PreviewProgram.Builder builder = new PreviewProgram.Builder();
builder.setChannelId(channelId)
        .setType(TvContractCompat.PreviewPrograms.TYPE_CLIP)
        .setTitle("Title")
        .setDescription("Program description")
        .setPosterArtUri(uri)
        .setIntentUri(uri)
        .setInternalProviderId(appProgramId);

Adicione mais atributos dependendo do tipo de programa. Para conferir os atributos disponíveis para cada tipo de programa, consulte as tabelas abaixo.

Insira o programa no provedor:

Kotlin

var programUri = context.contentResolver.insert(TvContractCompat.PreviewPrograms.CONTENT_URI,
        builder.build().toContentValues())

Java

Uri programUri = context.getContentResolver().insert(TvContractCompat.PreviewPrograms.CONTENT_URI,
      builder.build().toContentValues());

Recupere o ID do programa para referência futura:

Kotlin

val programId = ContentUris.parseId(programUri)

Java

long programId = ContentUris.parseId(programUri);

Como adicionar programas ao canal "Assistir a seguir"

Para inserir programas no canal "Assistir a seguir", consulte Adicionar programas ao canal "Assistir a seguir".

Como atualizar um programa

Você pode mudar as informações de um programa. Por exemplo, convém atualizar o preço do aluguel de um filme ou atualizar uma barra de progresso que mostra quanto de um programa o usuário assistiu.

Use um PreviewProgram.Builder para definir os atributos que precisam ser mudados e chame getContentResolver().update para atualizar o programa. Especifique o ID do programa que você salvou quando o programa foi adicionado originalmente:

Kotlin

context.contentResolver.update(
        TvContractCompat.buildPreviewProgramUri(programId),
                builder.build().toContentValues(), null, null
)

Java

context.getContentResolver().update(TvContractCompat.buildPreviewProgramUri(programId),
    builder.build().toContentValues(), null, null);

Como excluir um programa

Kotlin

context.contentResolver
        .delete(TvContractCompat.buildPreviewProgramUri(programId), null, null)

Java

context.getContentResolver().delete(TvContractCompat.buildPreviewProgramUri(programId), null, null);

Como gerenciar ações do usuário

Seu app pode ajudar os usuários a descobrir o conteúdo fornecendo uma IU para exibir e adicionar canais. Ele também precisa lidar com interações com seus canais depois que eles aparecem na tela inicial.

Como descobrir e adicionar canais

Seu app pode fornecer um elemento de IU que permite ao usuário selecionar e adicionar canais (por exemplo, um botão que pede para adicionar o canal).

Depois que o usuário solicitar um canal específico, execute este código para receber a permissão do usuário para adicioná-lo à IU da tela inicial:

Kotlin

val intent = Intent(TvContractCompat.ACTION_REQUEST_CHANNEL_BROWSABLE)
intent.putExtra(TvContractCompat.EXTRA_CHANNEL_ID, channelId)
try {
  activity.startActivityForResult(intent, 0)
} catch (e: ActivityNotFoundException) {
  // handle error
}

Java

Intent intent = new Intent(TvContractCompat.ACTION_REQUEST_CHANNEL_BROWSABLE);
intent.putExtra(TvContractCompat.EXTRA_CHANNEL_ID, channelId);
try {
   activity.startActivityForResult(intent, 0);
} catch (ActivityNotFoundException e) {
  // handle error
}

O sistema exibe uma caixa de diálogo solicitando que o usuário aprove o canal. Gerencie o resultado da solicitação no método onActivityResult da sua atividade (Activity.RESULT_CANCELED ou Activity.RESULT_OK).

Eventos da tela inicial do Android TV

Quando o usuário interage com os programas/canais publicados pelo app, a tela inicial envia intents para o app:

  • A tela inicial envia o Uri armazenado no atributo APP_LINK_INTENT_URI de um canal para o app quando o usuário seleciona o logotipo do canal. O app precisa abrir a IU principal ou uma visualização relacionada ao canal selecionado.
  • A tela inicial envia o Uri armazenado no atributo INTENT_URI de um programa para o app quando o usuário seleciona um programa. O app precisa reproduzir o conteúdo selecionado.
  • O usuário pode indicar que não está mais interessado em um programa e quer removê-lo da IU da tela inicial. O sistema removerá o programa da IU e enviará ao app proprietário do programa um intent (android.media.tv.ACTION_PREVIEW_PROGRAM_BROWSABLE_DISABLED ou android.media.tv.ACTION_WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED) com o ID do programa. O app precisa remover o programa do provedor e NÃO reinseri-lo.

Não esqueça de criar filtros de intent para todos os Uris que a tela inicial envia para interações de usuário. Por exemplo:

<receiver
   android:name=".WatchNextProgramRemoved"
   android:enabled="true"
   android:exported="true">
   <intent-filter>
       <action android:name="android.media.tv.ACTION_WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED" />
   </intent-filter>
</receiver>

Práticas recomendadas

  • Muitos apps de TV exigem que os usuários façam login. Nesse caso, o BroadcastReceiver que detecta android.media.tv.action.INITIALIZE_PROGRAMS precisa sugerir o conteúdo do canal para usuários não autenticados.Por exemplo, inicialmente, o app pode mostrar o melhor conteúdo ou conteúdo em alta no momento. Depois que o usuário fizer login, ele poderá mostrar conteúdo personalizado. Essa é uma ótima chance para os apps fazerem um upsell dos usuários antes que eles façam login.
  • Quando seu app não estiver em primeiro plano e você precisar atualizar um canal ou um programa, use o JobScheduler para programar o trabalho. Consulte JobScheduler e JobService.
  • O sistema pode revogar as permissões de provedor do app se ele não funcionar corretamente, por exemplo, enviar spam continuamente para o provedor com dados. Una o código que acessa o provedor com cláusulas try-catch para lidar com exceções de segurança.
  • Antes de atualizar programas e canais, consulte o provedor quanto aos dados necessários para atualizar e reconciliar os dados. Por exemplo, não é necessário atualizar um programa que o usuário quer remover da interface. Use um job em segundo plano que insira/atualize seus dados no provedor depois de consultar os dados existentes e, em seguida, solicite a aprovação para seus canais. É possível executar esse job quando o app for iniciado e sempre que o app precisar atualizar os dados.

    Kotlin

    context.contentResolver
      .query(
          TvContractCompat.buildChannelUri(channelId),
              null, null, null, null).use({
                  cursor-> if (cursor != null and cursor.moveToNext()) {
                               val channel = Channel.fromCursor(cursor)
                               if (channel.isBrowsable()) {
                                   //update channel's programs
                               }
                           }
              })
    

    Java

    try (Cursor cursor = context.getContentResolver()
          .query(
              TvContractCompat.buildChannelUri(channelId),
              null,
              null,
              null,
              null)) {
                  if (cursor != null && cursor.moveToNext()) {
                      Channel channel = Channel.fromCursor(cursor);
                      if (channel.isBrowsable()) {
                          //update channel's programs
                      }
                  }
              }
    
  • Use URIs exclusivos para todas as imagens (logotipos, ícones, imagens de conteúdo). Use um URI diferente ao atualizar uma imagem. Todas as imagens são armazenadas em cache. Se você não mudar o URI quando mudar a imagem, a imagem antiga continuará aparecendo.

  • Lembre-se de que cláusulas WHERE não são permitidas, e chamadas para os provedores com cláusulas WHERE gerarão uma exceção de segurança.

Atributos

Esta seção descreve os atributos do canal e do programa separadamente.

Atributos do canal

Você precisa especificar estes atributos para cada canal:

Atributo Notes
TYPE defina como TYPE_PREVIEW.
DISPLAY_NAME defina como o nome do canal.
APP_LINK_INTENT_URI Quando o usuário seleciona o logotipo do canal, o sistema envia um intent para iniciar uma atividade que apresenta conteúdo relevante para o canal. Defina esse atributo como o URI usado no filtro de intent dessa atividade.

Além disso, um canal também tem seis campos reservados para uso interno do app. Esses campos podem ser usados para armazenar chaves ou outros valores que podem ajudar o app a mapear o canal para a estrutura de dados interna:

  • INTERNAL_PROVIDER_ID
  • INTERNAL_PROVIDER_DATA
  • INTERNAL_PROVIDER_FLAG1
  • INTERNAL_PROVIDER_FLAG2
  • INTERNAL_PROVIDER_FLAG3
  • INTERNAL_PROVIDER_FLAG4

Atributos do programa

Veja as páginas individuais para os atributos de cada tipo de programa:

Exemplo de código

Para saber mais sobre como criar apps que interagem com a tela inicial e adicionar canais e programas à tela inicial da Android TV, consulte o codelab da tela inicial.