Dane wejściowe telewizora muszą w ramach konfiguracji udostępniać dane elektronicznego przewodnika po programach (EPG) co najmniej 1 kanału. Musisz też okresowo aktualizować te dane, biorąc pod uwagę rozmiar aktualizacji i wątek przetwarzania, który ją obsługuje. Dodatkowo możesz podać linki do aplikacji dla kanałów, które kierują użytkownika do powiązanych treści i działań. Z tego artykułu dowiesz się, jak tworzyć i aktualizować dane kanałów i programów w systemowej bazie danych, biorąc pod uwagę te kwestie.
Wypróbuj przykładową aplikację TV Input Service.
Uzyskiwanie uprawnień
Aby dane wejściowe telewizora mogły korzystać z danych EPG, muszą zadeklarować uprawnienie do zapisu w pliku manifestu Androida w ten sposób:
<uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />
Rejestrowanie kanałów w bazie danych
Systemowa baza danych Androida TV przechowuje rekordy danych kanałów dla danych wejściowych telewizora. W ramach konfiguracji dla każdego kanału musisz przypisać dane kanału do tych pól klasy TvContract.Channels:
COLUMN_DISPLAY_NAME– wyświetlana nazwa kanału .COLUMN_DISPLAY_NUMBER– wyświetlany numer kanałuCOLUMN_INPUT_ID– identyfikator usługi danych wejściowych telewizora.COLUMN_SERVICE_TYPE– typ usługi kanału.COLUMN_TYPE– typ standardu transmisji kanałuCOLUMN_VIDEO_FORMAT– domyślny format wideo kanału.
Chociaż framework danych wejściowych telewizora jest wystarczająco ogólny, aby obsługiwać zarówno tradycyjne transmisje, jak i treści OTT bez rozróżniania ich, możesz dodatkowo zdefiniować te kolumny, aby lepiej identyfikować tradycyjne kanały transmisji:
COLUMN_ORIGINAL_NETWORK_ID– identyfikator sieci telewizyjnej .COLUMN_SERVICE_ID– identyfikator usługi.COLUMN_TRANSPORT_STREAM_ID– identyfikator strumienia transportowego .
Jeśli chcesz podać szczegóły linku do aplikacji dla swoich kanałów, musisz zaktualizować kilka dodatkowych pól. Więcej informacji o polach linków do aplikacji znajdziesz w sekcji Dodawanie informacji o linkach do aplikacji.
W przypadku danych wejściowych telewizora opartych na strumieniowaniu internetowym przypisz odpowiednio własne wartości, aby każdy kanał można było jednoznacznie zidentyfikować.
Pobierz metadane kanału (w formacie XML, JSON lub innym) z serwera backendu, a następnie w ramach konfiguracji przypisz wartości do systemowej bazy danych w ten 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 tym przykładzie channel to obiekt, który zawiera metadane kanału z serwera backendu.
Wyświetlanie informacji o kanałach i programach
Systemowa aplikacja TV wyświetla użytkownikom informacje o kanałach i programach, gdy przełączają się między kanałami, jak pokazano na ilustracji 1. Aby informacje o kanałach i programach działały z modułem wyświetlania informacji o kanałach i programach w systemowej aplikacji TV, postępuj zgodnie z tymi wskazówkami:
- Numer kanału (
COLUMN_DISPLAY_NUMBER) - Ikona
(
android:iconw manifeście danych wejściowych telewizora) - Opis programu (
COLUMN_SHORT_DESCRIPTION) - Tytuł programu (
COLUMN_TITLE) - Logo kanału (
TvContract.Channels.Logo)- Użyj koloru #EEEEEE, aby dopasować go do otaczającego tekstu.
- Nie dodawaj dopełnienia.
- Grafika plakatu (
COLUMN_POSTER_ART_URI) - Format obrazu od 16:9 do 4:3.
Systemowa aplikacja TV udostępnia te same informacje w przewodniku po programach, w tym grafikę plakatu, jak pokazano na ilustracji 2.
Aktualizowanie danych kanału
Podczas aktualizowania istniejących danych kanału użyj metody update zamiast usuwać i ponownie dodawać dane. Aktualną wersję danych możesz zidentyfikować, używając Channels.COLUMN_VERSION_NUMBER i Programs.COLUMN_VERSION_NUMBER podczas wybierania rekordów do zaktualizowania.
Uwaga: dodawanie danych kanału do ContentProvider może zająć trochę czasu. Aktualne programy (te, które będą emitowane w ciągu 2 godzin od bieżącego czasu) dodawaj tylko wtedy, gdy skonfigurujesz EpgSyncJobService tak, aby aktualizował pozostałe dane kanału w tle. Przykład znajdziesz w
przykładowej aplikacji
Android TV Live TV.
Wczytywanie danych kanału w trybie wsadowym
Podczas aktualizowania systemowej bazy danych dużą ilością danych kanału użyj metody ContentResolver
applyBatch lub bulkInsert. Oto przykład użycia 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(); } }
Asynchroniczne przetwarzanie danych kanału
Manipulowanie danymi, np. pobieranie strumienia z serwera lub uzyskiwanie dostępu do bazy danych, nie powinno blokować wątku UI. Jednym ze sposobów na asynchroniczne wykonywanie aktualizacji jest użycie AsyncTask. Na przykład podczas wczytywania informacji o kanale z serwera backendu możesz użyć 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życie
WorkManager
aby uruchamiać proces aktualizacji w czasie bezczynności, np. codziennie o 3:00.
Inne techniki oddzielania zadań aktualizacji danych od wątku UI obejmują użycie klasy
HandlerThread lub zaimplementowanie własnej za pomocą klas Looper
i Handler. Więcej informacji znajdziesz w artykule
Procesy i wątki.
Dodawanie informacji o linkach do aplikacji
Kanały mogą używać linków do aplikacji, aby umożliwić użytkownikom uruchamianie powiązanej aktywności podczas oglądania treści kanału. Aplikacje kanałów używają linków do aplikacji, aby zwiększyć zaangażowanie użytkowników, uruchamiając aktywności, które wyświetlają powiązane informacje lub dodatkowe treści. Na przykład możesz użyć linków do aplikacji, aby:
- zachęcić użytkownika do odkrywania i kupowania powiązanych treści;
- udostępniać dodatkowe informacje o aktualnie odtwarzanych treściach;
- podczas oglądania treści odcinkowych rozpocząć oglądanie następnego odcinka serii;
- umożliwić użytkownikowi interakcję z treściami – np. ocenianie lub recenzowanie treści – bez przerywania odtwarzania.
Linki do aplikacji są wyświetlane, gdy użytkownik naciśnie Wybierz , aby wyświetlić menu telewizora podczas oglądania treści kanału.
Ilustracja 1. Przykład linku do aplikacji wyświetlanego w wierszu Kanały podczas wyświetlania treści kanału.
Gdy użytkownik wybierze link do aplikacji, system uruchomi aktywność za pomocą identyfikatora URI intencji określonego przez aplikację kanału. Treści kanału są odtwarzane, gdy aktywność linku do aplikacji jest aktywna. Użytkownik może wrócić do treści kanału, naciskając Wstecz.
Podawanie danych kanału linku do aplikacji
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 do aplikacji, określ te szczegóły w polach TvContract.Channels:
COLUMN_APP_LINK_COLOR– kolor uzupełniający linku aplikacji dla tego kanału. Przykład koloru uzupełniającego znajdziesz na ilustracji 2 w punkcie 3.COLUMN_APP_LINK_ICON_URI– identyfikator URI ikony plakietki aplikacji linku do aplikacji dla tego kanału. Przykład ikony plakietki aplikacji znajdziesz na ilustracji 2 w punkcie 2.COLUMN_APP_LINK_INTENT_URI– identyfikator URI intencji linku do aplikacji dla tego kanału. Identyfikator URI możesz utworzyć za pomocątoUri(int)zURI_INTENT_SCHEME, a następnie przekonwertować go z powrotem na pierwotną intencję za pomocąparseUri.COLUMN_APP_LINK_POSTER_ART_URI– identyfikator URI grafiki plakatu używanej jako tło linku do aplikacji dla tego kanału. Przykład obrazu plakatu znajdziesz na ilustracji 2 w punkcie 1.COLUMN_APP_LINK_TEXT– opisowy tekst linku do aplikacji dla tego kanału. Przykład opisu linku do aplikacji znajdziesz na ilustracji 2 w punkcie 3.
Jeśli dane kanału nie określają informacji o linku do aplikacji, system tworzy domyślny link do aplikacji. System wybiera domyślne szczegóły w ten sposób:
- W przypadku identyfikatora URI intencji (
COLUMN_APP_LINK_INTENT_URI) system używa aktywnościACTION_MAINdla kategoriiCATEGORY_LEANBACK_LAUNCHER, która jest zwykle zdefiniowana w manifeście aplikacji. Jeśli ta aktywność nie jest zdefiniowana, wyświetla się niedziałający link do aplikacji – jeśli użytkownik go kliknie, nic się nie stanie. - W przypadku tekstu opisowego
(
COLUMN_APP_LINK_TEXT) system używa tekstu „Otwórz app-name”. Jeśli nie zdefiniowano odpowiedniego identyfikatora URI intencji linku do aplikacji, system używa tekstu „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 ekranu głównego aplikacji. Jeśli aplikacja nie udostępnia banera, system używa domyślnego obrazu aplikacji TV. - W przypadku ikony plakietki (
COLUMN_APP_LINK_ICON_URI) system używa plakietki, która wyświetla nazwę aplikacji. Jeśli system używa też banera aplikacji lub domyślnego obrazu aplikacji jako obrazu plakatu, nie jest wyświetlana żadna plakietka aplikacji.
Szczegóły linku do aplikacji dla swoich kanałów określasz w ramach konfiguracji aplikacji. Te szczegóły linku do aplikacji możesz aktualizować w dowolnym momencie, więc jeśli link do aplikacji musi pasować do zmian kanału, zaktualizuj szczegóły linku do aplikacji i w razie potrzeby wywołaj ContentResolver.update. Więcej informacji o aktualizowaniu
danych kanału znajdziesz w sekcji Aktualizowanie danych kanału.