Intégrer "Continuer à regarder" sur Android TV

Ce guide explique comment intégrer la fonctionnalité Continuer à regarder dans votre application Android TV à l'aide du SDK Engage.

Travail préalable

Suivez les instructions de la section Préparation 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 de continuation accepte les entités suivantes :

  1. MovieEntity
  2. TvEpisodeEntity
  3. LiveStreamingVideoEntity
  4. VideoClipEntity

Spécifiez les URI et les images d'affiche spécifiques à la plate-forme pour ces entités.

Si vous ne l'avez pas déjà fait, créez également des URI de lecture pour chaque plate-forme, comme Android TV, Android ou iOS. Ainsi, lorsqu'un utilisateur continue de regarder une vidéo 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 de 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 d'une hauteur inférieure à 200 pixels risquent de 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 un MovieEntity avec tous les champs obligatoires :

val movieEntity = MovieEntity.Builder()
   .setWatchNextType(WatchNextType.TYPE_CONTINUE)
   .setName("Movie name")
   .addPlatformSpecificPlaybackUris(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
   .setDurationMillis(72000000)
   // Suppose last playback offset is 1 hour, 36000000 in milliseconds
   .setLastPlayBackPositionTimeMillis(36000000)
   .setCallToActionText("Resume")
   .addTag("Action")
   .build()

En fournissant des détails tels 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 proposer aux spectateurs appropriés.

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 un délai d'expiration plus court. 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 un TvEpisodeEntity avec tous les champs obligatoires :

val tvEpisodeEntity = TvEpisodeEntity.Builder()
    .setWatchNextType(WatchNextType.TYPE_CONTINUE)
    .setName("Episode name")
    .addPlatformSpecificPlaybackUris(platformSpecificPlaybackUris)
    .addPosterImages(images)
    // Timestamp in millis for sample last engagement time 12/1/2023 00:00:00
    .setLastEngagementTimeMillis(1701388800000)
    .setDurationMillis(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'indiquez pas "e2", "épisode 2", "s1" ou "saison 1".

Si une série TV 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, pensez à fournir des données supplémentaires telles que les genres, les classifications de contenu et les périodes de disponibilité, car ces informations peuvent améliorer l'affichage 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'un VideoClipEntity avec tous les champs obligatoires.

VideoClipEntity représente un extrait 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")
    .addPlatformSpecificPlaybackUris(platformSpecificPlaybackUris)
    .addPosterImages(images)
    // Timestamp in millis for sample last engagement time 12/1/2023 00:00:00
    .setLastEngagementTimeMillis(1701388800000)
    .setDurationMillis(600000) //10 minutes in milliseconds
    .setLastPlayBackPositionTimeMillis(300000) //5 minutes in milliseconds
    .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 période de disponibilité .

LiveStreamingVideoEntity

Voici un exemple de création d'un 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")
    .addPlatformSpecificPlaybackUris(platformSpecificPlaybackUris)
    .addPosterImages(images)
    // Timestamp in millis for sample last engagement time 12/1/2023 00:00:00
    .setLastEngagementTimeMillis(1701388800000)
    .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 période de disponibilité de l'entité de diffusion en direct.

Pour en savoir plus sur les attributs et les exigences, consultez la documentation de référence de l'API.

Fournir des données de cluster de continuation

AppEngagePublishClient est responsable de la publication du cluster "Continuation". Vous utilisez la méthode publishContinuationCluster 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 ContinuationCluster existantes du développeur partenaire sont supprimées.
  • Les données de la requête sont analysées et stockées dans le cluster ContinuationCluster mis à 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 de continuation, vous devrez republier toutes les entités.

Les données de cluster de continuation ne doivent être fournies que pour les comptes d'adultes. Publiez uniquement lorsque le profil du compte appartient à un adulte.

Synchronisation inter-appareil

setSyncAcrossDevices détermine si les données ContinuationCluster d'un utilisateur sont synchronisées sur plusieurs appareils, tels que des téléviseurs, des téléphones, des tablettes, etc. La synchronisation multi-appareil est désactivée par défaut.

Valeurs :

  • true : les données du cluster de reprise 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 optimale sur tous les appareils.
  • false : les données du cluster de 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.

L'application multimédia doit fournir un paramètre clair pour activer ou désactiver la synchronisation entre appareils. Expliquez les avantages à l'utilisateur, stockez sa préférence une fois et appliquez-la dans publishContinuationCluster en conséquence.

// Example to allow cross device syncing.
client.publishContinuationCluster(
    PublishContinuationClusterRequest
        .Builder()
        .setContinuationCluster(
            ContinuationCluster.Builder()
                .setAccountProfile(accountProfile)
                .setSyncAcrossDevices(true)
                .build()
        )
        .build()
)

Pour profiter pleinement de notre fonctionnalité inter-appareils, vérifiez que l'application obtient le consentement utilisateur et activez SyncAcrossDevices sur true. Cela permet de synchroniser facilement le contenu sur tous les appareils, ce qui améliore l'expérience utilisateur et l'engagement. Par exemple, un partenaire qui a implémenté cette fonctionnalité a constaté une augmentation de 40 % des clics sur "Continuer à regarder", car son contenu était proposé 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. À la réception de la requête, le service supprime toutes les données Engage existantes pour le profil du 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 de reprise de 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)
        .setDeleteReason(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 de continuation doit s'afficher sur une ligne distincte dans l'interface de l'application.

  • Testez ces actions dans votre application :
    • Connectez-vous.
    • Passer d'un profil à un autre(le cas échéant)
    • Lancez une vidéo, puis mettez-la en pause, ou revenez à la page d'accueil.
    • Fermez l'application pendant la lecture d'une vidéo.
    • Supprimer un élément de la ligne "Continuer à regarder" (si possible)
  • Après chaque action, vérifiez que votre application a appelé l'API publishContinuationClusters et que les données s'affichent correctement dans l'application de validation.
  • L'application de validation affiche une coche verte "Tout est OK" pour les entités correctement implémentées.

    Capture d'écran de l'application de validation (réussite)
    Figure 1. Validation de l'application réussie
  • L'application de validation signalera toutes les entités problématiques.

    Capture d'écran de l'erreur de l'application de validation
    Figure 2. Erreur de l'application de validation
  • Pour résoudre les problèmes liés aux entités comportant des erreurs, utilisez la télécommande TV de votre téléviseur pour sélectionner l'entité et cliquer dessus dans l'application de validation. Les problèmes spécifiques s'afficheront et seront mis en évidence en rouge pour que vous puissiez les examiner (voir l'exemple ci-dessous).

    Détails de l'erreur de l'application de validation
    Figure 3. Détails sur les erreurs de l'application de validation