L'ingresso della TV deve fornire dati della Guida elettronica ai programmi (EPG) per almeno un canale nella sua attività di configurazione. Inoltre, dovresti aggiornare periodicamente i dati, tenendo conto delle dimensioni dell'aggiornamento e del thread di elaborazione che li gestisce. Inoltre, puoi fornire link alle app per i canali che indirizzano l'utente verso attività e contenuti correlati. In questa lezione vengono illustrati la creazione e l'aggiornamento dei dati di canali e programmi nel database di sistema tenendo presenti queste considerazioni.
Prova l'app di esempio TV Input Service.
Autorizza
Affinché l'input TV funzioni con i dati EPG, deve dichiarare l'autorizzazione di scrittura nel file manifest 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 ingressi TV. Nell'attività di configurazione, per ciascuno dei tuoi canali, devi mappare i dati del canale ai seguenti campi della classe TvContract.Channels
:
COLUMN_DISPLAY_NAME
: il nome visualizzato del canaleCOLUMN_DISPLAY_NUMBER
: il numero del canale visualizzatoCOLUMN_INPUT_ID
: l'ID del servizio di input TV.COLUMN_SERVICE_TYPE
: il tipo di servizio del canale.COLUMN_TYPE
: il tipo di trasmissione standard del canaleCOLUMN_VIDEO_FORMAT
: il formato video predefinito per il canale
Sebbene il framework di input per la TV sia sufficientemente generico da gestire sia i contenuti trasmessi tradizionali sia i contenuti over-the-top (OTT) senza alcuna distinzione, ti consigliamo di definire le seguenti colonne in aggiunta a quelle riportate sopra per identificare meglio i canali di trasmissione tradizionali:
COLUMN_ORIGINAL_NETWORK_ID
: l'ID della rete televisivaCOLUMN_SERVICE_ID
: l'ID servizioCOLUMN_TRANSPORT_STREAM_ID
: l'ID dello stream di trasporto
Se vuoi fornire dettagli sul link dell'app per i tuoi canali, devi aggiornare alcuni campi aggiuntivi. Per ulteriori informazioni sui campi relativi ai link dell'app, consulta la pagina Aggiungere informazioni sul link dell'app.
Per gli ingressi TV basati sullo streaming Internet, assegna i tuoi valori a quelli indicati sopra 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 nel seguente modo:
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);
Nell'esempio precedente, channel
è un oggetto che contiene i metadati del canale dal server di backend.
Presentare informazioni sul canale e sul programma
L'app di sistema per la TV presenta agli utenti informazioni sui programmi e sui canali mentre passano da uno all'altro, come mostrato nella Figura 1. Per assicurarti che le informazioni sul canale e sul programma funzionino con il presentatore di informazioni sui canali e sui programmi dell'app di sistema per TV, segui le linee guida riportate di seguito.
- Numero di canale (
COLUMN_DISPLAY_NUMBER
) - Icona
(
android:icon
nel file manifest dell'input TV) - Descrizione del programma (
COLUMN_SHORT_DESCRIPTION
) - Titolo del programma (
COLUMN_TITLE
) - Logo del canale (
TvContract.Channels.Logo
)- Utilizza il colore #EEEEEE per abbinarlo al testo circostante
- Non includere spaziatura interna
- Poster art (
COLUMN_POSTER_ART_URI
)- Proporzioni tra 16:9 e 4:3

Figura 1. Il canale e il presentatore di informazioni sui programmi dell'app di sistema per TV.
L'app di sistema per la TV 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 per TV di sistema.
Aggiorna i dati del canale
Quando aggiorni i dati dei canali esistenti, utilizza il metodo
update()
invece di eliminare e aggiungere nuovamente 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 dei dati del canale a ContentProvider
può richiedere del tempo. Aggiungi i programmi attuali (entro due ore dall'ora corrente) solo quando configuri il tuo EpgSyncJobService
per aggiornare gli altri dati del canale in background. Per un esempio, vedi l'
app di esempio Android TV Live TV.
Caricamento in gruppo dei dati del canale
Quando aggiorni il database di sistema con una grande quantità di dati di canale, usa il metodo ContentResolver
applyBatch()
o
bulkInsert()
. Ecco un esempio utilizzando 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 del canale in modo asincrono
La manipolazione dei dati, come il recupero di un flusso dal server o l'accesso al database, non deve bloccare il thread dell'interfaccia utente. L'utilizzo di un AsyncTask
è un
modo per eseguire 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, potresti utilizzare WorkManager
per eseguire il processo di aggiornamento durante un periodo 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'uso della classe HandlerThread
oppure puoi implementarne di personalizzate utilizzando le classi Looper
e Handler
. Per ulteriori informazioni, consulta
Processi e thread.
Aggiungi informazioni sul link dell'app
I canali possono utilizzare i link alle app per consentire agli utenti di avviare facilmente un'attività correlata mentre guardano i contenuti del canale. Le app del canale utilizzano i link alle app per estendere il coinvolgimento degli utenti lanciando attività che mostrano informazioni correlate o contenuti aggiuntivi. Ad esempio, puoi usare i link dell'app per:
- Aiuta l'utente a scoprire e acquistare contenuti correlati.
- Fornisci ulteriori informazioni sui contenuti attualmente in riproduzione.
- Mentre guardi i contenuti a puntate, inizia a guardare l'episodio successivo di una serie.
- Consenti all'utente di interagire con i contenuti, ad esempio valutare o recensire i contenuti, senza interrompere la riproduzione dei contenuti.
I link delle 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 al momento della visualizzazione dei contenuti del canale.
Quando l'utente seleziona il link all'app, il sistema avvia un'attività utilizzando un URI di intent specificato dall'app del canale. La riproduzione dei contenuti del canale continua mentre l'attività del link all'app è attiva. L'utente può tornare ai contenuti del canale premendo Indietro.
Fornisci dati sul canale del link dell'app
Android TV crea automaticamente un link all'app per ogni canale,
utilizzando le informazioni ricavate dai dati del canale. Per fornire informazioni sul link dell'app, specifica i seguenti dettagli nei campi TvContract.Channels
:
COLUMN_APP_LINK_COLOR
: il colore dell'accento del link dell'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 dell'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 di intent del link all'app per questo canale. Puoi creare l'URI utilizzandotoUri(int)
conURI_INTENT_SCHEME
e convertire l'URI nell'intent originale conparseUri()
.COLUMN_APP_LINK_POSTER_ART_URI
- L'URI della locandina utilizzata come sfondo del link dell'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 dell'app per questo canale. Per una descrizione di esempio del link dell'app, vedi il testo nella figura 2, callout 3.

Figura 2. Dettagli del link dell'app.
Se i dati del canale non specificano informazioni sul link dell'app, il sistema crea un link dell'app predefinito. Il sistema sceglie i dettagli predefiniti nel seguente modo:
- Per l'URI di intent (
COLUMN_APP_LINK_INTENT_URI
), il sistema utilizza l'attivitàACTION_MAIN
per la categoriaCATEGORY_LEANBACK_LAUNCHER
, in genere definita nel file manifest dell'app. Se questa attività non è definita, viene visualizzato un link all'app non funzionante; se l'utente fa clic su di esso, non accade nulla. - Per il testo descrittivo (
COLUMN_APP_LINK_TEXT
), il sistema utilizza "Apri app-name". Se non viene definito un URI valido per l'intent del link dell'app, il sistema utilizza "Nessun link disponibile". - Per il colore di contrasto (
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 per la 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 o l'immagine predefinita dell'app per l'immagine poster, non viene mostrato alcun badge dell'app.
Puoi specificare i dettagli del link dell'app per i tuoi canali nell'attività di configurazione dell'app. Puoi aggiornare questi dettagli del link dell'app in qualsiasi momento; pertanto, se un link dell'app deve corrispondere alle modifiche del canale, aggiorna i dettagli del link dell'app e, se necessario, chiama ContentResolver.update()
. Per maggiori dettagli sull'aggiornamento dei dati del canale, consulta Aggiornare i dati dei canali.