En esta guía, se explica cómo integrar la función Ver a continuación en tu app para Android TV con el SDK de Engage.
Trabajo previo
Completa las instrucciones de trabajo previo en la guía de introducción.
Integración
Crea entidades
El SDK definió distintas entidades para representar cada tipo de artículo. El clúster de Continuation admite las siguientes entidades:
Especifica los URI específicos de la plataforma y las imágenes de pósteres para estas entidades.
Además, crea URI de reproducción para cada plataforma, como Android TV, Android o iOS, si aún no lo hiciste. De esta manera, cuando un usuario sigue viendo contenido en cada plataforma, la app usa un URI de reproducción segmentado 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ósteres requieren un URI y dimensiones en píxeles (altura y ancho). Segmenta diferentes factores de forma proporcionando varias imágenes de pósteres, pero verifica 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 "Ver a continuación", en especial, dentro de Entertainment Space de Google. Es posible que no se muestren las imágenes con una altura inferior a 200 píxeles.
val images = Arrays.asList(
Image.Builder()
.setImageUri(Uri.parse("http://www.example.com/entity_image1.png"))
.setImageHeightInPixel(300)
.setImageWidthInPixel(169)
.build(),
Image.Builder()
.setImageUri(Uri.parse("http://www.example.com/entity_image2.png"))
.setImageHeightInPixel(640)
.setImageWidthInPixel(360)
.build()
// Consider adding other images for different form factors
)
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)
.setCallToActionText("Resume")
.addTag("Action")
.build()
Proporcionar detalles como géneros y clasificaciones de 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 un vencimiento personalizado si necesitas que se quite la entidad antes de este período predeterminado.
// Set the expiration time to be now plus 30 days in milliseconds
val expirationTime = 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 obligatorios:
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")
.setCallToActionText("Resume")
.addTag("Comedy")
.build()
La cadena de número de episodio (como "2") y la cadena de número de temporada (como "1")
se expandirán al formulario adecuado antes de mostrarse en la tarjeta Ver a continuación. Ten en cuenta que deben ser una cadena numérica. No ingreses "e2", "episode 2", "s1" ni "season 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, clasificaciones de contenido y períodos de disponibilidad, ya que estos detalles pueden mejorar las pantallas y las opciones de 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)
.setCallToActionText("Resume")
.addTag("Vlog")
.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)
.setCallToActionText("Resume")
.addTag("Live")
.build()
De manera opcional, puedes establecer la hora de inicio, el emisor, el ícono del emisor o el período de disponibilidad para la entidad de transmisión en vivo.
Para obtener información detallada sobre los atributos y los 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 publishContinuationCluste para publicar un objeto ContinuationCluster.
Asegúrate de inicializar el cliente y verificar la disponibilidad del servicio como se describe en la guía de introducció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
ContinuationClusterdel socio desarrollador. - Los datos de la solicitud se analizan y almacenan en el
ContinuationClusteractualizado.
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 el clúster de Continuation, deberás volver a publicar todas las entidades.
Los datos del clúster de Continuation solo se deben proporcionar para cuentas de adultos. Publica solo cuando el perfil de la cuenta pertenezca a un adulto.
Sincronización multidispositivo
setSyncAcrossDevices controla si los datos ContinuationCluster de un usuario se sincronizan en dispositivos como TV, teléfonos, tablets, etcétera. La sincronización multidispositivo está inhabilitada de forma predeterminada.
Valores:
true: Los datos del clúster de Continuation se comparten en todos los dispositivos del usuario para brindar una experiencia de visualización fluida. Recomendamos encarecidamente esta opción para obtener la mejor experiencia multidispositivo.false: Los datos del clúster de Continuation se restringen al dispositivo actual.
Además, debes proporcionar un AccountProfile con un ID de cuenta para que el contenido se sincronice entre dispositivos. Consulta
Crea un perfil de cuenta.
Obtén el consentimiento
La aplicación de medios debe proporcionar una configuración clara para habilitar o inhabilitar la sincronización multidispositivo. Explícale los beneficios al usuario, 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, verifica que la app obtenga el consentimiento del usuario y habilita SyncAcrossDevices en true. Esto permite que el contenido se sincronice sin problemas entre dispositivos, lo que genera una mejor experiencia del usuario y un mayor participación. Por ejemplo, un socio que implementó esto observó un aumento del 40% en los clics de "Ver a continuación" porque su contenido apareció en varios dispositivos.
Borra los datos de Engage
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 deleteClusters. Cuando reciba la solicitud, el servicio borrará todos los datos existentes de Engage para el perfil de la cuenta o para toda la cuenta.
La enumeración DeleteReason define el motivo de la eliminación de datos.
El siguiente código quita los datos de Ver a continuación 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(
DeleteClustersRequest.Builder()
.setAccountProfile(AccountProfile())
.setReason(DeleteReason.DELETE_REASON_USER_LOG_OUT)
.setSyncAcrossDevices(true)
.build()
)
Prueba
Usa la app de verificación para verificar que la integración del SDK de Engage funcione correctamente.
Después de invocar la API de Publication, verifica que tus datos se publiquen correctamente. Para ello, consulta la app de verificación. Tu clúster de Continuation debe mostrarse como una fila distinta dentro de la interfaz de la app.
- Prueba estas acciones en tu app:
- Accede.
- Cambia entre perfiles(si corresponde).
- Inicia y, luego, pausa un video, o bien vuelve a la página principal.
- Cierra la app durante la reproducción de video.
- Quita un elemento de la fila "Ver a continuación" (si es compatible).
- Después de cada acción, confirma que tu app invocó la API de
publishContinuationClustersy que los datos se muestran correctamente en la app de verificación. La app de verificación muestra 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á cualquier entidad problemática.
Figura 2. Error de la app de verificación Para solucionar problemas con entidades con errores, usa el control remoto de la TV para seleccionar y hacer clic en la entidad 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