Intégrer "Continuer à regarder" sur Android TV

book_path: /distribute/other-docs/_book.yaml project_path: /distribute/other-docs/_project.yaml

Ce guide explique comment intégrer la fonctionnalité "Reprendre la lecture" à votre application Android TV à l'aide du SDK Engage.

Travail préalable

.

Suivez les instructions de la section Travaux préparatoires 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 affiches spécifiques à la plate-forme 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 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 de couverture nécessitent un URI et des dimensions en pixels (hauteur et largeur). Ciblez différents facteurs de forme en fournissant plusieurs affiches, mais vérifiez que toutes les images conservent un format 16:9 et une hauteur minimale de 200 pixels pour que l'entité "Reprendre la lecture" s'affiche correctement, en particulier dans l'Espace Divertissement de Google. Les images dont la hauteur est inférieure à 200 pixels ne seront peut-être pas diffusées.

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 requis :

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()

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 un TvEpisodeEntity avec tous les champs requis :

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 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 dans le 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 spécifique ne comporte qu'une seule saison, définissez le numéro de saison sur 1.

Pour maximiser les chances que les spectateurs trouvent vos contenus sur Google TV, pensez à fournir des données supplémentaires telles que les genres, les classifications de contenu et les périodes de disponibilité. 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 un 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)
    .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")
    .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()

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 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 sur le cluster "Continuation"

AppEngagePublishClient permet de publier le 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 ContinuationCluster existantes du développeur partenaire sont supprimées.
  • Les données de la requête sont analysées et stockées dans le 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 publier à nouveau toutes les entités.

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

Synchronisation inter-appareils

L'indicateur SyncAcrossDevices 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 multi-appareils est désactivée par défaut.

Valeurs :

  • true : les données du cluster de continuation sont partagées sur tous les appareils de l'utilisateur pour une expérience de visionnage fluide. Nous vous recommandons vivement de choisir cette option pour profiter d'une expérience optimale sur tous les appareils.
  • false : les données du cluster de continuation sont limitées à l'appareil actuel.

L'application multimédia doit proposer un paramètre clair permettant d'activer ou de désactiver la synchronisation multi-appareil. Explique les avantages à l'utilisateur, puis enregistre sa préférence une seule fois et applique-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é multi-appareils, vérifiez que l'application obtient le consentement de l'utilisateur et activez SyncAcrossDevices sur true. Cela permet de synchroniser facilement le contenu sur 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 enregistré une augmentation de 40 % des clics sur "Reprendre la lecture", car son contenu était diffusé sur plusieurs appareils.

Supprimer les données Discovery vidéo

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. Une fois la demande reçue, le service supprime toutes les données existantes de découverte de vidéos 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 "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 Publish, vérifiez que vos données sont correctement publiées en consultant l'application de validation. Votre cluster de poursuite devrait s'afficher sur une ligne distincte dans l'interface de l'application.

  • Testez ces actions dans votre application :
    • Connectez-vous.
    • Passez d'un profil à un autre(le cas échéant).
    • Démarrer, puis mettre en pause une vidéo, ou revenir à la page d'accueil
    • Fermez l'application pendant la lecture d'une vidéo.
    • Supprime un élément de la ligne "Reprendre la lecture" (si cette option est disponible).
  • 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 va bien" pour les entités correctement implémentées.

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

    Capture d'écran d'une 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 de votre téléviseur pour sélectionner l'entité dans l'application de validation, puis cliquez dessus. Les problèmes spécifiques s'affichent et sont mis en évidence en rouge pour que vous puissiez les examiner (voir l'exemple ci-dessous).

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