Ce guide explique comment intégrer la fonctionnalité "Reprendre la lecture" dans votre application Android TV à l'aide du SDK Engage.
Travail préalable
Suivez les instructions de la section "Travail préalable" du guide de démarrage.
Intégration
Créer des entités
Le SDK a défini différentes entités pour représenter chaque type d'élément. Le cluster "Continuation" accepte les entités suivantes :
Spécifiez les URI spécifiques à la plate-forme et les images d'affiche pour ces entités.
Créez également des URI de lecture pour chaque plate-forme (Android TV, Android ou iOS, par exemple), si ce n'est pas déjà fait. Ainsi, lorsqu'un utilisateur reprend la lecture sur chaque plate-forme, l'application utilise un URI de lecture ciblé pour lire le contenu vidéo.
// 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)
Les images d'affiche nécessitent un URI et des dimensions en pixels (hauteur et largeur). Ciblez différents facteurs de forme en fournissant plusieurs images d'affiche, mais vérifiez que toutes les images conservent un format 16:9 et une hauteur minimale de 200 pixels pour un affichage correct de l'entité "Reprendre la lecture", en particulier dans l'Entertainment Space de Google . Les images dont la hauteur est inférieure à 200 pixels peuvent ne pas s'afficher.
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
Cet exemple montre comment créer une MovieEntity avec tous les champs obligatoires :
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()
En fournissant des informations telles que les genres et les classifications de contenu, vous permettez à Google TV de présenter votre contenu de manière plus dynamique et de le mettre en relation avec les bons spectateurs.
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()
Les entités restent automatiquement disponibles pendant 60 jours, sauf si vous spécifiez une durée d'expiration plus courte. Ne définissez une expiration personnalisée que si vous avez besoin que l'entité soit supprimée avant cette période par défaut.
// 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
Cet exemple montre comment créer une TvEpisodeEntity avec tous les champs obligatoires :
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 chaîne du numéro d'épisode (par exemple, "2") et la chaîne du numéro de saison (par exemple, "1")
seront développées au format approprié avant d'être affichées sur la fiche "Reprendre la lecture". Notez qu'il doit s'agir d'une chaîne numérique. N'utilisez pas "e2", "épisode 2", "s1" ni "saison 1".
Si un programme TV particulier ne comporte qu'une seule saison, définissez le numéro de saison sur 1.
Pour maximiser les chances que les spectateurs trouvent votre contenu sur Google TV, envisagez de fournir des données supplémentaires telles que les genres, les classifications de contenu et les plages horaires de disponibilité, car ces informations peuvent améliorer les affichages et les options de filtrage.
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
Voici un exemple de création d'une VideoClipEntity avec tous les champs obligatoires.
VideoClipEntity représente un clip généré par l'utilisateur, comme une vidéo 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()
Vous pouvez éventuellement définir le créateur, l'image du créateur, l'heure de création en millisecondes ou la plage horaire de disponibilité .
LiveStreamingVideoEntity
Voici un exemple de création d'une LiveStreamingVideoEntity avec tous les champs obligatoires.
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()
Vous pouvez éventuellement définir l'heure de début, le diffuseur, l'icône du diffuseur ou la plage horaire de disponibilité pour l'entité de diffusion en direct.
Pour obtenir des informations détaillées sur les attributs et les exigences, consultez la documentation de référence de l'API.
Fournir des données de cluster "Continuation"
AppEngagePublishClient permet la publication du cluster "Continuation".
Vous utilisez la méthode publishContinuationCluste pour publier un objet ContinuationCluster.
Veillez à initialiser le client et à vérifier la disponibilité du service, comme décrit dans le guide de démarrage.
client.publishContinuationCluster(
PublishContinuationClusterRequest
.Builder()
.setContinuationCluster(
ContinuationCluster.Builder()
.setAccountProfile(accountProfile)
.addEntity(movieEntity1)
.addEntity(movieEntity2)
.addEntity(tvEpisodeEntity1)
.addEntity(tvEpisodeEntity2)
.setSyncAcrossDevices(true)
.build()
)
.build()
)
Lorsque le service reçoit la requête, les actions suivantes ont lieu dans une seule transaction :
- Les données
ContinuationClusterexistantes du développeur partenaire sont supprimées. - Les données de la requête sont analysées et stockées dans la
ContinuationClustermise à jour.
En cas d'erreur, la requête entière est rejetée, et l'état existant est maintenu.
Les API de publication sont des API upsert. Elles remplacent le contenu existant. Si vous devez mettre à jour une entité spécifique dans le cluster "Continuation", vous devrez publier à nouveau toutes les entités.
Les données de cluster "Continuation" ne doivent être fournies que pour les comptes adultes. Ne publiez que lorsque le profil de compte appartient à un adulte.
Synchronisation inter-appareils
setSyncAcrossDevices contrôle si les données ContinuationCluster d'un utilisateur sont synchronisées sur différents appareils tels que les téléviseurs, les téléphones, les tablettes, etc. La synchronisation inter-appareils est désactivée par défaut.
Valeurs :
true: les données de cluster "Continuation" sont partagées sur tous les appareils de l'utilisateur pour une expérience de visionnage fluide. Nous vous recommandons vivement cette option pour une expérience inter-appareils optimale.false: les données de cluster "Continuation" sont limitées à l'appareil actuel.
De plus, vous devez fournir un AccountProfile avec un ID de compte pour que le contenu soit synchronisé entre les appareils. Consultez
Créer un profil de compte.
Obtenir le consentement
L'application multimédia doit fournir un paramètre clair pour activer ou désactiver la synchronisation inter-appareils. Expliquez les avantages à l'utilisateur, stockez sa préférence une seule fois et appliquez-la en conséquence dans publishContinuationCluster.
// Example to allow cross device syncing.
client.publishContinuationCluster(
PublishContinuationClusterRequest
.Builder()
.setContinuationCluster(
ContinuationCluster.Builder()
.setAccountProfile(accountProfile)
.setSyncAcrossDevices(true)
.build()
)
.build()
)
Pour tirer le meilleur parti de notre fonctionnalité inter-appareils, vérifiez que l'application obtient le consentement utilisateur et activez SyncAcrossDevices sur true. Cela permet de synchroniser le contenu de manière transparente sur tous les appareils, ce qui améliore l'expérience utilisateur et augmente l'engagement. Par exemple, un partenaire qui a implémenté cette fonctionnalité a constaté une augmentation de 40 % des clics sur "Reprendre la lecture", car son contenu était diffusé sur plusieurs appareils.
Supprimer les données Engage
Pour supprimer manuellement les données d'un utilisateur du serveur Google TV avant la période de conservation standard de 60 jours, utilisez la méthode deleteClusters. Lors de la réception de la requête, le service supprime toutes les données Engage existantes pour le profil de compte ou pour l'ensemble du compte.
L'énumération DeleteReason définit la raison de la suppression des données.
Le code suivant supprime les données "Reprendre la lecture" lors de la déconnexion.
// 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()
)
Tests
Utilisez l'application de validation pour vérifier que l'intégration du SDK Engage fonctionne correctement.
Après avoir appelé l'API de publication, vérifiez que vos données sont correctement publiées en consultant l'application de validation. Votre cluster "Continuation" doit s'afficher sous forme de ligne distincte dans l'interface de l'application.
- Testez les actions suivantes dans votre application :
- Connectez-vous.
- Passez d'un profil à un autre(le cas échéant).
- Démarrez une vidéo, puis mettez-la en pause ou revenez à la page d'accueil.
- Fermez l'application pendant la lecture de la vidéo.
- Supprimez un élément de la ligne "Reprendre la lecture" (si cette fonctionnalité est disponible).
- Après chaque action, vérifiez que votre application a appelé l'API
publishContinuationClusterset que les données s'affichent correctement dans l'application de validation. L'application de validation affiche une coche verte "Tout va bien" pour les entités correctement implémentées.
Figure 1. Validation réussie dans l'application de validation L'application de validation signale toutes les entités problématiques.
Figure 2. Erreur dans l'application de validation Pour résoudre les problèmes liés aux entités, utilisez la télécommande de votre téléviseur pour sélectionner et cliquer sur l'entité dans l'application de validation. Les problèmes spécifiques s'affichent et sont mis en surbrillance en rouge pour que vous puissiez les examiner (voir l'exemple ci-dessous).
Figure 3. Détails de l'erreur dans l'application de validation