Utilizza i dati del canale

L'input TV deve fornire i dati della guida ai programmi elettronica (EPG) per almeno un canale nella sua attività di configurazione. Dovresti anche aggiornare periodicamente questi dati, tenendo conto delle dimensioni dell'aggiornamento e del thread di elaborazione che lo gestisce. Inoltre, puoi fornire link alle app per i canali che guidano l'utente a contenuti e attività correlate. Questa lezione illustra la creazione e l'aggiornamento dei dati di canali e programmi nel database di sistema tenendo conto di queste considerazioni.

Prova l' app di esempio del servizio di input TV.

Ottieni autorizzazione

Affinché l'input TV funzioni con i dati EPG, deve dichiarare l' autorizzazione di scrittura nel file manifest di Android come segue:

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

Registra i canali nel database

Il database di sistema di Android TV conserva i record dei dati dei canali per gli input TV. Nell'attività di configurazione, per ciascuno dei tuoi canali, devi mappare i dati del canale ai seguenti campi della classe TvContract.Channels:

Sebbene il framework di input TV sia sufficientemente generico da gestire sia i contenuti di trasmissione tradizionali sia i contenuti over-the-top (OTT) senza distinzioni, potresti voler definire le seguenti colonne in aggiunta per identificare meglio i canali di trasmissione tradizionali:

Se vuoi fornire i dettagli dei link alle app per i tuoi canali, devi aggiornare alcuni campi aggiuntivi. Per ulteriori informazioni sui campi dei link alle app, vedi Aggiungi informazioni sui link alle app.

Per gli input TV basati sullo streaming internet, assegna i tuoi valori di conseguenza in modo che ogni canale possa essere identificato in modo univoco.

Estrai i metadati del canale (in XML, JSON o altro) dal server di backend e, nell'attività di configurazione, mappa i valori al database di sistema come segue:

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);

In questo esempio, channel è un oggetto che contiene i metadati del canale dal server di backend.

Presenta le informazioni su canali e programmi

L'app TV di sistema presenta agli utenti le informazioni su canali e programmi mentre scorrono i canali, come mostrato nella Figura 1. Per assicurarti che le informazioni su canali e programmi funzionino con il presentatore di informazioni su canali e programmi dell'app TV di sistema, segui queste linee guida:

  1. Numero del canale (COLUMN_DISPLAY_NUMBER)
  2. Icona (android:icon nel manifest dell'input TV)
  3. Descrizione del programma (COLUMN_SHORT_DESCRIPTION)
  4. Titolo del programma (COLUMN_TITLE)
  5. Logo del canale (TvContract.Channels.Logo)
    • Utilizza il colore #EEEEEE per abbinarlo al testo circostante
    • Non includere il padding
  6. Locandina (COLUMN_POSTER_ART_URI)
    • Proporzioni tra 16:9 e 4:3
Figura 1. Il presentatore di informazioni su canali e programmi dell'app TV di sistema.

L'app TV di sistema fornisce le stesse informazioni tramite la guida ai programmi, inclusa la locandina, come mostrato nella Figura 2.

Figura 2. La guida ai programmi dell'app TV di sistema.

Aggiorna i dati del canale

Quando aggiorni i dati del canale esistenti, utilizza il metodo update anziché eliminare e aggiungere di nuovo i dati. Puoi identificare la versione corrente dei dati utilizzando Channels.COLUMN_VERSION_NUMBER e Programs.COLUMN_VERSION_NUMBER quando scegli i record da aggiornare.

Nota: l'aggiunta di dati dei canali al ContentProvider può richiedere tempo. Aggiungi i programmi attuali (quelli entro due ore dall'ora corrente) solo quando configuri EpgSyncJobService per aggiornare il resto dei dati del canale in background. Per un esempio, consulta l' app di esempio di TV in diretta di Android TV.

Carica in batch i dati dei canali

Quando aggiorni il database di sistema con una grande quantità di dati dei canali, utilizza il metodo ContentResolver applyBatch o bulkInsert. Ecco un esempio che utilizza 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();
    }
}

Elabora i dati dei canali in modo asincrono

La manipolazione dei dati, ad esempio il recupero di uno stream dal server o l'accesso al database, non deve bloccare il thread dell'interfaccia utente. L'utilizzo di AsyncTask è un modo per eseguire gli aggiornamenti in modo asincrono. Ad esempio, quando carichi le informazioni sul canale da un server di backend, puoi utilizzare AsyncTask come segue:

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();
            }
        }
    }
}

Se devi aggiornare regolarmente i dati EPG, valuta la possibilità di utilizzare WorkManager per eseguire il processo di aggiornamento durante il tempo di inattività, ad esempio ogni giorno alle 03:00.

Altre tecniche per separare le attività di aggiornamento dei dati dal thread dell'interfaccia utente includono l'utilizzo della HandlerThread classe, oppure puoi implementare la tua utilizzando Looper e Handler classi. Per ulteriori informazioni, consulta la sezione Processi e thread.

I canali possono utilizzare i link alle app per consentire agli utenti di avviare un'attività correlata mentre guardano i contenuti del canale. Le app dei canali utilizzano i link alle app per estendere il coinvolgimento degli utenti avviando attività che mostrano informazioni correlate o contenuti aggiuntivi. Ad esempio, puoi utilizzare i link alle app per:

  • Guidare l'utente alla scoperta e all'acquisto di contenuti correlati.
  • Fornire informazioni aggiuntive sui contenuti in riproduzione.
  • Durante la visualizzazione di contenuti episodici, avvia la visualizzazione dell'episodio successivo di una serie.
  • Consentire all'utente di interagire con i contenuti, ad esempio valutandoli o recensendoli, senza interrompere la riproduzione dei contenuti.

I link alle app vengono visualizzati quando l'utente preme Seleziona per mostrare il menu TV mentre guarda i contenuti del canale.

Figura 1. Un esempio di link all'app visualizzato nella riga Canali mentre vengono mostrati i contenuti del canale.

Quando l'utente seleziona il link all'app, il sistema avvia un'attività utilizzando un URI intent specificato dall'app del canale. I contenuti del canale continuano a essere riprodotti mentre l'attività del link all'app è attiva. L'utente può tornare ai contenuti del canale premendo Indietro.

Fornisci i dati dei canali dei link alle app

Android TV crea automaticamente un link all'app per ogni canale, utilizzando le informazioni dei dati del canale. Per fornire informazioni sui link alle app, specifica i seguenti dettagli nei campi TvContract.Channels:

  • COLUMN_APP_LINK_COLOR - Il colore intenso del link all'app per questo canale. Per un esempio di colore intenso, vedi la Figura 2, callout 3.
  • COLUMN_APP_LINK_ICON_URI - L'URI dell'icona del badge dell'app del link all'app per questo canale. Per un esempio di icona del badge dell'app, vedi la Figura 2, callout 2.
  • COLUMN_APP_LINK_INTENT_URI - L'URI intent del link all'app per questo canale. Puoi creare l'URI utilizzando toUri(int) con URI_INTENT_SCHEME e convertire l'URI di nuovo nell'intent originale con parseUri.
  • COLUMN_APP_LINK_POSTER_ART_URI - L'URI della locandina utilizzata come sfondo del link all'app per questo canale. Per un esempio di immagine poster, vedi la Figura 2, callout 1.
  • COLUMN_APP_LINK_TEXT - Il testo descrittivo del link all'app per questo canale. Per un esempio di descrizione del link all'app, vedi il testo nella Figura 2, callout 3.
Figura 2. Dettagli del link all'app.

Se i dati del canale non specificano le informazioni sui link alle app, il sistema crea un link all'app predefinito. Il sistema sceglie i dettagli predefiniti come segue:

  • Per l'URI intent (COLUMN_APP_LINK_INTENT_URI), il sistema utilizza l'attività ACTION_MAIN per la categoria CATEGORY_LEANBACK_LAUNCHER, in genere definita nel manifest dell'app. Se questa attività non è definita, viene visualizzato un link all'app non funzionante: se l'utente fa clic, non succede nulla.
  • Per il testo descrittivo (COLUMN_APP_LINK_TEXT), il sistema utilizza "Apri app-name". Se non è definito alcun URI intent di link all'app valido, il sistema utilizza "Nessun link disponibile".
  • Per il colore principale (COLUMN_APP_LINK_COLOR), il sistema utilizza il colore predefinito dell'app.
  • Per l'immagine poster (COLUMN_APP_LINK_POSTER_ART_URI), il sistema utilizza il banner della schermata Home dell'app. Se l'app non fornisce un banner, il sistema utilizza un'immagine predefinita dell'app TV.
  • Per l'icona del badge (COLUMN_APP_LINK_ICON_URI), il sistema utilizza un badge che mostra il nome dell'app. Se il sistema utilizza anche il banner dell'app o l'immagine predefinita dell'app per l'immagine poster, non viene visualizzato alcun badge dell'app.

Specifica i dettagli dei link alle app per i tuoi canali nell'attività di configurazione dell'app. Puoi aggiornare questi dettagli dei link alle app in qualsiasi momento, quindi se un link all'app deve corrispondere alle modifiche del canale, aggiorna i dettagli del link all'app e chiama ContentResolver.update in base alle esigenze. Per ulteriori dettagli sull'aggiornamento dei dati dei canali, vedi Aggiorna i dati dei canali.