Integrare Continua a guardare su Android TV

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

Questa guida spiega come integrare Continua a guardare nella tua app Android TV utilizzando l'SDK Engage.

Preparazione

Segui le istruzioni della sezione Attività preliminari nella Guida introduttiva.

Integrazione

Crea entità

L'SDK ha definito entità diverse per rappresentare ogni tipo di elemento. Il cluster di continuazione supporta le seguenti entità:

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

Specifica gli URI e le immagini poster specifici della piattaforma per queste entità.

Inoltre, crea URI di riproduzione per ogni piattaforma, ad esempio Android TV, Android o iOS, se non l'hai ancora fatto. Pertanto, quando un utente continua a guardare su ogni piattaforma, l'app utilizza un URI di riproduzione mirato per riprodurre i contenuti 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)

Le immagini del poster richiedono un URI e le dimensioni in pixel (altezza e larghezza). Prendi di mira diversi fattori di forma fornendo più immagini del poster, ma verifica che tutte le immagini mantengano proporzioni 16:9 e un'altezza minima di 200 pixel per la corretta visualizzazione dell'entità "Continua a guardare", in particolare all'interno di Entertainment Space di Google. Le immagini con un'altezza inferiore a 200 pixel potrebbero non essere visualizzate.

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

Questo esempio mostra come creare un MovieEntity con tutti i campi obbligatori:

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

Fornire dettagli come generi e classificazioni dei contenuti consente a Google TV di mostrare i tuoi contenuti in modo più dinamico e di metterli in contatto con i giusti spettatori.

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

Le entità rimangono disponibili automaticamente per 60 giorni, a meno che tu non specifichi un periodo di scadenza più breve. Imposta una scadenza personalizzata solo se vuoi che l'entità venga rimossa prima di questo periodo predefinito.

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

Questo esempio mostra come creare un TvEpisodeEntity con tutti i campi obbligatori:

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 stringa del numero di episodio (ad esempio "2") e la stringa del numero di stagione (ad esempio "1") verranno espanse nel formato corretto prima di essere visualizzate nella scheda Continua a guardare. Tieni presente che deve essere una stringa numerica, non inserire "e2", "episodio 2", "s1" o "stagione 1".

Se un determinato programma TV ha una sola stagione, imposta il numero della stagione su 1.

Per massimizzare le possibilità che gli spettatori trovino i tuoi contenuti su Google TV, valuta la possibilità di fornire dati aggiuntivi come generi, classificazioni dei contenuti e finestre temporali di disponibilità, in quanto questi dettagli possono migliorare le opzioni di visualizzazione e filtro.

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

Ecco un esempio di creazione di un VideoClipEntity con tutti i campi obbligatori.

VideoClipEntity rappresenta un clip generato dall'utente, ad esempio un video di 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()

Puoi impostare facoltativamente il creatore, l'immagine del creatore, l'ora di creazione in millisecondi o la finestra temporale di disponibilità .

LiveStreamingVideoEntity

Ecco un esempio di creazione di un LiveStreamingVideoEntity con tutti i campi obbligatori.

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

Se vuoi, puoi impostare l'ora di inizio, l'emittente, l'icona dell'emittente o la finestra temporale di disponibilità per l'entità live streaming.

Per informazioni dettagliate su attributi e requisiti, consulta il Riferimento API.

Fornisci i dati del cluster di continuazione

AppEngagePublishClient è responsabile della pubblicazione del cluster Continua. Utilizzi il metodo publishContinuationCluste per pubblicare un oggetto ContinuationCluster.

Assicurati di inizializzare il client e di verificare la disponibilità del servizio come descritto nella Guida introduttiva.

client.publishContinuationCluster(
    PublishContinuationClusterRequest
        .Builder()
        .setContinuationCluster(
            ContinuationCluster.Builder()
                .setAccountProfile(accountProfile)
                .addEntity(movieEntity1)
                .addEntity(movieEntity2)
                .addEntity(tvEpisodeEntity1)
                .addEntity(tvEpisodeEntity2)
                .setSyncAcrossDevices(true)
                .build()
        )
        .build()
)

Quando il servizio riceve la richiesta, all'interno di una transazione vengono eseguite le seguenti azioni:

  • I dati ContinuationCluster esistenti del partner sviluppatore vengono rimossi.
  • I dati della richiesta vengono analizzati e archiviati nel ContinuationCluster aggiornato.

In caso di errore, l'intera richiesta viene rifiutata e lo stato esistente viene mantenuto.

Le API di pubblicazione sono API upsert, ovvero sostituiscono i contenuti esistenti. Se devi aggiornare un'entità specifica nel cluster di continuazione, dovrai pubblicare di nuovo tutte le entità.

I dati dei cluster di continuazione devono essere forniti solo per gli account per adulti. Pubblica solo quando il profilo dell'account appartiene a un adulto.

Sincronizzazione cross-device

Il flag SyncAcrossDevices controlla se i dati ContinuationCluster di un utente vengono sincronizzati su dispositivi come TV, smartphone, tablet e così via. La sincronizzazione cross-device è disattivata per impostazione predefinita.

Valori:

  • true: i dati del cluster di continuazione vengono condivisi su tutti i dispositivi dell'utente per un'esperienza di visualizzazione senza interruzioni. Consigliamo vivamente questa opzione per un'esperienza ottimale su più dispositivi.
  • false: i dati del cluster di continuazione sono limitati al dispositivo attuale.

L'applicazione multimediale deve fornire un'impostazione chiara per attivare o disattivare la sincronizzazione cross-device. Spiega i vantaggi per l'utente e memorizza la sua preferenza una sola volta per applicarla di conseguenza in publishContinuationCluster.

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

Per sfruttare al meglio la nostra funzionalità cross-device, verifica che l'app ottenga il consenso dell'utente e attiva SyncAcrossDevices per true. Ciò consente ai contenuti di sincronizzarsi perfettamente su tutti i dispositivi, migliorando l'esperienza utente e aumentando il coinvolgimento. Ad esempio, un partner che ha implementato questa funzionalità ha registrato un aumento del 40% dei clic su "Continua a guardare" perché i suoi contenuti sono stati visualizzati su più dispositivi.

Eliminare i dati di Video discovery

Per eliminare manualmente i dati di un utente dal server Google TV prima del periodo di conservazione standard di 60 giorni, utilizza il metodo deleteClusters. Una volta ricevuta la richiesta, il servizio eliminerà tutti i dati esistenti di scoperta dei video per il profilo dell'account o per l'intero account.

L'enumerazione DeleteReason definisce il motivo dell'eliminazione dei dati. Il seguente codice rimuove i dati di Continua a guardare al momento della disconnessione.


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

Test

Utilizza l'app di verifica per verificare che l'integrazione dell'SDK Engage funzioni correttamente.

Dopo aver richiamato l'API di pubblicazione, verifica che i dati vengano pubblicati correttamente controllando l'app di verifica. Il cluster di continuazione deve essere visualizzato come riga distinta nell'interfaccia dell'app.

  • Testa queste azioni nella tua app:
    • Accedi.
    • Passa da un profilo all'altro(se applicabile).
    • Avvia, metti in pausa un video o torna alla home page.
    • Chiudi l'app durante la riproduzione del video.
    • Rimuovere un elemento dalla riga "Continua a guardare" (se supportata).
  • Dopo ogni azione, verifica che la tua app abbia richiamato l'API publishContinuationClusters e che i dati vengano visualizzati correttamente nell'app di verifica.
  • L'app di verifica mostra un segno di spunta verde "Tutto a posto" per le entità implementate correttamente.

    Screenshot della verifica riuscita dell'app
    Figura 1. Verifica app riuscita
  • L'app di verifica segnalerà eventuali entità problematiche.

    Screenshot dell'errore dell'app di verifica
    Figura 2. Errore dell'app di verifica
  • Per risolvere i problemi relativi alle entità con errori, utilizza il telecomando della TV per selezionare e fare clic sull'entità nell'app di verifica. I problemi specifici verranno visualizzati ed evidenziati in rosso per la tua revisione (vedi l'esempio di seguito).

    Dettagli sull'errore dell'app di verifica
    Figura 3. Dettagli errore app di verifica