Seguir mirando aprovecha el clúster de Continuation para mostrar videos sin terminar y los próximos episodios de la misma temporada de TV desde varias apps en una sola agrupación de IU. Puedes mostrar sus entidades en este clúster de Continuation. Sigue esta guía para aprender a mejorar la participación del usuario con la experiencia de Seguir mirando mediante el SDK de Engage.
Paso 1: Trabajo previo
Antes de comenzar, completa los siguientes pasos:
Asegúrate de que tu app esté orientada al nivel de API 19 o uno posterior para esta integración.
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.
Dispositivo móvil
dependencies { implementation 'com.google.android.engage:engage-core:1.5.5 }
TV
dependencies { implementation 'com.google.android.engage:engage-tv:1.0.2 }
Establece el entorno de servicio de Engage en producción en el archivo
AndroidManifest.xml
.Dispositivo móvil
<meta-data android:name="com.google.android.engage.service.ENV" android:value="PRODUCTION"> </meta-data>
TV
<meta-data android:name="com.google.android.engage.service.ENV" android:value="PRODUCTION"> </meta-data>
Se agregó el permiso para
WRITE_EPG_DATA
para el APK de TV.<uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />
Usa un servicio en segundo plano, como
androidx.work
, para programar y garantizar una publicación de contenido confiable.Para proporcionar una experiencia de visualización fluida, publica datos de reproducción continua cuando ocurran estos eventos:
- Primer acceso: Cuando un usuario accede por primera vez, la publicación de sus datos garantiza que su historial de reproducciones esté disponible de inmediato.
- Creación o cambio de perfil (apps de varios perfiles): Si tu app admite varios perfiles, publica datos cuando un usuario crea o cambia de perfil. Esto garantiza que cada usuario tenga una experiencia personalizada.
- Interrupción de la reproducción de video: Para ayudar a los usuarios a retomar desde donde se detuvieron, publica datos cuando pausan o detienen un video, o cuando se cierra la app durante la reproducción.
- Actualizaciones de la bandeja Continuar mirando (si se admite): Cuando un usuario quita un elemento de su bandeja Continuar mirando, publica datos actualizados para reflejar ese cambio. Esto garantiza que la bandeja siga siendo relevante y personalizada.
- Finalización del video:
- En el caso de las películas, quita la película completada de la bandeja Seguir mirando. Si la película forma parte de una serie, agrega la siguiente para mantener al usuario activo.
- En el caso de los episodios, quita el episodio que se terminó de mirar y agrega el siguiente de la serie, si está disponible, para fomentar la visualización continua.
Integración
AccountProfile
Para permitir una experiencia personalizada de “Continuar reproduciendo” en Google TV, proporciona información de la cuenta y el perfil. Usa AccountProfile para proporcionar lo siguiente:
ID de la cuenta: Es un identificador único que representa la cuenta del usuario dentro de tu aplicación. Puede ser el ID de la cuenta real o una versión ofuscada de forma adecuada.
ID de perfil (opcional): Si tu aplicación admite varios perfiles dentro de una sola cuenta, proporciona un identificador único para el perfil de usuario específico (una vez más, real o ofuscado).
// If your app only supports account
val accountProfile = AccountProfile.Builder()
.setAccountId("your_users_account_id")
.build()
// If your app supports both account and profile
val accountProfile = AccountProfile.Builder()
.setAccountId("your_users_account_id")
.setProfileId("your_users_profile_id")
.build()
Crea entidades
El SDK definió distintas entidades para representar cada tipo de elemento. El clúster de Continuation admite las siguientes entidades:
Especifica los URIs y las imágenes de póster específicos de la plataforma para estas entidades.
Además, crea URIs de reproducción para cada plataforma, como Android TV, Android o iOS, si aún no lo hiciste. Por lo tanto, cuando un usuario continúa mirando contenido en cada plataforma, la app usa un URI de reproducción segmentada para reproducir el contenido de video.
// Required. Set this when you want continue watching entities to show up on
// Google TV
val playbackUriTv =
PlatformSpecificUri.Builder()
.setPlatformType(PlatformType.TYPE_ANDROID_TV)
.setActionUri(Uri.parse("https://www.example.com/entity_uri_for_tv"))
.build()
// Required. Set this when you want continue watching entities to show up on
// Google TV Android app, Entertainment Space, Playstore Widget
val playbackUriAndroid =
PlatformSpecificUri.Builder()
.setPlatformType(PlatformType.TYPE_ANDROID_MOBILE)
.setActionUri(Uri.parse("https://www.example.com/entity_uri_for_android"))
.build()
// Optional. Set this when you want continue watching entities to show up on
// Google TV iOS app
val playbackUriIos =
PlatformSpecificUri.Builder()
.setPlatformType(PlatformType.TYPE_IOS)
.setActionUri(Uri.parse("https://www.example.com/entity_uri_for_ios"))
.build()
val platformSpecificPlaybackUris =
Arrays.asList(playbackUriTv, playbackUriAndroid, playbackUriIos)
Las imágenes de póster requieren un URI y dimensiones de píxeles (altura y ancho). Proporciona varias imágenes de póster para segmentar diferentes factores de forma, pero asegúrate de que todas las imágenes mantengan una relación de aspecto de 16:9 y una altura mínima de 200 píxeles para que se muestre correctamente la entidad "Continuar viendo", especialmente en el Espacio de entretenimiento de Google. Es posible que no se muestren las imágenes con una altura inferior a 200 píxeles.
Image image1 = new Image.Builder()
.setImageUri(Uri.parse("http://www.example.com/entity_image1.png");)
.setImageHeightInPixel(300)
.setImageWidthInPixel(169)
.build()
Image image2 = new Image.Builder()
.setImageUri(Uri.parse("http://www.example.com/entity_image2.png");)
.setImageHeightInPixel(640)
.setImageWidthInPixel(360)
.build()
// And other images for different form factors.
val images = Arrays.asList(image1, image2)
MovieEntity
En este ejemplo, se muestra cómo crear un MovieEntity
con todos los campos obligatorios:
val movieEntity = MovieEntity.Builder()
.setWatchNextType(WatchNextType.TYPE_CONTINUE)
.setName("Movie name")
.addPlatformSpecificPlaybackUri(platformSpecificPlaybackUris)
.addPosterImages(images)
// Timestamp in millis for sample last engagement time 12/1/2023 00:00:00
.setLastEngagementTimeMillis(1701388800000)
// Suppose the duration is 2 hours, it is 72000000 in milliseconds
.setDurationMills(72000000)
// Suppose last playback offset is 1 hour, 36000000 in milliseconds
.setLastPlayBackPositionTimeMillis(36000000)
.build()
Proporcionar detalles como géneros y calificaciones del contenido le permite a Google TV mostrar tu contenido de formas más dinámicas y conectarlo con los usuarios adecuados.
val genres = Arrays.asList("Action", "Science fiction");
val rating1 = RatingSystem.Builder().setAgencyName("MPAA").setRating("PG-13").build();
val contentRatings = Arrays.asList(rating1);
val movieEntity = MovieEntity.Builder()
...
.addGenres(genres)
.addContentRatings(contentRatings)
.build()
Las entidades permanecen disponibles automáticamente durante 60 días, a menos que especifiques un tiempo de vencimiento más corto. Solo establece una fecha de vencimiento personalizada si necesitas que la entidad se quite antes de este período predeterminado.
// Set the expiration time to be now plus 30 days in milliseconds
val expirationTime = new DisplayTimeWindow.Builder()
.setEndTimestampMillis(now().toMillis()+2592000000).build()
val movieEntity = MovieEntity.Builder()
...
.addAvailabilityTimeWindow(expirationTime)
.build()
TvEpisodeEntity
En este ejemplo, se muestra cómo crear un TvEpisodeEntity
con todos los campos requeridos:
val tvEpisodeEntity = TvEpisodeEntity.Builder()
.setWatchNextType(WatchNextType.TYPE_CONTINUE)
.setName("Episode name")
.addPlatformSpecificPlaybackUri(platformSpecificPlaybackUris)
.addPosterImages(images)
// Timestamp in millis for sample last engagement time 12/1/2023 00:00:00
.setLastEngagementTimeMillis(1701388800000)
.setDurationMills(72000000) // 2 hours in milliseconds
// 45 minutes and 15 seconds in milliseconds is 2715000
.setLastPlayBackPositionTimeMillis(2715000)
.setEpisodeNumber("2")
.setSeasonNumber("1")
.setShowTitle("Title of the show")
.build();
La cadena del número de episodio (como "2"
) y la cadena del número de temporada (como "1"
) se expandirán al formato adecuado antes de mostrarse en la tarjeta para seguir mirando. Ten en cuenta que deben ser una cadena numérica. No escribas "e2", "episodio 2", "s1" ni "temporada 1".
Si un programa de TV en particular tiene una sola temporada, establece el número de temporada como 1.
Para maximizar las posibilidades de que los usuarios encuentren tu contenido en Google TV, considera proporcionar datos adicionales, como géneros, calificaciones del contenido y ventanas de disponibilidad, ya que estos detalles pueden mejorar las opciones de visualización y filtrado.
val genres = Arrays.asList("Action", "Science fiction")
val rating1 = RatingSystem.Builder().setAgencyName("MPAA").setRating("PG-13").build()
val contentRatings = Arrays.asList(rating1)
val tvEpisodeEntity = TvEpisodeEntity.Builder()
...
.addGenres(genres)
.addContentRatings(contentRatings)
.setSeasonTitle("Season Title")
.setShowTitle("Show Title)
.build();
VideoClipEntity
Este es un ejemplo de cómo crear un VideoClipEntity
con todos los campos obligatorios.
VideoClipEntity
representa un clip generado por el usuario, como un video de YouTube.
val videoClipEntity = VideoClipEntity.Builder()
.setPlaybackUri(Uri.parse("https://www.example.com/uri_for_current_platform")
.setWatchNextType(WatchNextType.TYPE_CONTINUE)
.setName("Video clip name")
.addPlatformSpecificPlaybackUri(platformSpecificPlaybackUris)
.addPosterImages(images)
// Timestamp in millis for sample last engagement time 12/1/2023 00:00:00
.setLastEngagementTimeMillis(1701388800000)
.setDurationMills(600000) //10 minutes in milliseconds
.setLastPlayBackPositionTimeMillis(300000) //5 minutes in milliseconds
.addContentRating(contentRating)
.build();
De manera opcional, puedes establecer el creador, la imagen del creador, la hora de creación en milisegundos o el período de disponibilidad .
LiveStreamingVideoEntity
Este es un ejemplo de cómo crear un LiveStreamingVideoEntity
con todos los campos obligatorios.
val liveStreamingVideoEntity = LiveStreamingVideoEntity.Builder()
.setPlaybackUri(Uri.parse("https://www.example.com/uri_for_current_platform")
.setWatchNextType(WatchNextType.TYPE_CONTINUE)
.setName("Live streaming name")
.addPlatformSpecificPlaybackUri(platformSpecificPlaybackUris)
.addPosterImages(images)
// Timestamp in millis for sample last engagement time 12/1/2023 00:00:00
.setLastEngagementTimeMillis(1701388800000)
.setDurationMills(72000000) //2 hours in milliseconds
.setLastPlayBackPositionTimeMillis(36000000) //1 hour in milliseconds
.addContentRating(contentRating)
.build();
De manera opcional, puedes establecer la hora de inicio, el emisor, el ícono del emisor o el período de disponibilidad de la entidad de transmisión en vivo.
Para obtener información detallada sobre los atributos y requisitos, consulta la referencia de la API.
Proporciona datos del clúster de Continuation
AppEngagePublishClient
es responsable de publicar el clúster de Continuation.
Usas el método publishContinuationCluster()
para publicar un objeto ContinuationCluster
.
Primero, debes usar isServiceAvailable() para verificar si el servicio está disponible para la integración.
client.publishContinuationCluster(
PublishContinuationClusterRequest
.Builder()
.setContinuationCluster(
ContinuationCluster
.Builder()
.setAccountProfile(accountProfile)
.addEntity(movieEntity1)
.addEntity(movieEntity2)
.addEntity(tvEpisodeEntity1)
.addEntity(tvEpisodeEntity2)
.setSyncAcrossDevices(true)
.build()
)
.build();
)
Cuando el servicio recibe la solicitud, se realizan las siguientes acciones en una transacción:
- Se quitan los datos existentes de
ContinuationCluster
del socio desarrollador. - Los datos de la solicitud se analizan y se almacenan en el
ContinuationCluster
actualizado.
En caso de error, se rechaza la solicitud completa y se mantiene el estado existente.
Las APIs de Publication son APIs de inserción y actualización. Reemplazan el contenido existente. Si necesitas actualizar una entidad específica en ContinuationCluster, deberás volver a publicar todas las entidades.
Los datos de ContinuationCluster solo deben proporcionarse para las cuentas de adultos. Publica solo cuando el AccountProfile pertenezca a un adulto.
Sincronización entre dispositivos
Marca SyncAcrossDevices
Esta marca controla si los datos de ContinuationCluster de un usuario se sincronizan en todos sus dispositivos (TV, teléfono, tablet, etcétera). La configuración predeterminada es "false", lo que significa que la sincronización entre dispositivos está inhabilitada de forma predeterminada.
Valores:
- true: Los datos de ContinuationCluster se comparten en todos los dispositivos del usuario para brindar una experiencia de visualización fluida. Recomendamos esta opción para obtener la mejor experiencia en varios dispositivos.
- false: Los datos de ContinuationCluster se restringen al dispositivo actual.
Obtén el consentimiento:
La aplicación multimedia debe proporcionar una configuración clara para habilitar o inhabilitar la sincronización entre dispositivos. Explica los beneficios al usuario y almacena su preferencia una vez y aplícala en publishContinuationCluster según corresponda.
// Example to allow cross device syncing.
client.publishContinuationCluster(
PublishContinuationClusterRequest
.Builder()
.setContinuationCluster(
ContinuationCluster
.Builder()
.setAccountProfile(accountProfile)
.setSyncAcrossDevices(true)
.build();
)
.build();
)
Para aprovechar al máximo nuestra función multidispositivo, asegúrate de que tu app obtenga el consentimiento del usuario y habilita SyncAcrossDevices como verdadero. Esto permite que el contenido se sincronice sin problemas en todos los dispositivos, lo que genera una mejor experiencia del usuario y una mayor participación. Por ejemplo, un socio que implementó esta función observó un aumento del 40% en los clics de “Continuar mirando” porque su contenido se mostró en varios dispositivos.
Borra los datos de descubrimiento de videos
Para borrar manualmente los datos de un usuario del servidor de Google TV antes del período de retención estándar de 60 días, usa el método client.deleteClusters(). Cuando reciba la solicitud, el servicio borrará todos los datos de descubrimiento de videos existentes del perfil de la cuenta o de toda la cuenta.
La enumeración DeleteReason
define el motivo de la eliminación de datos.
El siguiente código quita los datos de seguir mirando cuando se cierra la sesión.
// If the user logs out from your media app, you must make the following call
// to remove continue watching data from the current google TV device,
// otherwise, the continue watching data will persist on the current
// google TV device until 60 days later.
client.deleteClusters(
new DeleteClustersRequest.Builder()
.setAccountProfile(AccountProfile())
.setReason(DeleteReason.DELETE_REASON_USER_LOG_OUT)
.setSyncAcrossDevices(true)
.build()
)
Prueba
Usa la app de verificación para asegurarte de que la integración del SDK de Engage funcione correctamente. Esta aplicación para Android proporciona herramientas para ayudarte a verificar tus datos y confirmar que los intents de transmisión se manejen correctamente.
Después de invocar la API de publicación, verifica la app de verificación para confirmar que tus datos se publiquen correctamente. Tu clúster de Continuation debería mostrarse como una fila distinta dentro de la interfaz de la app.
- Asegúrate de que la marca de servicio de Engage NO esté configurada en producción en el archivo de manifiesto de Android de tu app.
- Instala y abre la app de Engage Verify.
- Si
isServiceAvailable
esfalse
, haz clic en el botón "Toggle" para habilitarlo. - Ingresa el nombre del paquete de la app para ver automáticamente los datos publicados una vez que comiences a publicarlos.
- Prueba estas acciones en tu app:
- Accede.
- Cambiar de perfil(si corresponde)
- Iniciar, pausar un video o volver a la página principal
- Cerrar la app durante la reproducción de un video
- Quita un elemento de la fila "Continuar mirando" (si es compatible).
- Después de cada acción, confirma que tu app invocó la API de publishContinuationClusters y que los datos se muestran correctamente en la app de verificación.
La app de verificación mostrará una marca de verificación verde "Todo bien" para las entidades implementadas correctamente.
Figura 1: Éxito de la app de verificación La app de verificación marcará las entidades problemáticas.
Figura 2: Error de la app de verificación Para solucionar problemas con entidades que tienen errores, usa el control remoto de la TV para seleccionar la entidad y haz clic en ella en la app de verificación. Los problemas específicos se mostrarán y destacarán en rojo para que los revises (consulta el ejemplo a continuación).
Figura 3: Detalles del error de la app de verificación