Cómo compartir derechos de apps con Google TV con el SDK de Engage

Esta guía contiene instrucciones para que los desarrolladores compartan datos de suscripción y derechos de la app con Google TV mediante el SDK de Engage. Los usuarios pueden encontrar el contenido al que tienen derecho y permitir que Google TV les brinde recomendaciones de contenido altamente relevantes directamente en las experiencias de Google TV en TVs, dispositivos móviles y tablets.

Requisitos previos

Debes integrar el feed de acciones multimedia para poder usar la API de derechos del dispositivo. Si aún no lo hiciste, completa el proceso de integración del feed de acciones de medios.

Trabajo previo

Antes de comenzar, completa los siguientes pasos: Verifica que tu app esté orientada al nivel de API 19 o superior para esta integración.

  1. Agrega la biblioteca com.google.android.engage a tu app:

    Existen SDKs independientes para usar en la integración: uno para apps para dispositivos móviles y otro para apps para TV.

    Para dispositivos móviles

    
      dependencies {
        implementation 'com.google.android.engage:engage-core:1.5.5
      }
    

    para TV

    
      dependencies {
        implementation 'com.google.android.engage:engage-tv:1.0.2
      }
    
  2. Establece el entorno de servicio de Engage en producción en el archivo AndroidManifest.xml.

    Para el APK para dispositivos móviles

    
    <meta-data
          android:name="com.google.android.engage.service.ENV"
          android:value="PRODUCTION">
    </meta-data>
    

    APK para TV

    
    <meta-data
        android:name="com.google.android.engage.service.ENV"
        android:value="PRODUCTION">
    </meta-data>
    
  3. Antes de enviar el APK a Google, configura el entorno de servicio de participación en producción en tu archivo AndroidManifest.xml. Para obtener un rendimiento óptimo y compatibilidad en el futuro, publica datos solo cuando la app esté en primer plano y el usuario interactúe con ella de forma activa, como cuando se inicia la app, después de acceder o durante el uso activo. No se recomienda publicar desde procesos en segundo plano.

  4. Publica información de suscripción en los siguientes eventos:

    1. El usuario accede a tu app.
    2. El usuario cambia entre perfiles (si se admiten).
    3. El usuario compra una suscripción nueva.
    4. El usuario actualiza una suscripción existente.
    5. Vence la suscripción del usuario.

Integración

En esta sección, se proporcionan los ejemplos de código y las instrucciones necesarios para implementar AccountProfile y SubscriptionEntity para administrar varios tipos de suscripciones.

Cuenta y perfil de usuario

Para permitir funciones personalizadas en Google TV, proporciona información de la cuenta. Usa AccountProfile para proporcionar lo siguiente:

  1. ID de la cuenta: Es un identificador único que representa la cuenta del usuario. Puede ser el ID de la cuenta real o una versión ofuscada de forma adecuada.
// Set the account ID to which the subscription applies.
// Don't set the profile ID because subscription applies to account level.
val accountProfile = AccountProfile.Builder()
  .setAccountId("user_account_id")
  .setProfileId("user_profile id")
  .build();

Suscripción de nivel común

En el caso de los usuarios con suscripciones básicas a servicios de proveedores de contenido multimedia, por ejemplo, un servicio que tiene un nivel de suscripción que otorga acceso a todo el contenido pagado, proporciona estos detalles esenciales:

  1. Tipo de suscripción: Indica claramente el plan de suscripción específico que tiene el usuario.

    1. SUBSCRIPTION_TYPE_ACTIVE: El usuario tiene una suscripción pagada activa.
    2. SUBSCRIPTION_TYPE_ACTIVE_TRIAL: El usuario tiene una suscripción de prueba.
    3. SUBSCRIPTION_TYPE_INACTIVE: El usuario tiene una cuenta, pero no tiene una suscripción ni una prueba activas.
  2. Tiempo de vencimiento: Es un tiempo opcional en milisegundos. Especifica cuándo vencerá la suscripción.

  3. Nombre del paquete del proveedor: Especifica el nombre del paquete de la app que controla la suscripción.

Ejemplo del feed de proveedor de contenido multimedia de ejemplo

"actionAccessibilityRequirement": [
  {
    "@type": "ActionAccessSpecification",
    "category": "subscription",
    "availabilityStarts": "2022-06-01T07:00:00Z",
    "availabilityEnds": "2026-05-31T07:00:00Z",
    "requiresSubscription": {
    "@type": "MediaSubscription",
    // Don't match this string,
    // ID is only used to for reconciliation purpose
    "@id": "https://www.example.com/971bfc78-d13a-4419",
    // Don't match this, as name is only used for displaying purpose
    "name": "Basic common name",
    "commonTier": true
  }

En el siguiente ejemplo, se crea un SubscriptionEntity para un usuario:

val subscription = SubscriptionEntity
  .Builder()
  setSubscriptionType(
    SubscriptionType.SUBSCRIPTION_TYPE_ACTIVE
  )
  .setProviderPackageName("com.google.android.example")
  // Optional
  // December 30, 2025 12:00:00AM in milliseconds since epoch
  .setExpirationTimeMillis(1767052800000)
  .build();

Suscripción Premium

Si la app ofrece paquetes de suscripción premium de varios niveles, que incluyen contenido o funciones expandidos más allá del nivel común, agrega uno o más derechos a Subscription.

Este derecho tiene los siguientes campos:

  1. Identificador: Es la cadena de identificador obligatoria para este derecho. Debe coincidir con uno de los identificadores de derechos (ten en cuenta que no es el campo de ID) que se proporciona en el feed del proveedor de contenido multimedia publicado en Google TV.
  2. Nombre: Esta es información auxiliar y se usa para la coincidencia de derechos. Si bien es opcional, proporcionar un nombre de derecho legible por humanos mejora la comprensión de los derechos de los usuarios para los desarrolladores y los equipos de asistencia al cliente. Por ejemplo, Sling Orange.
  3. Expiration TimeMillis: De manera opcional, especifica el tiempo de vencimiento en milisegundos para este derecho, si difiere del tiempo de vencimiento de la suscripción. De forma predeterminada, el derecho vencerá cuando venza la suscripción.

En el siguiente fragmento de feed de proveedor de contenido multimedia de ejemplo:

"actionAccessibilityRequirement": [
  {
    "@type": "ActionAccessSpecification",
    "category": "subscription",
    "availabilityStarts": "2022-06-01T07:00:00Z",
    "availabilityEnds": "2026-05-31T07:00:00Z",
    "requiresSubscription": {
    "@type": "MediaSubscription",
    // Don't match this string,
    // ID is only used to for reconciliation purpose
    "@id": "https://www.example.com/971bfc78-d13a-4419",

    // Don't match this, as name is only used for displaying purpose
    "name": "Example entitlement name",
    "commonTier": false,
    // match this identifier in your API. This is the crucial
    // entitlement identifier used for recommendation purpose.
    "identifier": "example.com:entitlementString1"
  }

En el siguiente ejemplo, se crea un SubscriptionEntity para un usuario suscrito:

// Subscription with entitlements.
// The entitlement expires at the same time as its subscription.
val subscription = SubscriptionEntity
  .Builder()
  .setSubscriptionType(
    SubscriptionType.SUBSCRIPTION_TYPE_ACTIVE
  )
  .setProviderPackageName("com.google.android.example")
  // Optional
  // December 30, 2025 12:00:00AM in milliseconds
  .setExpirationTimeMillis(1767052800000)
  .addEntitlement(
    SubscriptionEntitlement.Builder()
    // matches with the identifier in media provider feed
    .setEntitlementId("example.com:entitlementString1")
    .setDisplayName("entitlement name1")
    .build()
  )
  .build();
// Subscription with entitlements
// The entitement has different expiration time from its subscription
val subscription = SubscriptionEntity
  .Builder()
  .setSubscriptionType(
    SubscriptionType.SUBSCRIPTION_TYPE_ACTIVE
  )
  .setProviderPackageName("com.google.android.example")
  // Optional
  // December 30, 2025 12:00:00AM in milliseconds
  .setExpirationTimeMillis(1767052800000)
  .addEntitlement(
    SubscriptionEntitlement.Builder()
    .setEntitlementId("example.com:entitlementString1")
    .setDisplayName("entitlement name1")
    // You may set the expiration time for entitlement
    // December 15, 2025 10:00:00 AM in milliseconds
    .setExpirationTimeMillis(1765792800000)
    .build())
  .build();

Suscripción al paquete de servicios vinculado

Si bien las suscripciones suelen pertenecer al proveedor de contenido multimedia de la app de origen, se puede atribuir una suscripción a un paquete de servicio vinculado si se especifica el nombre del paquete de servicio vinculado dentro de la suscripción.

En el siguiente ejemplo de código, se muestra cómo crear una suscripción de usuario.

// Subscription for linked service package
val subscription = SubscriptionEntity
  .Builder()
  .setSubscriptionType(
    SubscriptionType.SUBSCRIPTION_TYPE_ACTIVE
  )
  .setProviderPackageName("com.google.android.example")
  // Optional
  // December 30, 2025 12:00:00AM in milliseconds since epoch
  .setExpirationTimeMillis(1767052800000)
  .build();

Además, si el usuario tiene otra suscripción a un servicio subsidiario, agrega otra suscripción y establece el nombre del paquete del servicio vinculado según corresponda.

// Subscription for linked service package
val linkedSubscription = Subscription
  .Builder()
  .setSubscriptionType(
    SubscriptionType.SUBSCRIPTION_TYPE_ACTIVE
  )
  .setProviderPackageName("linked service package name")
  // Optional
  // December 30, 2025 12:00:00AM in milliseconds since epoch
  .setExpirationTimeMillis(1767052800000)
  .addBundledSubscription(
    BundledSubscription.Builder()
      .setBundledSubscriptionProviderPackageName(
        "bundled-subscription-package-name"
      )
      .setSubscriptionType(SubscriptionType.SUBSCRIPTION_TYPE_ACTIVE)
      .setExpirationTimeMillis(111)
      .addEntitlement(
        SubscriptionEntitlement.Builder()
        .setExpirationTimeMillis(111)
        .setDisplayName("Silver subscription")
        .setEntitlementId("subscription.tier.platinum")
        .build()
      )
      .build()
  )
    .build();

De forma opcional, también puedes agregar derechos a una suscripción de servicio vinculada.

Proporciona un conjunto de suscripciones

Ejecuta el trabajo de publicación de contenido mientras la app está en primer plano.

Usa el método publishSubscriptionCluster(), de la clase AppEngagePublishClient, para publicar un objeto SubscriptionCluster.

Usa isServiceAvailable para verificar si el servicio está disponible para la integración.

client.publishSubscription(
  PublishSubscriptionRequest.Builder()
    .setAccountProfile(accountProfile)
    .setSubscription(subscription)
    .build();
  )

Usa setSubscription() para verificar que el usuario solo debe tener una suscripción al servicio.

Usa addLinkedSubscription() o addLinkedSubscriptions(), que aceptan una lista de suscripciones vinculadas, para permitir que el usuario tenga cero o más suscripciones vinculadas.

Cuando el servicio recibe la solicitud, se crea una entrada nueva y se borra automáticamente la anterior después de 60 días. El sistema siempre usa la entrada más reciente. En caso de error, se rechaza la solicitud completa y se mantiene el estado existente.

Mantén la suscripción actualizada

  1. Para proporcionar actualizaciones inmediatas cuando se produzcan cambios, llama a publishSubscriptionCluster() cada vez que cambie el estado de suscripción de un usuario, como la activación, desactivación, actualización o cambio a una versión inferior.
  2. Para proporcionar una validación periódica de la precisión continua, llama a publishSubscriptionCluster() al menos una vez al mes.

  3. Para borrar los datos de descubrimiento de videos, borra manualmente los datos de un usuario del servidor de Google TV antes del período de retención estándar de 60 días. Para ello, usa el método client.deleteClusters(). De esta manera, se borran todos los datos de descubrimiento de videos existentes del perfil de la cuenta o de la cuenta completa, según el DeleteReason determinado.

    Fragmento de código para quitar la suscripción del usuario

      // If the user logs out from your media app, you must make the following call
      // to remove subscription and other video discovery data from the current
      // google TV device.
      client.deleteClusters(
        new DeleteClustersRequest.Builder()
          .setAccountProfile(
            AccountProfile
              .Builder()
              .setAccountId()
              .setProfileId()
              .build()
          )
        .setReason(DeleteReason.DELETE_REASON_USER_LOG_OUT)
        .build()
        )
      ```
    Following code snippet demonstrates removal of user subscription
    when user revokes the consent.
    
    ```Kotlin
      // If the user revokes the consent to share across device, make the call
      // to remove subscription and other video discovery data from all google
      // TV devices.
      client.deleteClusters(
        new DeleteClustersRequest.Builder()
          .setAccountProfile(
            AccountProfile
            .Builder()
            .setAccountId()
            .setProfileId()
            .build()
          )
          .setReason(DeleteReason.DELETE_REASON_LOSS_OF_CONSENT)
          .build()
      )
      ```
    
    Following code demonstrates how to remove subscription data on user profile
    deletion.
    
    ```Kotlin
    // If the user delete a specific profile, you must make the following call
    // to remove subscription data and other video discovery data.
    client.deleteClusters(
      new DeleteClustersRequest.Builder()
      .setAccountProfile(
        AccountProfile
        .Builder()
        .setAccountId()
        .setProfileId()
        .build()
      )
      .setReason(DeleteReason.DELETE_REASON_ACCOUNT_PROFILE_DELETION)
      .build()
    )
    

Prueba

En esta sección, se proporciona una guía paso a paso para probar la implementación de la suscripción. Verifica la exactitud de los datos y la funcionalidad adecuada antes del lanzamiento.

Lista de tareas para publicar la integración

  1. La publicación debe ocurrir cuando la app está en primer plano y el usuario interactúa con ella de forma activa.

  2. Publicar cuando:

    • El usuario accede por primera vez.
    • El usuario cambia de perfil (si se admiten perfiles).
    • El usuario compra una suscripción nueva.
    • El usuario actualiza la suscripción.
    • Vence la suscripción del usuario.
  3. Comprueba si la app llama correctamente a las APIs de isServiceAvailable() y publishClusters() en Logcat, en los eventos de publicación.

  4. Verifica que los datos sean visibles en la app de verificación. Esta debe mostrar la suscripción como una fila independiente. Cuando se invoque la API de publicación, los datos deberían aparecer en la app de verificación.

    • Verifica que la marca de servicio de Engage NO esté configurada en producción en el archivo de manifiesto de Android de la app.
    • Instala y abre la app de verificación de Engage.
    • Si el valor de isServiceAvailable es false en la app de verificación, haz clic en el botón Toggle dentro de la app de verificación para configurarlo como true.
    • Ingresa el nombre del paquete de la app. Se mostrarán automáticamente los datos publicados.
  5. Ve a la app y realiza cada una de las siguientes acciones:

    • Accede.
    • cambiar de perfil (si es compatible).
    • Compra una suscripción nueva.
    • Actualiza una suscripción existente.
    • Vencer la suscripción

Verifica la integración

Para probar tu integración, usa la app de verificación.

La app de verificación es una aplicación para Android que los desarrolladores pueden usar para verificar que la integración funcione. La app incluye capabilities que ayudan a los desarrolladores a verificar los datos y los intents de transmisión. Ayuda a verificar la precisión de los datos y la funcionalidad adecuada antes del lanzamiento.

  1. Para cada uno de los eventos, verifica si la app invocó la API de publishSubscription. Verifica los datos publicados en la app de verificación. Verifica que todo esté en verde en la app de verificación
  2. Si toda la información de la entidad es correcta, aparecerá una marca de verificación verde que dice “Todo está bien” en todas las entidades.

    Captura de pantalla de la verificación de la app que indica que se realizó correctamente
    Figura 1: Suscripción correcta
  3. Los problemas también se destacan en la app de verificación

    Captura de pantalla del error de la app de verificación
    Figura 2: La suscripción no se realizó correctamente
  4. Para ver los problemas de la suscripción del paquete, usa el control remoto de la TV para enfocarte en esa suscripción específica y haz clic para verlos. Es posible que primero debas enfocarte en la fila y moverte hacia la derecha para encontrar la tarjeta de suscripción en paquete. Los problemas se destacan en rojo, como se muestra en la Figura 3. Además, usa el control remoto para desplazarte hacia abajo y ver los problemas en los derechos dentro de la suscripción del paquete.

    Captura de pantalla de los detalles del error de la app de verificación
    Figura 3: Errores de suscripción
  5. Para ver los problemas en el derecho, usa el control remoto de la TV para enfocarte en ese derecho específico y haz clic para verlos. Los problemas se destacan en rojo.

    Captura de pantalla del error de la app de verificación
    Figura 4: Detalles del error de suscripción