Canales en la pantalla principal

La pantalla principal de Android TV, o simplemente la pantalla principal, proporciona una IU que muestra contenido recomendado en una tabla de canales y programas. Cada fila corresponde a un canal. Un canal, a su vez, tiene tarjetas para cada uno de sus programas:

Pantalla principal de TV

En este documento, se muestra cómo agregar canales y programas a la pantalla principal, actualizar contenido, controlar las acciones del usuario y proporcionar la mejor experiencia posible para todos los usuarios. (Si quieres profundizar en la API, prueba el codelab de la pantalla principal y mira la sesión de Android TV de I/O 2017).

Nota: Los canales de recomendaciones solo están disponibles en Android 8.0 (nivel de API 26) y versiones posteriores. Debes usarlos a fin de proporcionar recomendaciones para las apps que se ejecutan en Android 8.0 (nivel de API 26) y versiones posteriores. A fin de ofrecer recomendaciones para las apps que se ejecutan en versiones anteriores de Android, tu app debe usar la fila de recomendaciones en su lugar.

IU de la pantalla principal

Las apps pueden crear nuevos canales, o bien agregar, quitar y actualizar programas en un canal, así como controlar el orden de los programas de un canal. Por ejemplo, una app puede crear un canal denominado "Novedades" y mostrar tarjetas para los nuevos programas disponibles.

Las apps no pueden controlar el orden en que se muestran los canales en la pantalla principal. Cuando tu app crea un nuevo canal, la pantalla principal lo agrega a la parte inferior de la lista de canales. El usuario puede reordenar, ocultar y mostrar los canales.

Canal "Ver a continuación"

El canal Ver a continuación es la segunda fila que aparece en la pantalla principal, después de la fila de apps. El sistema crea y mantiene este canal. Tu app puede agregar programas al canal Ver a continuación. Para obtener más información, consulta Cómo agregar programas al canal Ver a continuación.

Canales de apps

Todos los canales que crea la app siguen este ciclo de vida:

  1. El usuario detecta un canal en tu app y solicita agregarlo a la pantalla principal.
  2. La app crea el canal y lo agrega a TvProvider (en este momento, el canal no es visible).
  3. La app le solicita al sistema que muestre el canal.
  4. El sistema le solicita al usuario que apruebe el nuevo canal.
  5. El nuevo canal se muestra en la última fila de la pantalla principal.

Canal predeterminado

Tu app puede ofrecer cualquier número de canales para que el usuario los agregue a la pantalla principal. Por lo general, el usuario debe seleccionar y aprobar cada canal antes de que aparezca en la pantalla principal. Sin embargo, las apps tienen la opción de crear un canal predeterminado. El canal predeterminado es especial porque se muestra automáticamente en la pantalla principal; el usuario no tiene que solicitarlo de forma explícita.

Requisitos previos

La pantalla principal de Android TV usa las API de TvProvider de Android para administrar los canales y programas que crea tu app. Para acceder a los datos del proveedor, agrega el siguiente permiso al manifiesto de la app:

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

La biblioteca de compatibilidad TvProvider facilita el uso del proveedor. Agrégala a las dependencias de tu archivo build.gradle:

Groovy

implementation 'androidx.tvprovider:tvprovider:1.0.0'

Kotlin

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

Para trabajar con canales y programas, asegúrate de incluir estas importaciones de la biblioteca de compatibilidad en tu 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;

Canales

El primer canal que crea tu app se convierte en el canal predeterminado. El canal predeterminado se muestra automáticamente en la pantalla principal. El usuario debe seleccionar y aceptar todos los demás canales que crees para que estos se muestren en la pantalla principal.

Cómo crear un canal

Tu app debe solicitar al sistema que muestre los últimos canales agregados solo cuando se ejecuta en segundo plano. De esta manera, se evita que tu app muestre un cuadro de diálogo solicitando la aprobación para agregar el canal mientras el usuario ejecuta otra app. Si intentas agregar un canal mientras se ejecuta en segundo plano, el método onActivityResult() de la actividad muestra el código de estado RESULT_CANCELED.

Para crear un canal, sigue estos pasos:

  1. Diseña un compilador de canales y establece sus atributos. Ten en cuenta que el tipo de canal debe ser TYPE_PREVIEW. Agrega más atributos según sea necesario.

    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. Inserta el canal en el proveedor:

    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. Debes guardar el ID del canal para poder agregar programas al canal más tarde. Extrae el ID de canal de la URI que se muestra:

    Kotlin

    var channelId = ContentUris.parseId(channelUri)
    

    Java

    long channelId = ContentUris.parseId(channelUri);
    
  4. Debes agregar un logotipo para el canal. Usa un elemento Uri o Bitmap. El ícono del logotipo debe ser opaco y de 80 dp x 80 dp. Se muestra con una máscara circular:

    Máscara de ícono de pantalla principal de 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. Crea el canal predeterminado (opcional): Cuando la app crea el primer canal, puedes convertirlo en el canal predeterminado para que se muestre en la pantalla principal de inmediato sin que el usuario realice ninguna acción. El resto de los canales que creas no se muestran hasta que el usuario los selecciona de forma explícita.

    Kotlin

    TvContractCompat.requestChannelBrowsable(context, channelId)
    

    Java

    TvContractCompat.requestChannelBrowsable(context, channelId);
    

  6. Puedes hacer que el canal predeterminado se muestre antes de que el usuario abra la app. Para definir este comportamiento, agrega un BroadcastReceiver que escuche la acción android.media.tv.action.INITIALIZE_PROGRAMS, que la pantalla principal envía después de instalar la app:
    <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>
    
    Cuando transfieres tu app durante el desarrollo, puedes probar este paso activando el intent mediante adb, donde your.package.name/.YourReceiverName es el BroadcastReceiver de la app:

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

    En casos excepcionales, es posible que tu app reciba la emisión al mismo tiempo que el usuario la inicia. Asegúrate de que tu código no intente agregar el canal predeterminado más de una vez.

Cómo actualizar un canal

La actualización de canales es muy similar a la creación.

Usa otro Channel.Builder para configurar los atributos que deben cambiarse.

Usa ContentResolver para actualizar el canal. Usa el ID de canal que guardaste cuando se agregó el canal 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 actualizar el logotipo de un canal, usa storeChannelLogo().

Cómo borrar un canal

Kotlin

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

Java

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

Programas

Cómo agregar programas a un canal de app

Crea un PreviewProgram.Builder y establece sus 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);

Agrega más atributos según el tipo de programa. (A fin de ver los atributos disponibles para cada tipo de programa, consulta las tablas a continuación).

Inserta el programa en el proveedor:

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());

Obtén el ID de programa para referencia futura:

Kotlin

val programId = ContentUris.parseId(programUri)

Java

long programId = ContentUris.parseId(programUri);

Cómo agregar programas al canal "Ver a continuación"

Para insertar programas en el canal Ver a continuación, consulta Cómo agregar programas al canal Ver a continuación.

Cómo actualizar un programa

Puedes cambiar la información de un programa, por ejemplo, si quieres actualizar el precio del alquiler de una película o actualizar la barra de progreso que muestra qué porcentaje de un programa vio el usuario.

Usa un PreviewProgram.Builder para establecer los atributos que necesitas cambiar y, luego, llama a getContentResolver().update para actualizar el programa. Especifica el ID de programa que guardaste cuando se agregó originalmente el programa:

Kotlin

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

Java

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

Cómo borrar un programa

Kotlin

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

Java

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

Cómo controlar las acciones del usuario

Tu app can puede ayudar a los usuarios a descubrir contenido mediante una IU que permita mostrar y agregar programas. La app también deberá controlar las interacciones con tus canales una vez que se muestren en la pantalla principal.

Cómo descubrir y agregar canales

Tu app puede proporcionar un elemento de IU que permita al usuario seleccionar y agregar sus canales (por ejemplo, un botón que solicite agregar el canal).

Después de que el usuario solicite un canal específico, ejecuta este código para obtener el permiso del usuario y agregarlo a la IU de la pantalla principal:

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
}

El sistema muestra un diálogo que solicita al usuario que apruebe el canal. Controla el resultado de la solicitud en el método onActivityResult de tu actividad (Activity.RESULT_CANCELED o Activity.RESULT_OK).

Eventos de la pantalla principal de Android TV

Cuando el usuario interactúa con los programas/canales publicados por la app, la pantalla principal envía instancias de intent a la app:

  • La pantalla principal envía el elemento Uri almacenado en el atributo APP_LINK_INTENT_URI de un canal a la app cuando el usuario selecciona el logotipo del canal. La app debe lanzar su IU principal o una vista relacionada con el canal seleccionado.
  • La pantalla principal envía el elemento Uri almacenado en el atributo INTENT_URI de un programa a la app cuando el usuario selecciona un programa. La app debe mostrar el contenido seleccionado.
  • El usuario puede indicar que ya no le interesa un programa y quiere que se quite de la IU de la pantalla principal. El sistema quita el programa de la IU y envía a la app a la que pertenece el programa un intent (android.media.tv.ACTION_PREVIEW_PROGRAM_BROWSABLE_DISABLED o android.media.tv.ACTION_WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED) con el ID del programa. La app debe quitar el programa del proveedor y NO volver a insertarlo.

Asegúrate de crear filtros de intents para todos los elementos Uris que la pantalla principal envía sobre interacciones del usuario, por ejemplo:

<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ácticas recomendadas

  • Muchas apps de TV requieren que los usuarios accedan. En este caso, el BroadcastReceiver que escucha a android.media.tv.action.INITIALIZE_PROGRAMS debe sugerir contenido del canal para los usuarios no autenticados.Por ejemplo, tu app inicialmente puede mostrar el mejor contenido o el contenido más popular. Una vez que el usuario accede, puede mostrar contenido personalizado. Esta es una gran oportunidad para que las apps realicen ventas incrementales a los usuarios antes de que accedan.
  • Cuando la app no esté en primer plano y necesites actualizar un canal o un programa, usa JobScheduler para programar el trabajo (consulta: JobScheduler y JobService).
  • El sistema puede revocar los permisos del proveedor de tu app si esta no se comporta correctamente (por ejemplo, si envía spam con datos de manera continua al proveedor). Asegúrate de unir el código que accede al proveedor con cláusulas try-catch para manejar las excepciones de seguridad.
  • Antes de actualizar los programas y canales, pídele al proveedor los datos que necesitas para actualizar y concílialos. Por ejemplo, no es necesario actualizar un programa que el usuario quiere quitar de la IU. Usa un trabajo en segundo plano que inserte o actualice tus datos en el proveedor después de consultar los datos existentes y solicitar la aprobación de los canales. Puedes ejecutar este trabajo cuando se inicia la app y cuando esta necesite actualizar sus datos.

    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 URI exclusivas para todas las imágenes (logotipos, íconos, imágenes de contenido). Asegúrate de usar una URI diferente cuando actualices una imagen. Todas las imágenes se almacenan en caché. Si no modificas URI cuando cambias la imagen, se seguirá mostrando la imagen anterior.

  • Recuerda que las cláusulas WHERE no están permitidas y las llamadas a los proveedores con cláusulas WHERE generarán una excepción de seguridad.

Atributos

En esta sección, se describen los atributos de canal y de programa por separado.

Atributos de canal

Debes especificar los siguientes atributos para cada canal:

Atributo Notas
TIPO Se define en TYPE_PREVIEW.
DISPLAY_NAME Se define en el nombre del canal.
APP_LINK_INTENT_URI Cuando el usuario selecciona el logotipo del canal, el sistema envía un intent para iniciar una actividad que presenta el contenido relevante al canal. Define este atributo en la URI usada por el filtro de intent para esa actividad.

Además, un canal también tiene seis campos reservados para uso interno de la app. Estos campos se pueden usar para almacenar claves y otros valores que pueden ayudar a la app a asignar el canal a la estructura interna de datos:

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

Atributos de programa

Consulta las páginas individuales de los atributos para cada tipo de programa:

Código de ejemplo

Para obtener más información sobre cómo compilar apps que interactúan con la pantalla principal y agregar canales y programas a la pantalla principal de Android TV, consulta el codelab de la pantalla principal.