Integrowanie funkcji Oglądaj dalej na Androidzie TV

Z tego przewodnika dowiesz się, jak zintegrować funkcję Oglądaj dalej z aplikacją na Androida TV za pomocą pakietu Engage SDK.

Przygotowanie

Wykonaj czynności opisane w sekcji Prace wstępne w Przewodniku dla początkujących.

Integracja

Tworzenie encji

Pakiet SDK definiuje różne encje reprezentujące poszczególne typy produktów. Klaster kontynuacji obsługuje te elementy:

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

Określ identyfikatory URI i plakaty dla tych elementów na poszczególnych platformach.

Jeśli jeszcze nie masz identyfikatorów URI odtwarzania dla każdej platformy, np. Android TV, Android lub iOS, utwórz je. Gdy użytkownik kontynuuje oglądanie na każdej platformie, aplikacja używa docelowego identyfikatora URI odtwarzania, aby odtworzyć treści wideo.

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

Obrazy plakatu wymagają identyfikatora URI i wymiarów w pikselach (wysokość i szerokość). Możesz kierować reklamy na różne formaty, przesyłając kilka obrazów plakatu, ale upewnij się, że wszystkie obrazy mają format obrazu 16:9 i minimalną wysokość 200 pikseli, aby element „Oglądaj dalej” wyświetlał się prawidłowo, zwłaszcza w Przestrzeni rozrywki Google. Obrazy o wysokości mniejszej niż 200 pikseli mogą nie być wyświetlane.

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

Ten przykład pokazuje, jak utworzyć MovieEntity ze wszystkimi wymaganymi polami:

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

Podanie szczegółów, takich jak gatunki i oceny treści, umożliwia Google TV prezentowanie Twoich treści w bardziej dynamiczny sposób i docieranie do odpowiednich widzów.

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

Encje automatycznie pozostają dostępne przez 60 dni, chyba że określisz krótszy czas ważności. Niestandardową datę ważności ustaw tylko wtedy, gdy chcesz, aby element został usunięty przed upływem domyślnego okresu.

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

Ten przykład pokazuje, jak utworzyć obiekt TvEpisodeEntity ze wszystkimi wymaganymi polami:

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

Ciąg znaków numeru odcinka (np. "2") i ciągu znaków numeru sezonu (np. "1") zostanie rozwinięty do odpowiedniej formy, zanim zostanie wyświetlony na karcie Oglądaj dalej. Pamiętaj, że powinien to być ciąg numeryczny, nie wpisuj „o2”, „odcinek 2”, „s1” ani „sezon 1”.

Jeśli dany program telewizyjny ma tylko 1 sezon, ustaw numer sezonu na 1.

Aby zmaksymalizować szanse na to, że widzowie znajdą Twoje treści w Google TV, rozważ podanie dodatkowych danych, takich jak gatunki, oceny treści i okna czasowe dostępności, ponieważ te informacje mogą ulepszyć wyświetlanie i opcje filtrowania.

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

Oto przykład tworzenia VideoClipEntity ze wszystkimi wymaganymi polami.

VideoClipEntity reprezentuje klip wygenerowany przez użytkownika, np. film na 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()

Możesz też opcjonalnie ustawić twórcę, obraz twórcy, czas utworzenia w milisekundach lub okno czasowe dostępności .

LiveStreamingVideoEntity

Oto przykład tworzenia obiektu LiveStreamingVideoEntity ze wszystkimi wymaganymi polami.

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

Opcjonalnie możesz ustawić czas rozpoczęcia, nadawcę, ikonę nadawcy lub okno czasowe dostępności elementu transmisji na żywo.

Szczegółowe informacje o atrybutach i wymaganiach znajdziesz w dokumentacji interfejsu API.

Podaj dane klastra kontynuacji

AppEngagePublishClient odpowiada za publikowanie klastra kontynuacji. Do opublikowania obiektu ContinuationCluster używasz metody publishContinuationCluster.

Zainicjuj klienta i sprawdź dostępność usługi zgodnie z opisem w Przewodniku dla początkujących.

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

Gdy usługa otrzyma żądanie, w ramach jednej transakcji zostaną wykonane te działania:

  • Dotychczasowe dane ContinuationCluster od partnera dewelopera zostaną usunięte.
  • Dane z żądania są analizowane i przechowywane w zaktualizowanym obiekcie ContinuationCluster.

W przypadku błędu całe żądanie jest odrzucane i zachowywany jest dotychczasowy stan.

Interfejsy API do publikowania to interfejsy API do aktualizowania i wstawiania, które zastępują istniejące treści. Jeśli chcesz zaktualizować konkretny element w klastrze kontynuacji, musisz ponownie opublikować wszystkie elementy.

Dane klastra kontynuacji powinny być podawane tylko w przypadku kont osób dorosłych. Publikuj tylko wtedy, gdy profil konta należy do osoby dorosłej.

Synchronizacja na innych urządzeniach

Ustawienie setSyncAcrossDevices określa, czy dane użytkownika w usłudze ContinuationCluster są synchronizowane na urządzeniach takich jak telewizor, telefon, tablet itp. Synchronizacja między urządzeniami jest domyślnie wyłączona.

Wartości:

  • true: dane klastra kontynuacji są udostępniane na wszystkich urządzeniach użytkownika, aby zapewnić mu nieprzerwane oglądanie. Zdecydowanie zalecamy tę opcję, aby zapewnić jak najlepsze działanie na różnych urządzeniach.
  • false: dane klastra kontynuacji są ograniczone do bieżącego urządzenia.

Aby treści były synchronizowane między urządzeniami, musisz też podać AccountProfile z identyfikatorem konta. Zobacz Tworzenie profilu konta.

Aplikacja multimedialna musi udostępniać przejrzyste ustawienie umożliwiające włączanie i wyłączanie synchronizacji między urządzeniami. Wyjaśnij użytkownikowi korzyści i zapisz jego preferencje, a następnie zastosuj je w publishContinuationCluster.

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

Aby w pełni wykorzystać możliwości funkcji dla wielu urządzeń, sprawdź, czy aplikacja uzyskuje zgodę użytkownika, i włącz SyncAcrossDevices na true. Dzięki temu treści mogą być płynnie synchronizowane między urządzeniami, co zwiększa wygodę użytkowników i ich zaangażowanie. Na przykład partner, który wdrożył tę funkcję, odnotował wzrost liczby kliknięć „Kontynuuj oglądanie” o 40%, ponieważ jego treści były wyświetlane na wielu urządzeniach.

Usuwanie danych Engage

Aby ręcznie usunąć dane użytkownika z serwera Google TV przed upływem standardowego 60-dniowego okresu przechowywania, użyj metody deleteClusters. Po otrzymaniu prośby usługa usunie wszystkie istniejące dane Engage z profilu konta lub z całego konta.

Typ wyliczeniowy DeleteReason określa przyczynę usunięcia danych. Poniższy kod usuwa dane dotyczące kontynuowania oglądania po wylogowaniu.


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

Testowanie

Użyj aplikacji do weryfikacji, aby sprawdzić, czy integracja z pakietem Engage SDK działa prawidłowo.

Po wywołaniu interfejsu API publikowania sprawdź, czy dane są prawidłowo publikowane, sprawdzając aplikację do weryfikacji. Klaster kontynuacji powinien być wyświetlany jako osobny wiersz w interfejsie aplikacji.

  • Przetestuj te działania w aplikacji:
    • Zaloguj się.
    • Przełączanie się między profilami(jeśli dotyczy).
    • Rozpocznij, a następnie wstrzymaj odtwarzanie filmu lub wróć na stronę główną.
    • Zamknij aplikację podczas odtwarzania filmu.
    • usunąć element z wiersza „Oglądaj dalej” (jeśli jest obsługiwany);
  • Po każdym działaniu sprawdź, czy aplikacja wywołała interfejs API publishContinuationClusters i czy dane są prawidłowo wyświetlane w aplikacji weryfikacyjnej.
  • W przypadku prawidłowo zaimplementowanych elementów aplikacja do weryfikacji wyświetla zielony znacznik wyboru „Wszystko w porządku”.

    Zrzut ekranu potwierdzający weryfikację aplikacji
    Rysunek 1. Aplikacja weryfikacyjna – sukces
  • Aplikacja do weryfikacji oznaczy wszystkie problematyczne elementy.

    Verification App Error Screenshot
    Rysunek 2. Błąd aplikacji weryfikacyjnej
  • Aby rozwiązać problemy z elementami, w których występują błędy, użyj pilota do telewizora, aby wybrać i kliknąć element w aplikacji do weryfikacji. Konkretne problemy zostaną wyświetlone i wyróżnione na czerwono (patrz przykład poniżej).

    Szczegóły błędu aplikacji do weryfikacji
    Rysunek 3. Szczegóły błędu aplikacji do weryfikacji