Utiliser les données de chaîne

Votre entrée TV doit fournir des données du guide des programmes électroniques (EPG) pour au moins une chaîne dans son activité de configuration. Vous devez également mettre à jour régulièrement ces données, 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 vers des applications pour les chaînes qui guident l'utilisateur vers des contenus et des activités associés. Cette leçon traite de la création et de la mise à 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'application exemple TV Input Service.

Obtenir l'autorisation

Pour que votre entrée TV fonctionne avec les données EPG, l'autorisation d'écriture doit être déclarée dans le fichier manifeste Android comme suit:

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

Enregistrer des canaux 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, vous devez mapper les données de chaque canal aux champs suivants de la classe TvContract.Channels:

Bien que le framework d'entrée TV soit suffisamment générique pour gérer à la fois le contenu de diffusion traditionnel et le contenu par contournement (OTT, over-the-top) sans aucune distinction, vous pouvez définir les colonnes suivantes en plus de celles ci-dessus pour mieux identifier les chaînes de diffusion traditionnelles:

Si vous souhaitez fournir des informations sur les liens d'application pour vos chaînes, vous devez mettre à jour certains champs supplémentaires. Pour en savoir plus sur les champs des liens d'application, consultez Ajouter des informations sur le lien d'application.

Pour les entrées TV utilisant le streaming sur Internet, attribuez vos propres valeurs aux éléments ci-dessus en conséquence afin que chaque canal puisse être identifié de manière unique.

Extrayez les métadonnées de votre canal (au format XML, JSON, etc.) à partir de votre serveur backend, puis mappez les valeurs à la base de données système dans votre activité de configuration:

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 l'exemple ci-dessus, channel est un objet qui contient les métadonnées de canal du serveur backend.

Présenter des informations sur la chaîne et le programme

L'application System TV présente des informations sur la chaîne et le programme aux utilisateurs lorsqu'ils les parcourent, comme illustré dans la figure 1. Pour vous assurer que les informations sur la chaîne et le programme sont compatibles avec le présentateur de l'application TV du système, suivez les consignes ci-dessous.

  1. Numéro de chaîne (COLUMN_DISPLAY_NUMBER)
  2. Icône (android:icon dans le fichier manifeste de l'entrée TV)
  3. Description du programme (COLUMN_SHORT_DESCRIPTION)
  4. Titre du programme (COLUMN_TITLE)
  5. Logo de la chaîne (TvContract.Channels.Logo)
    • Utilisez la couleur #EEEEEE pour correspondre au texte à proximité.
    • Ne pas inclure de marge intérieure
  6. Poster art (COLUMN_POSTER_ART_URI)
    • Format entre 16:9 et 4:3

Figure 1 : Présentateur des informations sur la chaîne et le programme de l'application TV système.

L'application TV du système fournit les mêmes informations via le guide des programmes, y compris les affiches, comme illustré dans la figure 2.

Figure 2. Guide des programmes de l'application System TV.

Mettre à jour les données de la chaîne

Lorsque vous mettez à jour des données de canal existantes, utilisez la méthode update() au lieu de supprimer et d'ajouter de nouveau 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 canal à ContentProvider peut prendre du temps. Ajoutez les programmes en cours (ceux qui ont lieu au cours des deux heures qui suivent) uniquement lorsque vous configurez votre EpgSyncJobService pour mettre à jour le reste des données de la chaîne en arrière-plan. Consultez l' application exemple Android TV Live TV pour obtenir un exemple.

Chargement par lot des données de canal

Lorsque vous mettez à jour la base de données système avec une grande quantité de données de canaux, utilisez la méthode ContentResolver applyBatch() ou bulkInsert(). Voici un exemple utilisant 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 des canaux de manière asynchrone

La manipulation des données, telle que l'extraction d'un flux 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 le canal à 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 les données EPG régulièrement, envisagez d'utiliser WorkManager pour exécuter le processus de mise à jour pendant les périodes d'inactivité, par exemple tous les jours à 3h du matin.

D'autres techniques permettent de séparer les tâches de mise à jour des données du thread UI à l'aide de la classe HandlerThread. Vous pouvez également implémenter la vôtre à l'aide des classes Looper et Handler. Pour en savoir plus, consultez la section Processus et threads.

Les chaînes peuvent utiliser des liens d'application pour permettre aux utilisateurs de lancer facilement une activité associée pendant qu'ils regardent du contenu de la chaîne. Les applications de chaîne utilisent des liens pour renforcer l'engagement utilisateur 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:

  • Guidez l'utilisateur pour découvrir et acheter des contenus associés.
  • Fournissez des informations supplémentaires sur le contenu en cours de lecture.
  • Lorsque vous regardez du contenu sous forme d'épisodes, commencez à regarder le prochain épisode d'une série.
  • Permettez à l'utilisateur d'interagir avec le contenu (par exemple, en l'évaluant ou en la notant) sans interrompre la lecture.

Les liens d'application s'affichent lorsque l'utilisateur appuie sur Sélectionner pour afficher le menu du téléviseur pendant qu'il regarde le contenu d'une chaîne.

Figure 1 : Exemple de lien d'application affiché sur la ligne Channels (Chaînes) lorsque le contenu d'une chaîne est affiché.

Lorsque l'utilisateur sélectionne le lien vers l'application, le système démarre une activité à l'aide d'un URI d'intent spécifié par l'application de la chaîne. La lecture du contenu du canal continue pendant que l'activité de lien d'application est active. L'utilisateur peut revenir au contenu de la chaîne en appuyant sur Retour.

Fournir des données sur le canal du lien vers l'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'applications, spécifiez les informations suivantes dans les champs TvContract.Channels:

  • COLUMN_APP_LINK_COLOR : couleur d'accentuation du lien de l'application pour cette chaîne. Pour un exemple de couleur d'accentuation, reportez-vous à la figure 2, légende 3.
  • COLUMN_APP_LINK_ICON_URI : URI de l'icône du badge d'application du lien vers l'application pour cette chaîne. Pour voir un exemple d'icône de badge d'application, reportez-vous à la figure 2, légende 2.
  • COLUMN_APP_LINK_INTENT_URI : URI d'intent du lien vers l'application pour ce canal. Vous pouvez créer l'URI à l'aide de toUri(int) avec URI_INTENT_SCHEME et reconvertir l'URI en intent d'origine avec parseUri().
  • COLUMN_APP_LINK_POSTER_ART_URI : URI de l'affiche utilisée en tant qu'arrière-plan du lien de l'application pour cette chaîne. Pour voir un exemple d'image poster, reportez-vous à la figure 2, accroche 1.
  • COLUMN_APP_LINK_TEXT : texte descriptif du lien vers l'application pour cette chaîne. Pour obtenir un exemple de description de lien d'application, consultez le texte de la figure 2, accroche 3.

Figure 2. Détails du lien vers l'application.

Si les données de la chaîne ne spécifient pas d'informations sur le lien vers l'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_MAIN pour la catégorie CATEGORY_LEANBACK_LAUNCHER, généralement définie dans le fichier manifeste de l'application. Si cette activité n'est pas définie, un lien d'application non fonctionnel apparaît. 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 indique le nom de l'application. Si le système utilise également la bannière ou l'image par défaut de l'application pour l'image poster, aucun badge d'application ne s'affiche.

Vous spécifiez les détails des liens vers une application pour vos chaînes dans l'activité de configuration de votre application. Vous pouvez modifier les détails de ces liens d'application à tout moment. Par conséquent, si une association d'application doit correspondre aux modifications de la version, mettez à jour les détails du lien vers l'application et appelez ContentResolver.update() si nécessaire. Pour en savoir plus sur la mise à jour des données de canal, consultez Mettre à jour les données de canal.