Twoje wejście TV musi dostarczać dane Elektronicznego przewodnika po programach (EPG) dla co najmniej jednego kanału w ramach jego konfiguracji. Musisz też okresowo aktualizować te dane, biorąc pod uwagę rozmiar aktualizacji i wątek przetwarzania, który je obsługuje. Możesz też podać linki do aplikacji kanałów, które prowadzą użytkownika do powiązanych treści i aktywności. Z tej lekcji dowiesz się, jak tworzyć i aktualizować dane kanału i programu w bazie danych systemu.
Wypróbuj przykładową aplikację Usługa wejścia TV.
Przyznaj dostęp
Aby wejście TV mogło obsługiwać dane EPG, w pliku manifestu Androida muszą zadeklarować uprawnienia do zapisu w następujący sposób:
<uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />
Zarejestruj kanały w bazie danych
Baza danych systemu Android TV zawiera rejestry danych kanałów dotyczących wejść TV. W konfiguracji każdego kanału musisz zmapować dane kanału na te pola klasy TvContract.Channels
:
COLUMN_DISPLAY_NAME
– wyświetlana nazwa kanałuCOLUMN_DISPLAY_NUMBER
– wyświetlany numer kanałuCOLUMN_INPUT_ID
– identyfikator usługi wejścia TVCOLUMN_SERVICE_TYPE
– typ usługi kanału.COLUMN_TYPE
– typ standardu transmisji kanału.COLUMN_VIDEO_FORMAT
– domyślny format wideo kanału
Chociaż struktura wejściowej sygnału telewizyjnego jest na tyle ogólna, aby poradzić sobie z tradycyjnymi transmisjami i treściami Over-The-Top (OTT) bez żadnego rozróżnienia, oprócz wymienionych powyżej możesz zdefiniować te kolumny, aby lepiej identyfikować tradycyjne kanały telewizyjne:
COLUMN_ORIGINAL_NETWORK_ID
– identyfikator sieci telewizyjnej,COLUMN_SERVICE_ID
– identyfikator usługi.COLUMN_TRANSPORT_STREAM_ID
– identyfikator strumienia transportu
Jeśli chcesz podać szczegóły linku aplikacji do swoich kanałów, musisz zaktualizować niektóre dodatkowe pola. Więcej informacji o polach linków aplikacji znajdziesz w artykule Dodawanie informacji o linku aplikacji.
W przypadku urządzeń wejściowych telewizyjnych opartych na streamingu internetowym przypisz do powyższych wartości własne wartości, aby każdy kanał był identyfikowany jednoznacznie.
Pobierz metadane kanału (w formacie XML, JSON lub innym) z serwera backendu i podczas konfiguracji zmapuj wartości na bazę danych systemu w następujący sposób:
Kotlin
val values = ContentValues().apply { put(TvContract.Channels.COLUMN_DISPLAY_NUMBER, channel.number) put(TvContract.Channels.COLUMN_DISPLAY_NAME, channel.name) put(TvContract.Channels.COLUMN_ORIGINAL_NETWORK_ID, channel.originalNetworkId) put(TvContract.Channels.COLUMN_TRANSPORT_STREAM_ID, channel.transportStreamId) put(TvContract.Channels.COLUMN_SERVICE_ID, channel.serviceId) put(TvContract.Channels.COLUMN_VIDEO_FORMAT, channel.videoFormat) } val uri = context.contentResolver.insert(TvContract.Channels.CONTENT_URI, values)
Java
ContentValues values = new ContentValues(); values.put(Channels.COLUMN_DISPLAY_NUMBER, channel.number); values.put(Channels.COLUMN_DISPLAY_NAME, channel.name); values.put(Channels.COLUMN_ORIGINAL_NETWORK_ID, channel.originalNetworkId); values.put(Channels.COLUMN_TRANSPORT_STREAM_ID, channel.transportStreamId); values.put(Channels.COLUMN_SERVICE_ID, channel.serviceId); values.put(Channels.COLUMN_VIDEO_FORMAT, channel.videoFormat); Uri uri = context.getContentResolver().insert(TvContract.Channels.CONTENT_URI, values);
W powyższym przykładzie channel
to obiekt, który przechowuje metadane kanału z serwera backendu.
Prezentowanie informacji o kanale i programie
Aplikacja systemowa TV wyświetla użytkownikom informacje o kanałach i programach, jak pokazano na rysunku 1. Aby mieć pewność, że informacje o kanale i programie są zgodne z wyświetlaczem informacji o kanale i programie w aplikacji systemowej TV, postępuj zgodnie z poniższymi wskazówkami.
- Numer kanału (
COLUMN_DISPLAY_NUMBER
) - Ikona (
android:icon
w pliku manifestu danych wejściowych TV) - Opis programu (
COLUMN_SHORT_DESCRIPTION
) - Tytuł programu (
COLUMN_TITLE
) - Logo kanału (
TvContract.Channels.Logo
)- Użyj koloru #EEEEEE, aby dopasować do tekstu otaczającego
- Nie dodawaj dopełnienia
- Poster art (
COLUMN_POSTER_ART_URI
)- Współczynnik proporcji: od 16:9 do 4:3
Rysunek 1. Prezentujący kanał aplikacji i informacje o programie w systemie TV.
Aplikacja systemowa TV podaje te same informacje z przewodnika po programach, w tym plakat, jak widać na ilustracji 2.
Rysunek 2. Przewodnik po programach po aplikacjach systemowych.
Aktualizowanie danych kanału
Do aktualizowania istniejących danych kanału użyj metody update()
, zamiast usuwać i ponownie dodawać dane. Bieżącą wersję danych możesz rozpoznać, wybierając rekordy do zaktualizowania za pomocą Channels.COLUMN_VERSION_NUMBER
i Programs.COLUMN_VERSION_NUMBER
.
Uwaga: dodanie danych kanału do ContentProvider
może trochę potrwać. Dodaj bieżące programy (te w ciągu 2 godzin od bieżącego czasu) tylko wtedy, gdy skonfigurujesz urządzenie EpgSyncJobService
tak, aby aktualizowało pozostałe dane kanału w tle. Przykład znajdziesz w artykule
Przykładowa aplikacja na Androida TV Live TV.
Zbiorcze wczytywanie danych kanału
W przypadku aktualizowania systemowej bazy danych o dużej ilości danych kanału użyj metody ContentResolver
applyBatch()
lub bulkInsert()
. Oto przykład użycia właściwości applyBatch()
:
Kotlin
val ops = ArrayList<ContentProviderOperation>() val programsCount = channelInfo.mPrograms.size channelInfo.mPrograms.forEachIndexed { index, program -> ops += ContentProviderOperation.newInsert( TvContract.Programs.CONTENT_URI).run { withValues(programs[index]) withValue(TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS, programStartSec * 1000) withValue( TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS, (programStartSec + program.durationSec) * 1000 ) build() } programStartSec += program.durationSec if (index % 100 == 99 || index == programsCount - 1) { try { contentResolver.applyBatch(TvContract.AUTHORITY, ops) } catch (e: RemoteException) { Log.e(TAG, "Failed to insert programs.", e) return } catch (e: OperationApplicationException) { Log.e(TAG, "Failed to insert programs.", e) return } ops.clear() } }
Java
ArrayList<ContentProviderOperation> ops = new ArrayList<>(); int programsCount = channelInfo.mPrograms.size(); for (int j = 0; j < programsCount; ++j) { ProgramInfo program = channelInfo.mPrograms.get(j); ops.add(ContentProviderOperation.newInsert( TvContract.Programs.CONTENT_URI) .withValues(programs.get(j)) .withValue(Programs.COLUMN_START_TIME_UTC_MILLIS, programStartSec * 1000) .withValue(Programs.COLUMN_END_TIME_UTC_MILLIS, (programStartSec + program.durationSec) * 1000) .build()); programStartSec = programStartSec + program.durationSec; if (j % 100 == 99 || j == programsCount - 1) { try { getContentResolver().applyBatch(TvContract.AUTHORITY, ops); } catch (RemoteException | OperationApplicationException e) { Log.e(TAG, "Failed to insert programs.", e); return; } ops.clear(); } }
Przetwarzanie danych kanału asynchronicznie
Manipulacja danymi, np. pobranie strumienia z serwera lub uzyskanie dostępu do bazy danych, nie powinna blokować wątku UI. Użycie elementu AsyncTask
to jeden ze sposobów asynchronicznego przeprowadzania aktualizacji. Na przykład podczas wczytywania informacji o kanale z serwera backendu możesz użyć polecenia AsyncTask
w ten sposób:
Kotlin
private class LoadTvInputTask(val context: Context) : AsyncTask<Uri, Unit, Unit>() { override fun doInBackground(vararg uris: Uri) { try { fetchUri(uris[0]) } catch (e: IOException) { Log.d("LoadTvInputTask", "fetchUri error") } } @Throws(IOException::class) private fun fetchUri(videoUri: Uri) { context.contentResolver.openInputStream(videoUri).use { inputStream -> Xml.newPullParser().also { parser -> try { parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false) parser.setInput(inputStream, null) sTvInput = ChannelXMLParser.parseTvInput(parser) sSampleChannels = ChannelXMLParser.parseChannelXML(parser) } catch (e: XmlPullParserException) { e.printStackTrace() } } } } }
Java
private static class LoadTvInputTask extends AsyncTask<Uri, Void, Void> { private Context mContext; public LoadTvInputTask(Context context) { mContext = context; } @Override protected Void doInBackground(Uri... uris) { try { fetchUri(uris[0]); } catch (IOException e) { Log.d("LoadTvInputTask", "fetchUri error"); } return null; } private void fetchUri(Uri videoUri) throws IOException { InputStream inputStream = null; try { inputStream = mContext.getContentResolver().openInputStream(videoUri); XmlPullParser parser = Xml.newPullParser(); try { parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false); parser.setInput(inputStream, null); sTvInput = ChannelXMLParser.parseTvInput(parser); sSampleChannels = ChannelXMLParser.parseChannelXML(parser); } catch (XmlPullParserException e) { e.printStackTrace(); } } finally { if (inputStream != null) { inputStream.close(); } } } }
Jeśli musisz regularnie aktualizować dane EPG, rozważ używanie WorkManager
do uruchamiania procesu aktualizacji w czasie bezczynności, np.codziennie o 3:00.
Inne metody oddzielania zadań aktualizacji danych od wątku interfejsu obejmują użycie klasy HandlerThread
lub wdrożenie własnych metod za pomocą klas Looper
i Handler
. Więcej informacji znajdziesz w artykule
Procesy i wątki.
Dodaj informacje o linku aplikacji
Kanały mogą używać linków do aplikacji, aby umożliwić użytkownikom łatwe uruchamianie powiązanych działań podczas oglądania treści kanału. Aplikacje kanału korzystają z linków do aplikacji, aby zwiększać zaangażowanie użytkowników przez uruchamianie działań pokazujących powiązane informacje lub dodatkowe treści. Za pomocą linków do aplikacji możesz np.:
- Pomagaj użytkownikom odkrywać i kupować powiązane treści.
- Podaj dodatkowe informacje o aktualnie odtwarzanych treściach.
- Oglądając treści w odcinkach, zacznij oglądać następny odcinek.
- Pozwól użytkownikom na interakcję z treściami – na przykład ocenianie lub recenzowanie treści – bez przerywania odtwarzania.
Linki do aplikacji wyświetlają się, gdy użytkownik naciśnie Wybierz, aby wyświetlić menu telewizora podczas oglądania treści na kanale.
Rysunek 1. Przykładowy link do aplikacji wyświetlany w wierszu Kanały podczas pokazywania treści kanału.
Gdy użytkownik kliknie link do aplikacji, system rozpocznie działanie przy użyciu identyfikatora URI intencji określonego przez aplikację kanału. Materiały na kanale będą nadal odtwarzane, gdy aktywne jest linki do aplikacji. Użytkownik może wrócić do treści kanału, naciskając Wstecz.
Podaj dane kanału powiązanego z aplikacją
Android TV automatycznie tworzy link do aplikacji dla każdego kanału,
korzystając z informacji z danych kanału. Aby podać informacje o linku aplikacji, w polach TvContract.Channels
podaj te informacje:
COLUMN_APP_LINK_COLOR
– kolor uzupełniający linku do aplikacji dla tego kanału. Przykładowy kolor uzupełniający zobacz ilustrację 2, objaśnienie 3.COLUMN_APP_LINK_ICON_URI
– Identyfikator URI ikony plakietki aplikacji linku do tego kanału. Przykład ikony plakietki przykładowej aplikacji znajdziesz na rys. 2, objaśnienie 2.COLUMN_APP_LINK_INTENT_URI
– identyfikator URI intencji linku do aplikacji dla tego kanału. Możesz utworzyć identyfikator URI za pomocą poleceniatoUri(int)
za pomocąURI_INTENT_SCHEME
, a potem przekonwertować identyfikator URI z powrotem na pierwotną intencję za pomocą poleceniaparseUri()
.COLUMN_APP_LINK_POSTER_ART_URI
– Identyfikator URI plakatu używany jako tło linku do aplikacji dla tego kanału. Przykładowy obraz plakatu znajdziesz na rys. 2, objaśnienie 1.COLUMN_APP_LINK_TEXT
– opisowy tekst linku do aplikacji dla tego kanału. Przykładowy opis linku do aplikacji znajdziesz na rys. 2, objaśnienie 3.
Rysunek 2. Szczegóły linku aplikacji.
Jeśli w danych kanału nie ma informacji o linku do aplikacji, system utworzy domyślny link do aplikacji. System wybiera te informacje domyślne:
- W przypadku identyfikatora URI intencji (
COLUMN_APP_LINK_INTENT_URI
) system używa aktywnościACTION_MAIN
dla kategoriiCATEGORY_LEANBACK_LAUNCHER
, zwykle zdefiniowanej w pliku manifestu aplikacji. Jeśli aktywność nie zostanie zdefiniowana, pojawi się link niedziałający aplikacji – jeśli użytkownik go kliknie, nic się nie stanie. - W przypadku tekstu opisu (
COLUMN_APP_LINK_TEXT
) system używa opcji „Otwórz app-name”. Jeśli nie zdefiniowany jest prawidłowy identyfikator URI intencji linku aplikacji, system użyje „Brak dostępnego linku”. - W przypadku koloru uzupełniającego (
COLUMN_APP_LINK_COLOR
) system używa domyślnego koloru aplikacji. - W przypadku obrazu plakatu (
COLUMN_APP_LINK_POSTER_ART_URI
) system używa banera na ekranie głównym aplikacji. Jeśli aplikacja nie ma banera, system użyje domyślnego obrazu aplikacji telewizyjnej. - W przypadku ikony plakietki (
COLUMN_APP_LINK_ICON_URI
) system używa plakietki z nazwą aplikacji. Jeśli jako obraz plakatu system używa też banera aplikacji lub domyślnego obrazu aplikacji, plakietka aplikacji nie jest widoczna.
Szczegóły linku aplikacji do kanałów określasz w ustawieniach konfiguracji aplikacji. Szczegóły linku aplikacji możesz zaktualizować w dowolnym momencie. Jeśli więc link aplikacji musi pasować do zmian w kanale, zaktualizuj szczegóły linku aplikacji i w razie potrzeby wywołaj ContentResolver.update()
. Więcej informacji o aktualizowaniu danych kanału znajdziesz w artykule Aktualizowanie danych kanału.