Dein TV-Eingang muss Daten zum elektronischen Programmführer (Electronic Program Guide, EPG) für mindestens einen Kanal bei der Einrichtung bereitstellen. Außerdem sollten Sie diese Daten regelmäßig aktualisieren, wobei Sie die Größe der Aktualisierung und den Verarbeitungsthread berücksichtigen, der sie verarbeitet. Außerdem kannst du App-Links für Kanäle bereitstellen, über die Nutzer zu ähnlichen Inhalten und Aktivitäten gelangen. In dieser Lektion wird beschrieben, wie Sie Kanal- und Programmdaten in der Systemdatenbank unter Berücksichtigung dieser Überlegungen erstellen und aktualisieren.
Probieren Sie die Beispiel-App TV Input Service aus.
Berechtigung einholen
Damit Ihre TV-Eingabe mit EPG-Daten funktioniert, muss die Schreibberechtigung in der zugehörigen Android-Manifestdatei wie folgt deklariert werden:
<uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />
Kanäle in der Datenbank registrieren
Die Android TV-Systemdatenbank verwaltet Datensätze von Kanaldaten für TV-Eingaben. In den Einrichtungsaktivitäten müssen Sie die Kanaldaten für jeden Kanal den folgenden Feldern der Klasse TvContract.Channels
zuordnen:
COLUMN_DISPLAY_NAME
– der angezeigte Name des KanalsCOLUMN_DISPLAY_NUMBER
– die angezeigte KanalnummerCOLUMN_INPUT_ID
– die ID des TV-EingabedienstesCOLUMN_SERVICE_TYPE
– der Diensttyp des KanalsCOLUMN_TYPE
– der Übertragungsstandardtyp des KanalsCOLUMN_VIDEO_FORMAT
: das Standardvideoformat für den Kanal
Obwohl das TV-Eingabe-Framework generisch genug ist, um sowohl herkömmliche Rundfunk- als auch OTT-Inhalte ohne Unterscheidung zu verarbeiten, können Sie die folgenden Spalten zusätzlich zu den obigen Spalten definieren, um herkömmliche Übertragungskanäle besser zu identifizieren:
COLUMN_ORIGINAL_NETWORK_ID
ist die ID des Fernsehsenders.COLUMN_SERVICE_ID
– die Dienst-IDCOLUMN_TRANSPORT_STREAM_ID
– die Transportstream-ID
Wenn du Details zu App-Links für deine Kanäle angeben möchtest, musst du einige zusätzliche Felder aktualisieren. Weitere Informationen zu Feldern für App-Links finden Sie unter Informationen zu App-Links hinzufügen.
Weisen Sie für TV-Eingaben, die auf Internetstreaming basieren, Ihre eigenen Werte zu, damit jeder Kanal eindeutig identifiziert werden kann.
Rufen Sie die Kanalmetadaten (in XML, JSON usw.) von Ihrem Back-End-Server ab und ordnen Sie die Werte in Ihrer Einrichtungsaktivität der Systemdatenbank zu:
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);
Im obigen Beispiel ist channel
ein Objekt, das Kanalmetadaten vom Back-End-Server enthält.
Kanal- und Programminformationen präsentieren
Die System-TV-App zeigt Nutzern beim Blättern durch Kanäle Kanal- und Programminformationen an (siehe Abbildung 1). Damit die Kanal- und Programminformationen mit dem Kanal- und Programminformationsfeed der System-TV-App funktionieren, beachte die folgenden Richtlinien.
- Kanalnummer (
COLUMN_DISPLAY_NUMBER
) - Symbol (
android:icon
im Manifest der TV-Eingabe) - Programmbeschreibung (
COLUMN_SHORT_DESCRIPTION
) - Programmtitel (
COLUMN_TITLE
) - Kanallogo (
TvContract.Channels.Logo
)- Verwenden Sie die Farbe #EEEEEE, um sie an den umgebenden Text anzupassen.
- Keinen Abstand einfügen
- Poster (
COLUMN_POSTER_ART_URI
)- Seitenverhältnis zwischen 16:9 und 4:3
Die System-TV-App stellt über die Programmübersicht dieselben Informationen bereit, einschließlich Postergrafiken, wie in Abbildung 2 dargestellt.
Kanaldaten aktualisieren
Wenn Sie vorhandene Kanaldaten aktualisieren, sollten Sie die Methode update()
verwenden, anstatt die Daten zu löschen und neu hinzuzufügen. Sie können die aktuelle Version der Daten mithilfe von Channels.COLUMN_VERSION_NUMBER
und Programs.COLUMN_VERSION_NUMBER
ermitteln, wenn Sie die zu aktualisierenden Datensätze auswählen.
Hinweis:Das Hinzufügen von Kanaldaten zu ContentProvider
kann einige Zeit dauern. Füge aktuelle Programme (die innerhalb von zwei Stunden ab der aktuellen Uhrzeit) nur hinzu, wenn du EpgSyncJobService
so konfigurierst, dass die restlichen Kanaldaten im Hintergrund aktualisiert werden. Ein Beispiel hierfür ist die
Beispiel-App „Android TV Live TV“.
Kanaldaten im Batch laden
Verwenden Sie zum Aktualisieren der Systemdatenbank mit großen Mengen an Kanaldaten die Methode ContentResolver
applyBatch()
oder bulkInsert()
. Hier ein Beispiel mit 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(); } }
Kanaldaten asynchron verarbeiten
Datenmanipulationen wie das Abrufen eines Streams vom Server oder der Zugriff auf die Datenbank sollten den UI-Thread nicht blockieren. Die Verwendung von AsyncTask
ist eine Möglichkeit, Aktualisierungen asynchron durchzuführen. Wenn Sie beispielsweise Kanalinformationen von einem Back-End-Server laden, können Sie AsyncTask
so verwenden:
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(); } } } }
Wenn Sie die EPG-Daten regelmäßig aktualisieren müssen, sollten Sie WorkManager
verwenden, um den Aktualisierungsprozess bei Inaktivität auszuführen, z. B. täglich um 3:00 Uhr.
Andere Techniken zum Trennen der Aufgaben zur Datenaktualisierung vom UI-Thread sind die Verwendung der Klasse HandlerThread
. Sie können auch Ihre eigenen mit den Klassen Looper
und Handler
implementieren. Weitere Informationen finden Sie unter
Prozesse und Threads.
Informationen zum App-Link hinzufügen
Kanäle können App-Links verwenden, mit denen Nutzer auf einfache Weise eine ähnliche Aktivität starten können, während sie sich Kanalinhalte ansehen. Kanal-Apps verwenden App-Links, um die Nutzerinteraktion durch Aktivitäten zu steigern, die zugehörige Informationen oder zusätzliche Inhalte enthalten. Sie können App-Links beispielsweise für Folgendes verwenden:
- Leiten Sie den Nutzer dazu, ähnliche Inhalte zu entdecken und zu kaufen.
- Geben Sie zusätzliche Informationen zu aktuell wiedergegebenen Inhalten an.
- Beginne während der Wiedergabe von Inhalten im Serienformat die nächste Folge einer Serie.
- Ermöglichen Sie den Nutzern, mit Inhalten zu interagieren (z. B. Inhalte zu bewerten oder zu überprüfen), ohne die Wiedergabe zu unterbrechen.
App-Links werden angezeigt, wenn der Nutzer während der Wiedergabe von Kanalinhalten auf Auswählen drückt, um das TV-Menü aufzurufen.
Wenn der Nutzer den App-Link auswählt, startet das System eine Aktivität mithilfe eines von der Kanal-App angegebenen Intent-URI. Kanalinhalte werden weiter abgespielt, während die App-Link-Aktivität aktiv ist. Der Nutzer kann durch Drücken der Zurück-Taste zum Kanalinhalt zurückkehren.
Daten zum App-Link-Kanal angeben
Android TV erstellt mithilfe von Informationen aus den Kanaldaten automatisch einen App-Link für jeden Kanal. Geben Sie in den TvContract.Channels
-Feldern die folgenden Details an, um Informationen zu App-Links bereitzustellen:
COLUMN_APP_LINK_COLOR
: Die Akzentfarbe des App-Links für diesen Kanal. Ein Beispiel für eine Akzentfarbe siehe Abbildung 2, Legende 3.COLUMN_APP_LINK_ICON_URI
: Der URI für das App-Badge-Symbol des App-Links für diesen Kanal. Ein Beispiel für ein App-Badge-Symbol findest du in Abbildung 2, Zusatzinformationen 2.COLUMN_APP_LINK_INTENT_URI
: Der Intent-URI des App-Links für diesen Kanal. Sie können den URI mittoUri(int)
undURI_INTENT_SCHEME
erstellen und den URI mitparseUri()
wieder in den ursprünglichen Intent konvertieren.COLUMN_APP_LINK_POSTER_ART_URI
– Der URI für die Postergrafiken, die als Hintergrund des App-Links für diesen Kanal verwendet werden. Ein Beispiel für ein Posterbild findest du in Abbildung 2, Zusatzinformationen 1.COLUMN_APP_LINK_TEXT
: der beschreibende Linktext des App-Links für diesen Kanal. Ein Beispiel für die Beschreibung eines App-Links findest du in Abbildung 2, Zusatzinformation 3.
Wenn in den Kanaldaten keine Informationen zur App-Verknüpfung angegeben sind, erstellt das System eine standardmäßige App-Verknüpfung. Das System wählt die Standarddetails so aus:
- Für den Intent-URI (
COLUMN_APP_LINK_INTENT_URI
) verwendet das System die AktivitätACTION_MAIN
für die KategorieCATEGORY_LEANBACK_LAUNCHER
, die in der Regel im App-Manifest definiert ist. Wenn diese Aktivität nicht definiert ist, wird ein nicht funktionierender App-Link angezeigt. Klickt der Nutzer darauf, passiert nichts. - Für den beschreibenden Text (
COLUMN_APP_LINK_TEXT
) verwendet das System „Open app-name“. Wenn kein gültiger URI für den App-Link definiert ist, verwendet das System „Kein Link verfügbar“. - Für die Akzentfarbe (
COLUMN_APP_LINK_COLOR
) verwendet das System die Standard-App-Farbe. - Für das Posterbild (
COLUMN_APP_LINK_POSTER_ART_URI
) verwendet das System das Startbildschirmbanner der App. Wenn die App kein Banner bereitstellt, verwendet das System ein Standardbild für die TV-App. - Für das Logosymbol (
COLUMN_APP_LINK_ICON_URI
) verwendet das System ein Logo, das den App-Namen anzeigt. Wenn das System zusätzlich das App-Banner oder das Standard-App-Bild für das Posterbild verwendet, wird kein App-Logo angezeigt.
Details zum App-Link für Ihre Kanäle geben Sie bei der Einrichtungsaktivität Ihrer App an. Du kannst die Details zur App-Verknüpfung jederzeit aktualisieren. Wenn ein App-Link also mit Kanaländerungen übereinstimmen muss, aktualisiere die Details zur App-Verknüpfung und rufe ContentResolver.update()
nach Bedarf auf. Weitere Informationen zum Aktualisieren von Kanaldaten finden Sie unter Kanaldaten aktualisieren.