Mit Kanaldaten arbeiten

Deine TV-Eingabe muss Daten des elektronischen Programmführers (Electronic Program Guide, EPG) für mindestens für einen Kanal eingerichtet haben. Sie sollten auch diese regelmäßig aktualisieren, Daten unter Berücksichtigung der Größe des Updates und des Verarbeitungs-Threads das sich darum kümmert. Außerdem können Sie App-Links für Kanäle die Nutzende zu ähnlichen Inhalten und Aktivitäten führen. In dieser Lektion erfahren Sie, wie Sie Kanal- und Programmdaten auf der Systemdatenbank diese Überlegungen im Hinterkopf.

Probieren Sie die aus. Beispiel-App „TV Input Service“

Berechtigung einholen

Damit auf Ihrem TV-Eingang Daten aus dem elektronischen Programmführer verarbeitet werden können, muss Folgendes deklariert werden: Schreibberechtigung in der Android-Manifestdatei wie folgt:

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

Kanäle in der Datenbank registrieren

In der Android TV-Systemdatenbank werden Datensätze der Kanaldaten für TV-Eingaben gespeichert. In der Einrichtung -Aktivität ist, müssen Sie Ihre Channel-Daten für jeden Channel folgenden Feldern der Spalte Klasse TvContract.Channels:

Obwohl das TV-Eingabesystem allgemein genug ist, um sowohl traditionelle Fernsehsendungen als auch Over-the-Top-Inhalte (OTT) ohne Unterscheidung verwenden möchten, können Sie die folgenden Spalten in ergänzen, um herkömmliche Fernsehkanäle besser identifizieren zu können:

Wenn du Details zu App-Links für deine Kanäle angeben möchtest, musst du um einige zusätzliche Felder zu aktualisieren. Weitere Informationen zu Feldern für App-Links finden Sie unter Fügen Sie Informationen zu App-Links hinzu.

Weisen Sie bei TV-Eingängen, die auf Internetstreaming basieren, dem Obengen Ihre eigenen Werte zu, damit kann jeder Channel eindeutig identifiziert werden.

Rufe deine Kanalmetadaten (in XML, JSON usw.) von deinem Backend-Server und in deiner Einrichtung ab. werden die Werte folgendermaßen der Systemdatenbank zugeordnet:

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 aus dem Back-End-Server.

Kanal- und Programminformationen präsentieren

Die System TV App zeigt Nutzern beim Blättern durch Kanäle Kanal- und Programminformationen an. wie in Abbildung 1 dargestellt. Um sicherzugehen, dass Kanal- und Programminformationen mit den Programmfunktionen der System TV App kompatibel sind, Präsentation von Kanal- und Programminformationen gemäß den unten stehenden Richtlinien.

  1. Kanalnummer (COLUMN_DISPLAY_NUMBER)
  2. Symbol (android:icon in der TV-Eingabe-Manifest)
  3. Programmbeschreibung (COLUMN_SHORT_DESCRIPTION)
  4. Programmtitel (COLUMN_TITLE)
  5. Kanallogo (TvContract.Channels.Logo) <ph type="x-smartling-placeholder">
      </ph>
    • Verwenden Sie die Farbe #EEEEEE zur Anpassung an den umgebenden Text
    • Keine Abstände hinzufügen
  6. Poster (COLUMN_POSTER_ART_URI) <ph type="x-smartling-placeholder">
      </ph>
    • Seitenverhältnis zwischen 16:9 und 4:3

Abbildung 1: Kanal- und Programminformationen des Systems für die TV-App.

Die System-TV-App bietet dieselben Informationen über die Programmübersicht, einschließlich Postergrafiken, wie in Abbildung 2 dargestellt.

Abbildung 2: Programmübersicht der System TV App

Kanaldaten aktualisieren

Verwenden Sie beim Aktualisieren vorhandener Channel-Daten die Methode update() anstatt die Daten zu löschen und wieder hinzuzufügen. Sie können die aktuelle Version der Daten mit Channels.COLUMN_VERSION_NUMBER und Programs.COLUMN_VERSION_NUMBER bei der Auswahl der zu aktualisierenden Einträge.

Hinweis: Kanaldaten werden zum ContentProvider hinzugefügt. kann einige Zeit in Anspruch nehmen. Aktuelle Programme hinzufügen (im Umkreis von zwei Stunden ab der aktuellen Uhrzeit) wenn du dein EpgSyncJobService so konfigurierst, dass der Rest aktualisiert wird der Kanaldaten im Hintergrund. Weitere Informationen finden Sie unter die Android TV Live TV Sample App.

Kanaldaten im Batch laden

Wenn du die Systemdatenbank mit einer großen Menge an Kanaldaten aktualisierst, verwende die 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

Datenmanipulation, z. B. das Abrufen eines Streams vom Server oder der Zugriff auf die Datenbank, sollte den UI-Thread nicht blockieren. Ein AsyncTask wird verwendet Aktualisierungen asynchron durchführen. Wenn du beispielsweise Kanalinformationen von einem Backend-Server lädst, 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 EPG-Daten regelmäßig aktualisieren müssen, sollten Sie WorkManager um den Aktualisierungsvorgang bei Inaktivität auszuführen, z. B. täglich um 3:00 Uhr.

Weitere Techniken, um die Datenaktualisierungsaufgaben vom UI-Thread zu trennen, sind unter anderem die Verwendung des HandlerThread-Klasse, oder Sie können Ihre eigene mit Looper implementieren. und Handler Klassen. Weitere Informationen finden Sie unter . Prozesse und Threads.

Kanäle können App-Links verwenden, damit Nutzer auf einfache Weise eine ähnliche während sie sich Kanalinhalte ansehen. Kanal-Apps verwenden um das Nutzer-Engagement zu erhöhen, indem Sie Aktivitäten starten, oder zusätzliche Inhalte. Zum Beispiel können Sie App-Links um Folgendes zu tun:

  • Nutzer anleiten, ähnliche Inhalte zu entdecken und zu kaufen
  • Hier kannst du zusätzliche Informationen zu den aktuell wiedergegebenen Inhalten angeben.
  • Bei der Wiedergabe von Serieninhalten kannst du die nächste Folge in einer .
  • Lassen Sie den Nutzer mit Inhalten interagieren, z. B. Bewerten oder Erfahrungsberichte schreiben. ohne die Wiedergabe zu unterbrechen.

App-Links werden angezeigt, wenn der Nutzer auf Auswählen drückt. TV-Menü während der Wiedergabe von Kanalinhalten.

Abbildung 1: Beispiel für einen App-Link wird in der Zeile Kanäle angezeigt, während Kanalinhalte angezeigt werden.

Wenn der Nutzer den App-Link auswählt, startet das System eine Aktivität mit einen Intent-URI, der von der Kanal-App angegeben wird. Inhalte des Kanals werden weiterhin wiedergegeben während die App-Link-Aktivität aktiv ist. Der Nutzer kann zum Kanal zurückkehren. indem Sie auf Zurück drücken.

App-Link-Kanaldaten angeben

Android TV erstellt automatisch einen App-Link für jeden Kanal, anhand von Informationen aus den Kanaldaten. Um Informationen zu App-Links bereitzustellen, geben Sie die folgenden Details in Ihrem TvContract.Channels-Felder:

  • COLUMN_APP_LINK_COLOR: Die Akzentfarbe des App-Links für diesen Kanal. Ein Beispiel für eine Akzentfarbe siehe Abbildung 2, Zusatzinformationen 3.
  • COLUMN_APP_LINK_ICON_URI – Der URI für das App-Badge-Symbol des App-Links für diesen Kanal. Für eine Beispiel für ein App-Badge, siehe Abbildung 2, Zusatzinformationen 2.
  • COLUMN_APP_LINK_INTENT_URI – Der Intent-URI des App-Links für diesen Kanal. Sie können den URI erstellen, Verwendung von toUri(int) mit URI_INTENT_SCHEME und konvertieren Sie den URI zurück in den ursprünglichen Intent mit parseUri()
  • COLUMN_APP_LINK_POSTER_ART_URI – URI für das Poster, das als Hintergrund des App-Links verwendet wird für diesen Kanal. Ein Beispiel für ein Posterbild finden Sie in Abbildung 2, Zusatzinformationen 1.
  • COLUMN_APP_LINK_TEXT – Der beschreibende Linktext des App-Links für diesen Kanal. Beispiel App-Link-Beschreibung, siehe Text in Abbildung 2, Zusatzinformationen 3.

Abbildung 2: Details zum App-Link.

Wenn in den Kanaldaten keine App-Link-Informationen angegeben sind, wird ein Standard-App-Link erstellt. Das System wählt die Standarddetails wie folgt aus:

  • Für den Intent-URI (COLUMN_APP_LINK_INTENT_URI), verwendet das System die ACTION_MAIN Aktivität für die Kategorie CATEGORY_LEANBACK_LAUNCHER, in der Regel im App-Manifest definiert. Wenn diese Aktivität nicht definiert ist, wird ein nicht funktionierender App-Link angezeigt, sofern wenn der Nutzer darauf klickt, passiert nichts.
  • Für den beschreibenden Text (COLUMN_APP_LINK_TEXT), das System verwendet „app-name öffnen“. Wenn kein gültiger URI für den App-Link-Intent 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 Banner für den Startbildschirm der App. Wenn die App keine Banner verwenden, verwendet das System ein Standardbild für die TV-App.
  • Für das Logosymbol (COLUMN_APP_LINK_ICON_URI), die verwendet ein Logo, das den App-Namen zeigt. Verwendet das System auch die App-Banner oder Standard-App-Bild für das Posterbild, es wird kein App-Logo angezeigt.

App-Linkdetails für Ihre Kanäle geben Sie in der Einrichtungsaktivität. Sie können diese App-Linkdetails jederzeit aktualisieren. App-Link aktualisieren, wenn ein App-Link mit Kanaländerungen übereinstimmen muss Linkdetails und Anruf ContentResolver.update() nach Bedarf. Weitere Informationen zum Aktualisieren Weitere Informationen zum Aktualisieren von Kanaldaten