Praca z danymi kanału

Wejście TV musi zawierać dane przewodnika po programach elektronicznym (EPG) przez co najmniej jeden kanał w konfiguracji. Należy też okresowo aktualizować z uwzględnieniem rozmiaru aktualizacji i wątku przetwarzania która radzi sobie z tym zadaniem. Możesz też dodać linki do aplikacji dla kanałów które kierują użytkownika do powiązanych treści i działań. Z tej lekcji dowiesz się, jak tworzyć i aktualizować dane kanału i programu z uwzględnieniem tych kwestii.

Wypróbuj Przykładowa aplikacja do obsługi wejścia TV.

Przyznaj dostęp

Aby wejście TV działało z danymi EPG, musi zadeklarować uprawnienia do zapisu w pliku manifestu Androida w ten 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 przechowuje rejestry danych kanałów z wejściach telewizyjnych. W konfiguracji dla każdego ze swoich kanałów musisz zmapować dane kanału do następujących pól Zajęcia TvContract.Channels:

Chociaż struktura wejścia TV jest wystarczająco ogólna, by obsługiwać zarówno tradycyjne transmisje, treści Over-The-Top (OTT) bez rozróżniania, warto zdefiniować następujące kolumny w aby lepiej określić tradycyjne kanały telewizyjne:

Jeśli chcesz podać szczegóły linków do aplikacji swoich kanałów, musisz zaktualizować niektóre dodatkowe pola. Więcej informacji o polach linku aplikacji: Dodaj informacje o linku aplikacji.

W przypadku strumieniowych źródeł sygnału telewizyjnego z internetu przypisz do powyższych wartości własne wartości, każdy kanał można jednoznacznie zidentyfikować.

Pobierz metadane kanału (w formacie XML, JSON lub innym) z serwera backendu i podczas konfiguracji aktywność mapuje wartości na systemową bazę danych 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 z serwera backendu.

Prezentowanie informacji o kanale i programie

Systemowa aplikacja TV wyświetla użytkownikom informacje o kanałach i programach, gdy przeglądają kanały, jak widać na ilustracji 1. Aby upewnić się, że informacje o kanale i programie są zgodne z prezentera informacji o kanale i programie, zastosuj się do poniższych wskazówek.

  1. Numer kanału (COLUMN_DISPLAY_NUMBER)
  2. Ikona (android:icon w pliku manifestu wejścia TV)
  3. Opis programu (COLUMN_SHORT_DESCRIPTION)
  4. Tytuł programu (COLUMN_TITLE)
  5. Logo kanału (TvContract.Channels.Logo)
      .
    • Użyj koloru #EEEEEE, aby dopasować otaczający tekst.
    • Nie używaj dopełnienia
  6. Platka (COLUMN_POSTER_ART_URI)
      .
    • Format obrazu: od 16:9 do 4:3

Rysunek 1. Prezentujący informacje o kanale i programie w aplikacji systemowej.

Systemowa aplikacja TV udostępnia te same informacje w przewodniku po programach, w tym plakat, jak widać na rysunku 2.

Rysunek 2. Przewodnik po programach systemowych aplikacji TV.

Zaktualizuj dane kanału

Przy aktualizacji danych kanału skorzystaj z update() zamiast usuwać i ponownie dodawać dane. Możesz określić bieżącą wersję danych za pomocą funkcji Channels.COLUMN_VERSION_NUMBER i Programs.COLUMN_VERSION_NUMBER podczas wybierania rekordów do zaktualizowania.

Uwaga: dodaję dane kanału do ContentProvider może zająć trochę czasu. Dodaj bieżące programy (te znajdujące się w okresie 2 godzin od aktualnej godziny). tylko wtedy, gdy skonfigurujesz EpgSyncJobService, aby zaktualizować resztę z danymi kanałów w tle. Zobacz Przykładowa aplikacja Android TV Live TV.

Zbiorcze wczytywanie danych kanału

Gdy aktualizujesz systemową bazę danych za pomocą dużej ilości danych o kanałach, użyj funkcji 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();
    }
}

Asynchroniczne przetwarzanie danych kanału

Manipulacja danymi, np. pobieranie strumienia z serwera lub uzyskiwanie dostępu do bazy danych, powinna nie blokować wątku UI. Korzystanie z AsyncTask asynchronicznie. Na przykład podczas wczytywania informacji o kanale z serwera backendu Z usługi AsyncTask możesz korzystać w następujący 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, WorkManager , aby proces aktualizacji był uruchamiany w czasie bezczynności, np.codziennie o 3:00.

Inne techniki oddzielania zadań aktualizacji danych od wątku UI to użycie parametru HandlerThread. Możesz też wdrożyć własne za pomocą narzędzia Looper. i Handler zajęć. Zobacz procesy i wątki, aby dowiedzieć się więcej.

Kanały mogą korzystać z linków do aplikacji, aby umożliwić użytkownikom łatwe uruchamianie co robią podczas oglądania treści na kanale. Używane przez aplikacje kanału linków do aplikacji, aby zwiększać zaangażowanie użytkowników przez uruchamianie działań, które pokazują powiązane informacje lub dodatkowe treści. Możesz na przykład użyć linków do aplikacji, wykonaj następujące czynności:

  • Poinformuj użytkownika, jak odkrywać i kupować powiązane treści.
  • Podaj dodatkowe informacje o aktualnie odtwarzanych treściach.
  • Podczas oglądania treści w odcinkach zacznij oglądać następny odcinek serii.
  • Pozwól użytkownikowi na interakcję z treścią, np. ocenę lub opinię. bez przerywania odtwarzania.

Linki do aplikacji pojawiają się, gdy użytkownik naciśnie Wybierz, aby wyświetlić Menu telewizora podczas oglądania treści kanału.

Rysunek 1. Przykładowy link aplikacji w wierszu Kanały.

Gdy użytkownik kliknie link do aplikacji, system rozpocznie aktywność za pomocą parametru identyfikator URI intencji określony przez aplikację kanału. Treści na kanale są nadal odtwarzane gdy aktywność związana z linkiem do aplikacji jest aktywna. Użytkownik może wrócić na kanał. kliknij Wstecz.

Podawanie danych kanału linku aplikacji

Android TV automatycznie tworzy link do aplikacji dla każdego kanału, na podstawie informacji z danych kanału. Aby podać informacje o linku aplikacji: podaj następujące szczegóły w TvContract.Channels pola:

  • COLUMN_APP_LINK_COLOR kolor uzupełniający linku do aplikacji dla tego kanału Przykładem koloru uzupełniającego: patrz: rysunek 2, objaśnienie 3.
  • COLUMN_APP_LINK_ICON_URI – Identyfikator URI ikony plakietki aplikacji w linku do aplikacji na tym kanale. Dla przykładowa ikona plakietki aplikacji, patrz ilustracja 2, objaśnienie 2.
  • COLUMN_APP_LINK_INTENT_URI – Identyfikator URI intencji linku do aplikacji na tym kanale. Możesz utworzyć identyfikator URI używasz toUri(int) z URI_INTENT_SCHEME i skonwertuj identyfikator URI z powrotem na pierwotną intencję z parseUri()
  • COLUMN_APP_LINK_POSTER_ART_URI – Identyfikator URI plakatu używanego jako tło linku do aplikacji. dla tego kanału. Przykładowy obraz plakatu znajdziesz na ilustracji 2, objaśnienie 1.
  • COLUMN_APP_LINK_TEXT – Opisowy tekst linku do aplikacji na tym kanale. Przykład: opis linku aplikacji, zobacz tekst na rysunku 2, objaśnienie 3.

Rysunek 2. Szczegóły linku aplikacji.

Jeśli dane kanału nie zawierają informacji o linku aplikacji, system tworzy domyślny link aplikacji. System wybiera domyślne szczegóły w następujący sposób:

  • W przypadku identyfikatora URI intencji (COLUMN_APP_LINK_INTENT_URI), system używa funkcji ACTION_MAIN aktywność w kategorii CATEGORY_LEANBACK_LAUNCHER, zwykle zdefiniowanej w manifeście aplikacji. Jeśli aktywność nie jest zdefiniowana, pojawi się link niedziałającej aplikacji – użytkownik kliknie reklamę, ale nic się nie stanie.
  • Dla tekstu opisu (COLUMN_APP_LINK_TEXT), system korzysta z funkcji „Otwórz app-name”. Jeśli nie określono prawidłowego identyfikatora URI intencji linku aplikacji, system użyje stanu „Brak linku”.
  • Kolor uzupełniający: (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 udostępnia system używa domyślnego obrazu aplikacji telewizyjnej.
  • W przypadku ikony plakietki (COLUMN_APP_LINK_ICON_URI), wartość system używa plakietki z nazwą aplikacji. Jeśli system używa też atrybutu baner aplikacji lub domyślny obraz aplikacji w przypadku obrazu plakatu, plakietka aplikacji nie jest wyświetlana.

Szczegóły linku aplikacji dla kanałów określasz w sekcji Konfiguracja. Szczegóły linku do aplikacji możesz zaktualizować w każdej chwili, Jeśli link aplikacji musi pasować do zmian wprowadzonych w kanale, zaktualizuj aplikację Szczegóły połączenia i połączenie ContentResolver.update(). Więcej informacji o aktualizacjach Więcej informacji znajdziesz w artykule Aktualizowanie danych kanału.