Votre entrée TV doit fournir des données de guide électronique des programmes (EPG) pour au moins une chaîne dans son activité de configuration. Vous devez également mettre à jour ces données régulièrement, en tenant compte de la taille de la mise à jour et du thread de traitement qui la gère. De plus, vous pouvez fournir des liens d'application pour les chaînes qui guident l'utilisateur vers des contenus et des activités associés. Cette leçon explique comment créer et mettre à jour des données de chaîne et de programme dans la base de données système en tenant compte de ces considérations.
Essayez l'exemple d'application TV Input Service.
Obtenir l'autorisation
Pour que votre entrée TV fonctionne avec les données EPG, elle doit déclarer l' autorisation d'écriture dans son fichier manifeste Android comme suit :
<uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />
Enregistrer des chaînes dans la base de données
La base de données système Android TV conserve les enregistrements des données de chaîne pour les entrées TV. Dans votre activité de configuration, pour chacune de vos chaînes, vous devez mapper vos données de chaîne avec les champs suivants de la classe TvContract.Channels :
COLUMN_DISPLAY_NAME: nom affiché de la chaîneCOLUMN_DISPLAY_NUMBER: numéro de chaîne affichéCOLUMN_INPUT_ID: ID du service d'entrée TVCOLUMN_SERVICE_TYPE: type de service de la chaîneCOLUMN_TYPE: type de norme de diffusion de la chaîneCOLUMN_VIDEO_FORMAT: format vidéo par défaut de la chaîne
Bien que le framework d'entrée TV soit suffisamment générique pour gérer les contenus de diffusion traditionnels et les contenus OTT (Over-The-Top) sans distinction, vous pouvez définir les colonnes suivantes en plus pour mieux identifier les chaînes de diffusion traditionnelles :
COLUMN_ORIGINAL_NETWORK_ID: ID du réseau de télévisionCOLUMN_SERVICE_ID: ID du serviceCOLUMN_TRANSPORT_STREAM_ID: ID du flux de transport
Si vous souhaitez fournir des informations sur les liens d'application pour vos chaînes, vous devez mettre à jour des champs supplémentaires. Pour en savoir plus sur les champs de lien d'application, consultez Ajouter des informations sur les liens d'application.
Pour les entrées TV basées sur la diffusion en streaming sur Internet, attribuez vos propres valeurs en conséquence afin que chaque chaîne puisse être identifiée de manière unique.
Extrayez les métadonnées de votre chaîne (au format XML, JSON ou autre) à partir de votre serveur backend, puis, dans votre activité de configuration, mappez les valeurs à la base de données système comme suit :
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);
Dans cet exemple, channel est un objet qui contient les métadonnées de la chaîne provenant du serveur backend.
Présenter des informations sur la chaîne et le programme
L'application TV du système présente des informations sur la chaîne et le programme aux utilisateurs lorsqu'ils zappent, comme illustré dans la figure 1. Pour vous assurer que les informations sur la chaîne et le programme fonctionnent avec le présentateur d'informations sur la chaîne et le programme de l'application TV du système, suivez ces consignes :
- Numéro de chaîne (
COLUMN_DISPLAY_NUMBER) - Icône
(
android:icondans le fichier manifeste de l'entrée TV) - Description du programme (
COLUMN_SHORT_DESCRIPTION) - Titre du programme (
COLUMN_TITLE) - Logo de la chaîne (
TvContract.Channels.Logo)- Utilisez la couleur #EEEEEE pour correspondre au texte environnant.
- N'incluez pas de marge intérieure.
- Affiche (
COLUMN_POSTER_ART_URI) - Format entre 16:9 et 4:3
L'application TV du système fournit les mêmes informations via le guide des programmes, y compris l'affiche, comme illustré dans la figure 2.
Mettre à jour les données de la chaîne
Lorsque vous mettez à jour des données de chaîne existantes, utilisez la méthode update au lieu de supprimer et de rajouter les données. Vous pouvez identifier la version actuelle des données à l'aide de Channels.COLUMN_VERSION_NUMBER et Programs.COLUMN_VERSION_NUMBER lorsque vous choisissez les enregistrements à mettre à jour.
Remarque : L'ajout de données de chaîne au ContentProvider peut prendre du temps. N'ajoutez les programmes en cours (ceux qui sont diffusés dans les deux heures suivant l'heure actuelle) que lorsque vous configurez votre EpgSyncJobService pour mettre à jour le reste des données de la chaîne en arrière-plan. Pour obtenir un exemple, consultez
l'
exemple d'application Android TV Live TV.
Charger des données de chaîne par lot
Lorsque vous mettez à jour la base de données système avec une grande quantité de données de chaîne, utilisez la méthode ContentResolver
applyBatch ou bulkInsert. Voici un exemple d'utilisation de 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(); } }
Traiter les données de chaîne de manière asynchrone
La manipulation des données, comme l'extraction d'un flux à partir du serveur ou l'accès à la base de données, ne doit pas bloquer le thread UI. L'utilisation d'un AsyncTask est un moyen d'effectuer des mises à jour de manière asynchrone. Par exemple, lorsque vous chargez des informations sur la chaîne à partir d'un serveur backend, vous pouvez utiliser AsyncTask comme suit :
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(); } } } }
Si vous devez mettre à jour régulièrement les données EPG, envisagez d'utiliser
WorkManager
pour exécuter le processus de mise à jour pendant les périodes d'inactivité, par exemple tous les jours à 3h00.
D'autres techniques permettent de séparer les tâches de mise à jour des données du thread UI, par exemple en utilisant la
HandlerThread classe. Vous pouvez également implémenter la vôtre à l'aide des Looper
et Handler classes. Pour en savoir plus, consultez
Processus et threads.
Ajouter des informations sur les liens d'application
Les chaînes peuvent utiliser des liens d'application pour permettre aux utilisateurs de lancer une activité associée pendant qu'ils regardent le contenu de la chaîne. Les applications de chaîne utilisent des liens d'application pour étendre l'engagement des utilisateurs en lançant des activités qui affichent des informations associées ou du contenu supplémentaire. Par exemple, vous pouvez utiliser des liens d'application pour effectuer les opérations suivantes :
- Guider l'utilisateur pour qu'il découvre et achète du contenu associé.
- Fournir des informations supplémentaires sur le contenu en cours de lecture.
- Lorsque vous regardez un contenu épisodique, commencez à regarder l'épisode suivant d'une série.
- Permettre à l'utilisateur d'interagir avec le contenu (par exemple, le noter ou le commenter) sans interrompre la lecture.
Les liens d'application s'affichent lorsque l'utilisateur appuie sur Sélectionner pour afficher le menu TV pendant qu'il regarde le contenu de la chaîne.
Figure 1 : Exemple de lien d'application affiché sur la ligne Chaînes pendant la diffusion du contenu de la chaîne.
Lorsque l'utilisateur sélectionne le lien d'application, le système démarre une activité à l'aide d'un URI d'intent spécifié par l'application de la chaîne. Le contenu de la chaîne continue d'être diffusé pendant que l'activité du lien d'application est active. L'utilisateur peut revenir au contenu de la chaîne en appuyant sur Retour.
Fournir des données de chaîne de lien d'application
Android TV crée automatiquement un lien d'application pour chaque chaîne à l'aide des informations provenant des données de la chaîne. Pour fournir des informations sur les liens d'application, spécifiez les détails suivants dans les champs TvContract.Channels :
COLUMN_APP_LINK_COLOR: couleur d'accentuation du lien d'application pour cette chaîne. Pour obtenir un exemple de couleur d'accentuation, consultez la figure 2, légende 3.COLUMN_APP_LINK_ICON_URI- URI de l'icône de badge d'application du lien d'application pour cette chaîne. Pour obtenir un exemple d'icône de badge d'application, consultez la figure 2, légende 2.COLUMN_APP_LINK_INTENT_URI- URI d'intent du lien d'application pour cette chaîne. Vous pouvez créer l'URI à l'aide detoUri(int)avecURI_INTENT_SCHEMEet reconvertir l'URI en intent d'origine avecparseUri.COLUMN_APP_LINK_POSTER_ART_URI: URI de l'affiche utilisée comme arrière-plan du lien d'application pour cette chaîne. Pour obtenir un exemple d'affiche, consultez la figure 2, légende 1.COLUMN_APP_LINK_TEXT- texte descriptif du lien d'application pour cette chaîne. Pour obtenir un exemple de description de lien d'application, consultez le texte de la figure 2, légende 3.
Si les données de la chaîne ne spécifient pas d'informations sur les liens d'application, le système crée un lien d'application par défaut. Le système choisit les détails par défaut comme suit :
- Pour l'URI d'intent (
COLUMN_APP_LINK_INTENT_URI), le système utilise l'activitéACTION_MAINpour la catégorieCATEGORY_LEANBACK_LAUNCHER, généralement définie dans le fichier manifeste d'application. Si cette activité n'est pas définie, un lien d'application non fonctionnel s'affiche. Si l'utilisateur clique dessus, rien ne se passe. - Pour le texte descriptif
(
COLUMN_APP_LINK_TEXT), le système utilise "Ouvrir app-name". Si aucun URI d'intent de lien d'application viable n'est défini, le système utilise "Aucun lien disponible". - Pour la couleur d'accentuation (
COLUMN_APP_LINK_COLOR), le système utilise la couleur par défaut de l'application. - Pour l'image poster (
COLUMN_APP_LINK_POSTER_ART_URI), le système utilise la bannière de l'écran d'accueil de l'application. Si l'application ne fournit pas de bannière, le système utilise une image d'application TV par défaut. - Pour l'icône de badge (
COLUMN_APP_LINK_ICON_URI), le système utilise un badge qui affiche le nom de l'application. Si le système utilise également la bannière de l'application ou l'image d'application par défaut pour l'image poster, aucun badge d'application n'est affiché.
Vous spécifiez les détails des liens d'application pour vos chaînes dans l'activité de configuration de votre application. Vous pouvez mettre à jour ces détails à tout moment. Par conséquent, si un lien d'application doit correspondre aux modifications de la chaîne, mettez à jour les détails du lien d'application et appelez ContentResolver.update si nécessaire. Pour en savoir plus sur la mise à jour des données de la chaîne, consultez Mettre à jour les données de la chaîne.