Canais na tela inicial

A tela inicial da 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:

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 mais na API, teste o codelab da tela inicial e assista à sessão sobre Android TV do I/O 2017 (link e vídeo em inglês).

Observação: os canais de recomendações estão disponíveis apenas no Android 8.0 (API de nível 26) e versões posteriores. Você precisa usá-los para fornecer recomendações para apps executados no Android 8.0 (API de nível 26) e versões posteriores. 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": programas que o usuário marcou como interessantes, parou de assistir no meio ou que estão 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).

O canal "Assistir a seguir" tem algumas restrições: seu app não pode mover, remover ou ocultar a linha desse canal.

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:

compile 'com.android.support:support-tv-provider:27.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 do 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 criar o primeiro canal, você poderá torná-lo o canal padrão para que ele apareça na tela inicial imediatamente sem ação do usuário. Nenhum outro canal que você criar ficará visível até que o usuário o 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 após o app ser 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 fazer upload do app durante o desenvolvimento, você pode testar essa etapa acionando o intent por meio do adb, em que nome.do.seu.pacote/.NomeDoSeuReceptor é o BroadcastReceiver do seu app:

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

    Em casos raros, seu app pode receber a transmissão ao mesmo tempo em que o usuário inicia o app. Verifique se seu 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 ver 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"

Inserir programas no canal "Assistir a seguir" é o mesmo que inserir programas no seu próprio canal.

Existem quatro tipos de programa. Selecione o tipo apropriado:

TipoObservações
WATCH_NEXT_TYPE_CONTINUEO usuário parou enquanto assistia ao conteúdo.
WATCH_NEXT_TYPE_NEXTO próximo programa em uma série que o usuário está assistindo está disponível. Por exemplo, se o usuário assistir ao episódio 3 de uma série, o app poderá sugerir que ele assista ao episódio 4 em seguida.
WATCH_NEXT_TYPE_NEWNovos conteúdos que seguem claramente o que o usuário está assistindo já estão disponíveis. Por exemplo, o usuário está assistindo ao episódio número 5 de uma série, e o episódio 6 é disponibilizado para exibição.
WATCH_NEXT_TYPE_WATCHLISTInserido pelo sistema ou pelo app quando o usuário salva um programa.

Use um WatchNextProgram.Builder:

Kotlin

    val builder = WatchNextProgram.Builder()
    builder.setType(TvContractCompat.WatchNextPrograms.TYPE_CLIP)
            .setWatchNextType(TvContractCompat.WatchNextPrograms.WATCH_NEXT_TYPE_CONTINUE)
            .setLastEngagementTimeUtcMillis(time)
            .setTitle("Title")
            .setDescription("Program description")
            .setPosterArtUri(uri)
            .setIntentUri(uri)
            .setInternalProviderId(appProgramId)

    val watchNextProgramUri = context.contentResolver
            .insert(TvContractCompat.WatchNextPrograms.CONTENT_URI,
                    builder.build().toContentValues())
    

Java

    WatchNextProgram.Builder builder = new WatchNextProgram.Builder();
    builder.setType(TvContractCompat.WatchNextPrograms.TYPE_CLIP)
            .setWatchNextType(TvContractCompat.WatchNextPrograms.WATCH_NEXT_TYPE_CONTINUE)
            .setLastEngagementTimeUtcMillis(time)
            .setTitle("Title")
            .setDescription("Program description")
            .setPosterArtUri(uri)
            .setIntentUri(uri)
            .setInternalProviderId(appProgramId);

    Uri watchNextProgramUri = context.getContentResolver()
            .insert(TvContractCompat.WatchNextPrograms.CONTENT_URI, builder.build().toContentValues());
    

Use TvContractCompat.buildWatchNextProgramUri(long watchNextProgramId) para criar o Uri de que você precisa para atualizar um programa de "Assistir a seguir".

Quando o usuário adiciona um programa ao canal "Assistir a seguir", o sistema copia o programa para a linha. Ele envia o intent TvContractCompat.ACTION_PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT para notificar o app que o programa foi adicionado. O intent inclui dois extras: o ID do programa que foi copiado e o ID do programa criado para o programa no 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 você precisa mudar 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 lidar com 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 ouve android.media.tv.action.INITIALIZE_PROGRAMS precisa sugerir conteúdo do canal para usuários não autenticados. Por exemplo, inicialmente, seu app pode mostrar o melhor conteúdo ou conteúdo em alta atualmente. Depois que o usuário fizer login, ele poderá mostrar conteúdo personalizado. Essa é uma ótima oportunidade para os apps fazerem um upsell dos usuários antes do login. O app de amostra leanback-homescreen-channels (link em inglês) demonstra como carregar canais após a instalação do seu app, ou após a configuração do dispositivo se o app estiver pré-instalado.
  • 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 seu app se o aplicativo não funcionar corretamente (por exemplo, enviar spam continuamente para o provedor com dados). Você precisa unir 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 para ver os dados que você precisa atualizar e reconciliá-los. Por exemplo, não é necessário atualizar um programa que o usuário quer remover da IU. Use um job em segundo plano que insira/atualize seus dados no provedor depois de consultar os dados existentes e, em seguida, solicite aprovação para seus canais. Você pode 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 Observações
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:

Código de amostra

Para saber mais sobre como criar apps que interagem com a tela inicial e adicionar canais e programas à tela inicial da Android TV, consulte nosso codelab e a amostra do Github (links em inglês) da tela inicial.