Guide d'intégration du SDK Engage pour la TV

La fonctionnalité Continuer à regarder utilise le cluster Continuation pour afficher les vidéos inachevées et les prochains épisodes à regarder de la même saison télévisée, à partir de plusieurs applications dans un même groupe d'UI. Vous pouvez présenter leurs entités dans ce cluster de suite. Suivez ce guide pour découvrir comment améliorer l'engagement utilisateur grâce à l'expérience "Continuer à regarder" à l'aide du SDK Engage.

Étape 1: Effectuer le travail préparatoire

Avant de commencer, procédez comme suit:

Assurez-vous que votre application cible le niveau d'API 19 ou supérieur pour cette intégration.

  1. Ajoutez la bibliothèque com.google.android.engage à votre application :

    Des SDK distincts doivent être utilisés pour l'intégration: un pour les applications mobiles et un pour les applications TV.

    Mobile

    
      dependencies {
        implementation 'com.google.android.engage:engage-core:1.5.5
      }
    

    TV

    
      dependencies {
        implementation 'com.google.android.engage:engage-tv:1.0.2
      }
    
  2. Définissez l'environnement de service Engage sur "production" dans le fichier AndroidManifest.xml.

    Mobile

    
    <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>
    
  3. Ajouter une autorisation pour WRITE_EPG_DATA pour l'APK TV

      <uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />
    
  4. Assurez-vous d'une publication fiable du contenu en utilisant un service en arrière-plan, tel que androidx.work, pour la planification.

  5. Pour offrir une expérience de visionnage fluide, publiez des données de poursuite de la lecture lorsque ces événements se produisent:

    1. Première connexion: lorsqu'un utilisateur se connecte pour la première fois, la publication de ses données garantit que son historique de visionnage est immédiatement disponible.
    2. Création ou changement de profil (applications multiprofils): si votre application prend en charge plusieurs profils, publiez des données lorsqu'un utilisateur crée ou change de profil. Cela garantit une expérience personnalisée à chaque utilisateur.
    3. Interruption de la lecture vidéo: pour aider les utilisateurs à reprendre là où ils s'étaient arrêtés, publiez des données lorsqu'ils mettent en pause ou arrêtent une vidéo, ou lorsqu'ils quittent l'application pendant la lecture.
    4. Mises à jour de la section "À regarder plus tard" (si prise en charge): lorsqu'un utilisateur supprime un élément de sa section "À regarder plus tard", reflétez ce changement en publiant des données mises à jour. Cela garantit que le bac reste pertinent et personnalisé.
    5. Fin de la vidéo :
      1. Pour les films, supprimez le film terminé de la section "Continuer à regarder". Si le film fait partie d'une série, ajoutez le film suivant pour maintenir l'engagement de l'utilisateur.
      2. Pour les épisodes, supprimez l'épisode terminé et ajoutez le prochain épisode de la série, le cas échéant, pour encourager la poursuite du visionnage.

Intégration

AccountProfile

Pour personnaliser l'expérience "Continuer à regarder" sur Google TV, fournissez des informations sur votre compte et votre profil. Utilisez AccountProfile pour fournir les éléments suivants:

  1. ID de compte: identifiant unique représentant le compte de l'utilisateur dans votre application. Il peut s'agir de l'ID de compte réel ou d'une version obscurcie de manière appropriée.

  2. ID de profil (facultatif): si votre application prend en charge plusieurs profils dans un même compte, fournissez un identifiant unique pour le profil utilisateur spécifique (à nouveau, réel ou masqué).

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

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 est compatible avec les entités suivantes:

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

Spécifiez les URI et les images de l'affiche propres à la plate-forme pour ces entités.

Si ce n'est pas déjà fait, créez également des URI de lecture pour chaque plate-forme (Android TV, Android ou iOS, par exemple). 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 l'affiche nécessitent un URI et des dimensions en pixels (hauteur et largeur). Ciblez différents facteurs de forme en fournissant plusieurs images de couverture, mais assurez-vous que toutes les images conservent un format 16:9 et une hauteur minimale de 200 pixels pour un affichage correct de l'entité "Continuer à regarder", en particulier dans l'espace de divertissement de Google. Les images dont la hauteur est inférieure à 200 pixels ne sont pas nécessairement affichées.


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

Cet exemple montre comment créer un 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)
   .build()

En fournissant des informations telles que les genres et les classifications de contenu, vous permettez à Google TV de présenter vos contenus de manière plus dynamique et de les mettre en relation avec les 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 une durée d'expiration plus courte. Ne définissez une date d'expiration personnalisée que si vous devez supprimer l'entité avant cette période par défaut.

// 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

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 de numéro d'épisode (par exemple, "2") et la chaîne de numéro de saison (par exemple, "1") seront développées dans la forme appropriée avant d'être affichées sur la fiche de lecture en cours. 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 vos contenus sur Google TV, envisagez de 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 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'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")
    .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 streaming 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 "Continuation"

AppEngagePublishClient est responsable de la publication du cluster "Suite". Vous utilisez la méthode publishContinuationCluster() pour publier un objet ContinuationCluster.

Tout d'abord, vous devez utiliser isServiceAvailable() pour vérifier si le service est disponible pour l'intégration.

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 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 ContinuationCluster, vous devrez publier à nouveau toutes les entités.

Les données ContinuationCluster ne doivent être fournies que pour les comptes d'adultes. Ne publier que lorsque le compte Profile appartient à un adulte.

Synchronisation entre les appareils

Option SyncAcrossDevices

Ce flag indique si les données ContinuationCluster d'un utilisateur sont synchronisées sur ses appareils (téléviseur, téléphone, tablette, etc.). Par défaut, il est défini sur "false", ce qui signifie que la synchronisation entre les appareils est désactivée par défaut.

Valeurs:

  • "true" : les données ContinuationCluster 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 une expérience optimale entre les appareils.
  • false: les données ContinuationCluster sont limitées à l'appareil actuel.

L'application multimédia doit fournir un paramètre clair pour activer/désactiver la synchronisation entre les appareils. Expliquez les avantages pour l'utilisateur, stockez ses préférences une fois et appliquez-les 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 exploiter tout le potentiel de notre fonctionnalité multi-appareil, assurez-vous que votre application obtient le consentement de l'utilisateur et activez SyncAcrossDevices sur "true". Cela permet de synchroniser facilement les contenus sur plusieurs appareils, ce qui améliore l'expérience utilisateur et stimule l'engagement. Par exemple, un partenaire qui a implémenté cette fonctionnalité a constaté une augmentation de 40% du nombre de clics sur "Continuer à regarder", car ses contenus étaient diffusés sur plusieurs appareils.

Supprimer les données de la fonctionnalité de découverte de vidéos

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 client.deleteClusters(). À la réception de la requête, le service supprimera toutes les données de découverte de vidéos 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 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(
  new DeleteClustersRequest.Builder()
        .setAccountProfile(AccountProfile())
        .setReason(DeleteReason.DELETE_REASON_USER_LOG_OUT)
        .setSyncAcrossDevices(true)
        .build()
)

Tests

Utilisez l'application de validation pour vous assurer que l'intégration du SDK Engage fonctionne correctement. Cette application Android fournit des outils pour vous aider à vérifier vos données et à confirmer que les intents de diffusion sont gérés 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 sous forme de ligne distincte dans l'interface de l'application.

  • Assurez-vous que l'indicateur de service Engage n'est PAS défini sur "production" dans le fichier manifeste Android de votre application.
  • Installez et ouvrez l'application Engage Verify.
  • Si isServiceAvailable est false, cliquez sur le bouton "Activer/Désactiver" pour activer la fonctionnalité.
  • Saisissez le nom du package de votre application pour afficher automatiquement les données publiées une fois que vous commencez à publier.
  • Testez ces actions dans votre application :
    • Connectez-vous.
    • Changer de profil(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 cette fonctionnalité est disponible)
  • Après chaque action, vérifiez que votre application a appelé l'API publishContinuationClusters et que les données sont correctement affichées dans l'application de validation.
  • L'application de validation affiche une coche verte "OK" pour les entités implémentées correctement.

    Capture d&#39;écran de validation de l&#39;application
    Figure 1. Validation de l'application : succès
  • L'application de validation signalera les entités problématiques.

    Capture d&#39;écran de l&#39;erreur de l&#39;application de validation
    Figure 2. Erreur de 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 l'entité dans l'application de validation, puis cliquez dessus. Les problèmes spécifiques s'affichent et sont mis en surbrillance en rouge pour vous permettre de les examiner (voir exemple ci-dessous).

    Détails de l&#39;erreur de validation de l&#39;application
    Figure 3. Détails sur l'erreur de l'application de validation