Kanały na ekranie głównym

Ekran główny Androida TV (lub po prostu ekran główny) to interfejs, który wyświetla rekomendowane treści w postaci tabeli kanałów i programów. Każdy wiersz odpowiada kanałowi. Na kanale znajdują się karty wszystkich programów dostępnych na tym kanale:

Ekran główny telewizora

Ten dokument pokazuje, jak dodawać kanały i programy do ekranu głównego, aktualizować treści, wykonywać działania użytkowników i zapewnić użytkownikom najlepsze wrażenia. (Jeśli chcesz dowiedzieć się więcej o interfejsie API, wykonaj ćwiczenia z programowania dotyczące ekranu głównego i obejrzyj sesję I/O 2017 na temat Androida TV).

Uwaga: kanały rekomendacji są dostępne tylko na Androidzie 8.0 (poziom interfejsu API 26) i nowszych. Musisz ich używać do podawania rekomendacji aplikacji działających na Androidzie 8.0 (poziom interfejsu API 26) i nowszych. Aby udostępniać rekomendacje dotyczące aplikacji działających na wcześniejszych wersjach Androida, Twoja aplikacja musi używać wiersza rekomendacji.

Interfejs ekranu głównego

Aplikacje mogą tworzyć nowe kanały, dodawać, usuwać i aktualizować programy na kanale oraz kontrolować kolejność programów na kanale. Na przykład aplikacja może utworzyć kanał o nazwie „Co nowego” i wyświetlać karty dotyczące nowo dostępnych programów.

Aplikacje nie mogą kontrolować kolejności, w jakiej kanały pojawiają się na ekranie głównym. Gdy aplikacja utworzy nowy kanał, na ekranie głównym zostanie on dodany na dole listy kanałów. Użytkownik może zmieniać kolejność, ukrywać i pokazywać kanały.

Kanał Warte obejrzenia

Kanał „Warte obejrzenia” to drugi wiersz na ekranie głównym, po wierszu aplikacji. System utworzy i będzie utrzymywać ten kanał. Twoja aplikacja może dodawać programy do kanału Warte obejrzenia. Więcej informacji znajdziesz w artykule Dodawanie programów na kanale Warte obejrzenia.

Kanały aplikacji

Kanały tworzone przez aplikację są zgodne z tym cyklem życia:

  1. Użytkownik odkrywa kanał w Twojej aplikacji i prosi o dodanie go do ekranu głównego.
  2. Aplikacja tworzy kanał i dodaje go do sekcji TvProvider (w tym momencie kanał jest niewidoczny).
  3. Aplikacja poprosi system o wyświetlenie kanału.
  4. System prosi użytkownika o zatwierdzenie nowego kanału.
  5. Nowy kanał pojawi się w ostatnim wierszu ekranu głównego.

Kanał domyślny

Aplikacja może oferować dowolną liczbę kanałów, które użytkownik może dodać do ekranu głównego. Zanim kanał pojawi się na ekranie głównym, użytkownik musi najpierw wybrać i zatwierdzić. Każda aplikacja ma opcję utworzenia jednego domyślnego kanału. Kanał domyślny jest specjalny, ponieważ automatycznie pojawia się na ekranie głównym. Użytkownik nie musi o to prosić.

Wymagania wstępne

Ekran główny Androida TV używa interfejsów API TvProvider Androida do zarządzania kanałami i programami, które tworzy Twoja aplikacja. Aby uzyskać dostęp do danych dostawcy, dodaj do pliku manifestu aplikacji te uprawnienia:

<uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />

Biblioteka pomocy TvProvider ułatwia korzystanie z usług dostawcy. Dodaj go do zależności w pliku build.gradle:

Odlotowy

implementation 'androidx.tvprovider:tvprovider:1.0.0'

Kotlin

implementation("androidx.tvprovider:tvprovider:1.0.0")

Aby korzystać z kanałów i programów, musisz uwzględnić w programie te operacje importowania bibliotek:

Kotlin

import android.support.media.tv.Channel
import android.support.media.tv.TvContractCompat
import android.support.media.tv.ChannelLogoUtils
import android.support.media.tv.PreviewProgram
import android.support.media.tv.WatchNextProgram

Java

import android.support.media.tv.Channel;
import android.support.media.tv.TvContractCompat;
import android.support.media.tv.ChannelLogoUtils;
import android.support.media.tv.PreviewProgram;
import android.support.media.tv.WatchNextProgram;

Kanały

Pierwszy kanał utworzony przez Twoją aplikację staje się kanałem domyślnym. Kanał domyślny pojawi się automatycznie na ekranie głównym. Wszystkie inne utworzone przez Ciebie kanały muszą zostać wybrane i zaakceptowane przez użytkownika, zanim pojawią się na ekranie głównym.

Tworzenie kanału

Aplikacja powinna prosić o wyświetlanie nowo dodanych kanałów tylko wtedy, gdy działa na pierwszym planie. Dzięki temu w aplikacji nie będzie wyświetlane okno z prośbą o zatwierdzenie dodania kanału, gdy użytkownik korzysta z innej aplikacji. Jeśli spróbujesz dodać kanał, gdy działa on w tle, metoda onActivityResult() aktywności zwróci kod stanu RESULT_CANCELED.

Aby utworzyć kanał, wykonaj te czynności:

  1. Utwórz kreator kanałów i ustaw jego atrybuty. Pamiętaj, że typem kanału musi być TYPE_PREVIEW. W razie potrzeby dodaj więcej atrybutów.

    Kotlin

    val builder = Channel.Builder()
    // Every channel you create must have the type TYPE_PREVIEW
    builder.setType(TvContractCompat.Channels.TYPE_PREVIEW)
            .setDisplayName("Channel Name")
            .setAppLinkIntentUri(uri)
    

    Java

    Channel.Builder builder = new Channel.Builder();
    // Every channel you create must have the type TYPE_PREVIEW
    builder.setType(TvContractCompat.Channels.TYPE_PREVIEW)
            .setDisplayName("Channel Name")
            .setAppLinkIntentUri(uri);
    
  2. Wstaw kanał do dostawcy:

    Kotlin

    var channelUri = context.contentResolver.insert(
            TvContractCompat.Channels.CONTENT_URI, builder.build().toContentValues())
    

    Java

    Uri channelUri = context.getContentResolver().insert(
            TvContractCompat.Channels.CONTENT_URI, builder.build().toContentValues());
    
  3. Musisz zapisać identyfikator kanału, aby móc później dodawać do niego programy. Wyodrębnij identyfikator kanału ze zwróconego identyfikatora URI:

    Kotlin

    var channelId = ContentUris.parseId(channelUri)
    

    Java

    long channelId = ContentUris.parseId(channelUri);
    
  4. Musisz dodać logo do kanału. Użyj Uri lub Bitmap. Ikona logo powinna mieć wymiary 80 x 80 dp i być nieprzezroczysta. Wyświetla się pod okrągłą maską:

    Maska ikony na ekranie głównym telewizora

    Kotlin

    // Choose one or the other
    storeChannelLogo(context: Context, channelId: Long, logoUri: Uri) // also works if logoUri is a URL
    storeChannelLogo(context: Context, channelId: Long, logo: Bitmap)
    

    Java

    // Choose one or the other
    storeChannelLogo(Context context, long channelId, Uri logoUri); // also works if logoUri is a URL
    storeChannelLogo(Context context, long channelId, Bitmap logo);
    
  5. Utwórz kanał domyślny (opcjonalnie): gdy Twoja aplikacja utworzy swój pierwszy kanał, możesz ustawić go jako kanał domyślny, aby od razu pojawiał się na ekranie głównym bez żadnych działań ze strony użytkownika. Inne utworzone przez Ciebie kanały nie będą widoczne, dopóki użytkownik ich nie wybierzesz.

    Kotlin

    TvContractCompat.requestChannelBrowsable(context, channelId)
    

    Java

    TvContractCompat.requestChannelBrowsable(context, channelId);
    

  6. Ustaw kanał domyślny przed otwarciem aplikacji. Możesz to osiągnąć, dodając obiekt BroadcastReceiver, który nasłuchuje działania android.media.tv.action.INITIALIZE_PROGRAMS wysyłanego przez ekran główny po zainstalowaniu aplikacji:
    <receiver
      android:name=".RunOnInstallReceiver"
      android:exported="true">
        <intent-filter>
          <action android:name="android.media.tv.action.INITIALIZE_PROGRAMS" />
          <category android:name="android.intent.category.DEFAULT" />
        </intent-filter>
    </receiver>
    
    Podczas instalowania aplikacji z nieoficjalnych źródeł możesz przetestować ten krok, wywołując intencję za pomocą adb, gdzie your.package.name/.YourReceiverName to BroadcastReceiver aplikacji:

    adb shell am broadcast -a android.media.tv.action.INITIALIZE_PROGRAMS -n \
        your.package.name/.YourReceiverName
    

    W rzadkich przypadkach aplikacja może odebrać komunikat w tym samym czasie, gdy użytkownik ją uruchomi. Nie próbuj dodać kanału domyślnego więcej niż raz.

Aktualizowanie kanału

Aktualizowanie kanałów przebiega podobnie do ich tworzenia.

Aby określić atrybuty, które muszą zostać zmienione, użyj innego elementu Channel.Builder.

Użyj przycisku ContentResolver, aby zaktualizować kanał. Użyj identyfikatora kanału zapisanego podczas dodawania kanału:

Kotlin

context.contentResolver.update(
        TvContractCompat.buildChannelUri(channelId),
        builder.build().toContentValues(),
        null,
        null
)

Java

context.getContentResolver().update(TvContractCompat.buildChannelUri(channelId),
    builder.build().toContentValues(), null, null);

Aby zaktualizować logo kanału, użyj storeChannelLogo().

Usuwanie kanału

Kotlin

context.contentResolver.delete(TvContractCompat.buildChannelUri(channelId), null, null)

Java

context.getContentResolver().delete(TvContractCompat.buildChannelUri(channelId), null, null);

Programy

Dodawanie programów do kanału aplikacji

Utwórz PreviewProgram.Builder i ustaw jego atrybuty:

Kotlin

val builder = PreviewProgram.Builder()
builder.setChannelId(channelId)
        .setType(TvContractCompat.PreviewPrograms.TYPE_CLIP)
        .setTitle("Title")
        .setDescription("Program description")
        .setPosterArtUri(uri)
        .setIntentUri(uri)
        .setInternalProviderId(appProgramId)

Java

PreviewProgram.Builder builder = new PreviewProgram.Builder();
builder.setChannelId(channelId)
        .setType(TvContractCompat.PreviewPrograms.TYPE_CLIP)
        .setTitle("Title")
        .setDescription("Program description")
        .setPosterArtUri(uri)
        .setIntentUri(uri)
        .setInternalProviderId(appProgramId);

Dodaj więcej atrybutów w zależności od typu programu. (listę atrybutów dostępnych w przypadku poszczególnych typów programu znajdziesz w tabelach poniżej).

Wstaw program do dostawcy:

Kotlin

var programUri = context.contentResolver.insert(TvContractCompat.PreviewPrograms.CONTENT_URI,
        builder.build().toContentValues())

Java

Uri programUri = context.getContentResolver().insert(TvContractCompat.PreviewPrograms.CONTENT_URI,
      builder.build().toContentValues());

Pobierz identyfikator programu do późniejszego wykorzystania:

Kotlin

val programId = ContentUris.parseId(programUri)

Java

long programId = ContentUris.parseId(programUri);

Dodawanie programów na kanale Warte obejrzenia

Aby wstawiać programy na kanale Warte obejrzenia, zapoznaj się z sekcją Dodawanie programów na kanale Warte obejrzenia.

Aktualizowanie programu

Informacje o programie można zmieniać. Możesz na przykład zaktualizować cenę wypożyczenia filmu lub pasek postępu pokazujący, jaka część programu obejrzał użytkownik.

Użyj PreviewProgram.Builder, aby ustawić atrybuty, które trzeba zmienić, a następnie wywołaj getContentResolver().update, aby zaktualizować program. Podaj identyfikator programu zapisany podczas jego dodawania:

Kotlin

context.contentResolver.update(
        TvContractCompat.buildPreviewProgramUri(programId),
                builder.build().toContentValues(), null, null
)

Java

context.getContentResolver().update(TvContractCompat.buildPreviewProgramUri(programId),
    builder.build().toContentValues(), null, null);

Usuwanie programu

Kotlin

context.contentResolver
        .delete(TvContractCompat.buildPreviewProgramUri(programId), null, null)

Java

context.getContentResolver().delete(TvContractCompat.buildPreviewProgramUri(programId), null, null);

Obsługa działań użytkownika

Aplikacja może ułatwić użytkownikom odkrywanie treści, udostępniając interfejs do wyświetlania i dodawania kanałów. Aplikacja powinna też obsługiwać interakcje z kanałami po ich pojawieniu się na ekranie głównym.

Odkrywanie i dodawanie kanałów

Aplikacja może zawierać element interfejsu, który pozwala użytkownikowi wybrać i dodać kanały (np. przycisk z prośbą o dodanie kanału).

Gdy użytkownik poprosi o dostęp do konkretnego kanału, wykonaj ten kod, aby uzyskać uprawnienia do dodania go do interfejsu ekranu głównego:

Kotlin

val intent = Intent(TvContractCompat.ACTION_REQUEST_CHANNEL_BROWSABLE)
intent.putExtra(TvContractCompat.EXTRA_CHANNEL_ID, channelId)
try {
  activity.startActivityForResult(intent, 0)
} catch (e: ActivityNotFoundException) {
  // handle error
}

Java

Intent intent = new Intent(TvContractCompat.ACTION_REQUEST_CHANNEL_BROWSABLE);
intent.putExtra(TvContractCompat.EXTRA_CHANNEL_ID, channelId);
try {
   activity.startActivityForResult(intent, 0);
} catch (ActivityNotFoundException e) {
  // handle error
}

System wyświetli okno z prośbą o zatwierdzenie kanału. Przetwórz wynik żądania w metodzie onActivityResult swojej aktywności (Activity.RESULT_CANCELED lub Activity.RESULT_OK).

Zdarzenia na ekranie głównym Androida TV

Gdy użytkownik wchodzi w interakcję z programami lub kanałami opublikowanymi przez aplikację, ekran główny wysyła do niej intencje:

  • Gdy użytkownik wybierze logo kanału, ekran główny wysyła do aplikacji parametr Uri zapisany w atrybucie APP_LINK_INTENT_uri kanału. Aplikacja powinna uruchomić swój główny interfejs lub widok powiązany z wybranym kanałem.
  • Gdy użytkownik wybierze program, ekran główny wysyła do aplikacji Uri zapisany w atrybucie INTENT_uri programu. Aplikacja powinna odtworzyć wybraną treść.
  • Użytkownik może zaznaczyć, że nie jest już zainteresowany programem i poprosić o usunięcie go z interfejsu na ekranie głównym. System usuwa program z interfejsu i wysyła do aplikacji będącej jego właścicielem intencję (android.media.tv.ACTION_PREVIEW_PROGRAM_BROWSABLE_DISABLED lub android.media.tv.ACTION_WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED) z identyfikatorem programu. Aplikacja powinna usuwać program u dostawcy i NIE powinna dodawać go ponownie.

Utwórz filtry intencji dla wszystkich elementów Uris, które ekran główny wysyła w związku z interakcjami użytkownika, na przykład:

<receiver
   android:name=".WatchNextProgramRemoved"
   android:enabled="true"
   android:exported="true">
   <intent-filter>
       <action android:name="android.media.tv.ACTION_WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED" />
   </intent-filter>
</receiver>

Sprawdzone metody

  • Wiele aplikacji na telewizory wymaga zalogowania się. W takim przypadku BroadcastReceiver, który nasłuchuje polecenia android.media.tv.action.INITIALIZE_PROGRAMS, powinien sugerować treści na kanale nieuwierzytelnionym użytkownikom.Aplikacja może na przykład początkowo wyświetlać najlepsze treści lub aktualnie popularne treści. Po zalogowaniu się użytkownika mogą wyświetlać spersonalizowane treści. Dzięki temu aplikacje mogą zwiększyć sprzedaż użytkowników, zanim się zalogują.
  • Jeśli aplikacja nie działa na pierwszym planie i chcesz zaktualizować kanał lub program, użyj JobScheduler do zaplanowania pracy (zobacz: JobScheduler i JobService).
  • System może cofnąć uprawnienia dostawcy aplikacji, jeśli aplikacja działa niepoprawnie (np. stale spamuje dostawcę danymi). Pamiętaj, aby opakować kod, który uzyskuje dostęp do dostawcy, klauzulami try-catch, które obsługują wyjątki dotyczące zabezpieczeń.
  • Przed aktualizacją programów i kanałów poproś dostawcę o dane, które chcesz zaktualizować i uzgodnić. Nie ma na przykład potrzeby aktualizowania programu, który użytkownik chce usunąć z interfejsu. użyć zadania w tle, które wstawia lub aktualizuje Twoje dane u dostawcy po wysłaniu zapytania o istniejące dane, a następnie poproszenia o zatwierdzenie kanałów. Możesz je uruchamiać podczas uruchamiania aplikacji lub za każdym razem, gdy trzeba zaktualizować dane.

    Kotlin

    context.contentResolver
      .query(
          TvContractCompat.buildChannelUri(channelId),
              null, null, null, null).use({
                  cursor-> if (cursor != null and cursor.moveToNext()) {
                               val channel = Channel.fromCursor(cursor)
                               if (channel.isBrowsable()) {
                                   //update channel's programs
                               }
                           }
              })
    

    Java

    try (Cursor cursor = context.getContentResolver()
          .query(
              TvContractCompat.buildChannelUri(channelId),
              null,
              null,
              null,
              null)) {
                  if (cursor != null && cursor.moveToNext()) {
                      Channel channel = Channel.fromCursor(cursor);
                      if (channel.isBrowsable()) {
                          //update channel's programs
                      }
                  }
              }
    
  • Używaj unikalnych identyfikatorów URI do wszystkich obrazów (logo, ikon, obrazów treści). Pamiętaj, aby przy aktualizacji obrazu użyć innego identyfikatora URI. Wszystkie obrazy są zapisywane w pamięci podręcznej. Jeśli podczas edytowania obrazu nie zmienisz identyfikatora URI, stary obraz będzie nadal wyświetlany.

  • Pamiętaj, że klauzule WHERE są niedozwolone, a wywołania dostawców z klauzulami WHERE będą zgłaszać wyjątek bezpieczeństwa.

Atrybuty

W tej sekcji opisujemy atrybuty kanału i programu oddzielnie.

Atrybuty kanału

Musisz określić te atrybuty dla każdego kanału:

Atrybut Uwagi
TYP ustaw na TYPE_PREVIEW.
DISPLAY_NAME ustawiony na nazwę kanału.
APP_LINK_INTENT_uri Gdy użytkownik wybierze logo kanału, system wyśle prośbę o rozpoczęcie działania, które przedstawia treści związane z kanałem. Ustaw ten atrybut na identyfikator URI używany w filtrze intencji dla tej aktywności.

Dodatkowo kanał ma też 6 pól zarezerwowanych do użytku wewnętrznego w aplikacjach. Te pola mogą służyć do przechowywania kluczy i innych wartości, które ułatwiają mapowanie kanału na wewnętrzną strukturę danych:

  • WEWNĘTRZNY_IDENTYFIKATOR_DOSTAWCY
  • DANE_DOSTAWCY WEWNĘTRZNE
  • WEWNĘTRZNY_DOSTAWCA_FLAG1
  • INTERNAL_PROVIDER_FLAG2
  • WEWNĘTRZNY_DOSTAWCA_FLAG3
  • WEWNĘTRZNY_DOSTAWCA_FLAG4

Atrybuty programu

Sprawdź atrybuty poszczególnych typów programów na poszczególnych stronach:

Kod demonstracyjny

Więcej informacji o tworzeniu aplikacji, które wchodzą w interakcję z ekranem głównym oraz o dodawaniu kanałów i programów do ekranu głównego Androida TV, znajdziesz w ćwiczeniach z programowania na ekranie głównym.