Développer un service d'entrée TV

Un service d'entrée TV représente une source de flux multimédia et vous permet de présenter votre contenu multimédia de manière linéaire, comme une chaîne de télévision et des programmes. Avec un service d'entrée TV, vous pouvez fournir des contrôles parentaux, des informations sur le guide des programmes et des classifications de contenu. Le service d'entrée TV fonctionne avec l'application TV du système Android. Cette application contrôle et présente le contenu des chaînes sur le téléviseur. L'application TV du système est développée spécifiquement pour l'appareil et ne peut pas être modifiée par des applications tierces. Pour en savoir plus sur l'architecture du framework d'entrée TV (TIF) et ses composants, consultez Framework d'entrée TV.

Créer un service d'entrée TV à l'aide de la bibliothèque complémentaire TIF

La bibliothèque complémentaire TIF est un framework qui fournit des implémentations extensibles de fonctionnalités courantes du service d'entrée TV. Elle est destinée à être utilisée par les OEM pour créer des chaînes pour Android 5.0 (niveau d'API 21) à Android 7.1 (niveau d'API 25) uniquement.

Mettre à jour votre projet

La bibliothèque complémentaire TIF est disponible pour une utilisation héritée par les OEM dans le androidtv-sample-inputs dépôt. Consultez ce dépôt pour obtenir un exemple d'inclusion de la bibliothèque dans une application.

Déclarer votre service d'entrée TV dans le fichier manifeste

Votre application doit fournir un service compatible avec TvInputService que le système utilise pour accéder à votre application. La bibliothèque complémentaire TIF fournit la classe BaseTvInputService, qui fournit une implémentation par défaut de TvInputService que vous pouvez personnaliser. Créez une sous-classe de BaseTvInputService et déclarez-la dans votre fichier manifeste en tant que service.

Dans la déclaration du fichier manifeste, spécifiez l'autorisation BIND_TV_INPUT pour permettre au service de connecter l'entrée TV au système. Un service système effectue la liaison et dispose de l'autorisation BIND_TV_INPUT. L'application TV du système envoie des requêtes aux services d'entrée TV via l'interface TvInputManager.

Dans la déclaration de votre service, incluez un filtre d'intent qui spécifie TvInputService comme action à effectuer avec l'intent. Déclarez également les métadonnées du service en tant que ressource XML distincte. La déclaration du service, le filtre d'intent et la déclaration des métadonnées du service sont présentés dans l'exemple suivant :

<service android:name=".rich.RichTvInputService"
    android:label="@string/rich_input_label"
    android:permission="android.permission.BIND_TV_INPUT">
    <!-- Required filter used by the system to launch our account service. -->
    <intent-filter>
        <action android:name="android.media.tv.TvInputService" />
    </intent-filter>
    <!-- An XML file which describes this input. This provides pointers to
    the RichTvInputSetupActivity to the system/TV app. -->
    <meta-data
        android:name="android.media.tv.input"
        android:resource="@xml/richtvinputservice" />
</service>

Définissez les métadonnées du service dans un fichier XML distinct. Le fichier XML des métadonnées du service doit inclure une interface de configuration qui décrit la configuration initiale de l'entrée TV et l'analyse des chaînes. Le fichier de métadonnées doit également contenir un indicateur indiquant si les utilisateurs peuvent ou non enregistrer du contenu. Pour en savoir plus sur la prise en charge de l'enregistrement de contenu dans votre application, consultez Prise en charge de l'enregistrement de contenu.

Le fichier de métadonnées du service se trouve dans le répertoire des ressources XML de votre application et doit correspondre au nom de la ressource que vous avez déclarée dans le fichier manifeste. En utilisant les entrées du fichier manifeste de l'exemple précédent, vous créeriez le fichier XML dans res/xml/richtvinputservice.xml, avec le contenu suivant :

<?xml version="1.0" encoding="utf-8"?>
<tv-input xmlns:android="http://schemas.android.com/apk/res/android"
  android:canRecord="true"
  android:setupActivity="com.example.android.sampletvinput.rich.RichTvInputSetupActivity" />

Définir des chaînes et créer votre activité de configuration

Votre service d'entrée TV doit définir au moins une chaîne à laquelle les utilisateurs accèdent via l'application TV du système. Vous devez enregistrer vos chaînes dans la base de données du système et fournir une activité de configuration que le système appelle lorsqu'il ne trouve pas de chaîne pour votre application.

Tout d'abord, autorisez votre application à lire et à écrire dans le guide électronique des programmes (EPG) du système, dont les données incluent les chaînes et les programmes disponibles pour l'utilisateur. Pour permettre à votre application d'effectuer ces actions et de se synchroniser avec l'EPG après le redémarrage de l'appareil, ajoutez les éléments suivants au fichier manifeste de votre application :

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

Ajoutez l'élément suivant pour vous assurer que votre application s'affiche dans le Google Play Store en tant qu'application fournissant des chaînes de contenu dans Android TV :

<uses-feature
    android:name="android.software.live_tv"
    android:required="true" />

Créez ensuite une classe qui étend la classe EpgSyncJobService. Cette classe abstraite vous permet de créer un service de tâches qui crée et met à jour des chaînes dans la base de données du système.

Dans votre sous-classe, créez et renvoyez la liste complète de vos chaînes dans getChannels. Si vos chaînes proviennent d'un fichier XMLTV, utilisez la classe XmlTvParser. Sinon, générez des chaînes par programmation à l'aide de la classe Channel.Builder.

Pour chaque chaîne, le système appelle getProgramsForChannel lorsqu'il a besoin d'une liste de programmes pouvant être visionnés dans une fenêtre temporelle donnée sur la chaîne. Renvoyez une liste d'objets Program pour la chaîne. Utilisez la classe XmlTvParser pour obtenir des programmes à partir d'un fichier XMLTV ou générez-les par programmation à l'aide de la classe Program.Builder.

Pour chaque objet Program, utilisez un objet InternalProviderData pour définir des informations sur le programme, telles que son type de vidéo. Si vous ne disposez que d'un nombre limité de programmes que vous souhaitez que la chaîne répète en boucle, utilisez la méthode InternalProviderData.setRepeatable avec la valeur true lorsque vous définissez des informations sur votre programme.

Une fois le service de tâches implémenté, ajoutez-le au fichier manifeste d'application :

<service
    android:name=".sync.SampleJobService"
    android:permission="android.permission.BIND_JOB_SERVICE"
    android:exported="true" />

Enfin, créez une activité de configuration. Votre activité de configuration doit permettre de synchroniser les données des chaînes et des programmes. Pour ce faire, l'utilisateur peut utiliser l'interface utilisateur de l'activité. Vous pouvez également faire en sorte que l'application le fasse automatiquement au démarrage de l'activité. Lorsque l'activité de configuration doit synchroniser les informations sur les chaînes et les programmes, l'application doit démarrer le service de tâches :

Kotlin

val inputId = getActivity().intent.getStringExtra(TvInputInfo.EXTRA_INPUT_ID)
EpgSyncJobService.cancelAllSyncRequests(getActivity())
EpgSyncJobService.requestImmediateSync(
        getActivity(),
        inputId,
        ComponentName(getActivity(), SampleJobService::class.java)
)

Java

String inputId = getActivity().getIntent().getStringExtra(TvInputInfo.EXTRA_INPUT_ID);
EpgSyncJobService.cancelAllSyncRequests(getActivity());
EpgSyncJobService.requestImmediateSync(getActivity(), inputId,
        new ComponentName(getActivity(), SampleJobService.class));

Utilisez la méthode requestImmediateSync pour synchroniser le service de tâches. L'utilisateur doit attendre la fin de la synchronisation. Vous devez donc maintenir une période de requête relativement courte.

Utilisez la méthode setUpPeriodicSync pour que le service de tâches synchronise régulièrement les données des chaînes et des programmes en arrière-plan :

Kotlin

EpgSyncJobService.setUpPeriodicSync(
        context,
        inputId,
        ComponentName(context, SampleJobService::class.java)
)

Java

EpgSyncJobService.setUpPeriodicSync(context, inputId,
        new ComponentName(context, SampleJobService.class));

La bibliothèque complémentaire TIF fournit une méthode surchargée supplémentaire de requestImmediateSync qui vous permet de spécifier la durée des données de chaîne à synchroniser en millisecondes. La méthode par défaut synchronise une heure de données de chaîne.

La bibliothèque complémentaire TIF fournit également une méthode surchargée supplémentaire de setUpPeriodicSync qui vous permet de spécifier la durée des données de chaîne à synchroniser et la fréquence de la synchronisation périodique. La méthode par défaut synchronise 48 heures de données de chaîne toutes les 12 heures.

Pour en savoir plus sur les données de chaîne et l'EPG, consultez Utiliser des données de chaîne.

Gérer les requêtes de réglage et la lecture de contenus multimédias

Lorsqu'un utilisateur sélectionne une chaîne spécifique, l'application TV du système utilise une Session, créée par votre application, pour se régler sur la chaîne demandée et lire le contenu. La bibliothèque complémentaire TIF fournit plusieurs classes que vous pouvez étendre pour gérer les appels de chaînes et de sessions à partir du système.

Votre sous-classe BaseTvInputService crée des sessions qui gèrent les requêtes de réglage. Remplacez la méthode onCreateSession, créez une session étendue à partir de la classe BaseTvInputService.Session et appelez super.sessionCreated avec votre nouvelle session. Dans l'exemple suivant, onCreateSession renvoie un objet RichTvInputSessionImpl qui étend BaseTvInputService.Session :

Kotlin

override fun onCreateSession(inputId: String): Session =
        RichTvInputSessionImpl(this, inputId).apply {
            setOverlayViewEnabled(true)
        }

Java

@Override
public final Session onCreateSession(String inputId) {
    RichTvInputSessionImpl session = new RichTvInputSessionImpl(this, inputId);
    session.setOverlayViewEnabled(true);
    return session;
}

Lorsque l'utilisateur utilise l'application TV du système pour commencer à regarder l'une de vos chaînes, le système appelle la méthode onPlayChannel de votre session. Remplacez cette méthode si vous devez effectuer une initialisation spéciale de la chaîne avant le début de la lecture du programme.

Le système obtient ensuite le programme actuellement planifié et appelle la méthode onPlayProgram de votre session, en spécifiant les informations sur le programme et l'heure de début en millisecondes. Utilisez l'interface TvPlayer pour lancer la lecture du programme.

Le code de votre lecteur multimédia doit implémenter TvPlayer pour gérer des événements de lecture spécifiques. La classe TvPlayer gère des fonctionnalités telles que les commandes de décalage temporel sans ajouter de complexité à votre implémentation BaseTvInputService.

Dans la méthode getTvPlayer de votre session, renvoyez votre lecteur multimédia qui implémente TvPlayer. L'exemple d'application de service d'entrée TV implémente un lecteur multimédia qui utilise ExoPlayer.

Créer un service d'entrée TV à l'aide du framework d'entrée TV

Si votre service d'entrée TV ne peut pas utiliser la bibliothèque complémentaire TIF, vous devez implémenter les composants suivants :

  • TvInputService fournit une disponibilité à long terme et en arrière-plan pour l'entrée TV
  • TvInputService.Session gère l'état de l'entrée TV et communique avec l'application hôte
  • TvContract décrit les chaînes et les programmes disponibles pour l'entrée TV
  • TvContract.Channels représente des informations sur une chaîne TV
  • TvContract.Programs décrit un programme TV avec des données telles que le titre du programme et l'heure de début
  • TvTrackInfo représente une piste audio, vidéo ou de sous-titres
  • TvContentRating décrit une classification du contenu et permet des schémas de classification du contenu personnalisés
  • TvInputManager fournit une API à l'application TV du système et gère l'interaction avec les entrées TV et les applications

Vous devez également effectuer les opérations suivantes :

  1. Déclarez votre service d'entrée TV dans le fichier manifeste, comme décrit dans Déclarer votre service d'entrée TV dans le fichier manifeste.
  2. Créez le fichier de métadonnées du service.
  3. Créez et enregistrez les informations sur votre chaîne et votre programme.
  4. Créez votre activité de configuration.

Définir votre service d'entrée TV

Pour votre service, vous étendez la classe TvInputService. Une TvInputService implémentation est un service lié où le service système est le client qui s'y lie. Les méthodes de cycle de vie du service que vous devez implémenter sont illustrées dans la figure 1.

La méthode onCreate initialise et démarre le HandlerThread, qui fournit un thread de processus distinct du thread UI pour gérer les actions pilotées par le système. Dans l'exemple suivant, la méthode onCreate initialise le CaptioningManager et se prépare à gérer les actions ACTION_BLOCKED_RATINGS_CHANGED et ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED. Ces actions décrivent les intents système déclenchés lorsque l'utilisateur modifie les paramètres de contrôle parental et lorsque la liste des classifications bloquées est modifiée.

Kotlin

override fun onCreate() {
    super.onCreate()
    handlerThread = HandlerThread(javaClass.simpleName).apply {
        start()
    }
    dbHandler = Handler(handlerThread.looper)
    handler = Handler()
    captioningManager = getSystemService(Context.CAPTIONING_SERVICE) as CaptioningManager

    setTheme(android.R.style.Theme_Holo_Light_NoActionBar)

    sessions = mutableListOf<BaseTvInputSessionImpl>()
    val intentFilter = IntentFilter().apply {
        addAction(TvInputManager.ACTION_BLOCKED_RATINGS_CHANGED)
        addAction(TvInputManager.ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED)
    }
    registerReceiver(broadcastReceiver, intentFilter)
}

Java

@Override
public void onCreate() {
    super.onCreate();
    handlerThread = new HandlerThread(getClass()
      .getSimpleName());
    handlerThread.start();
    dbHandler = new Handler(handlerThread.getLooper());
    handler = new Handler();
    captioningManager = (CaptioningManager)
      getSystemService(Context.CAPTIONING_SERVICE);

    setTheme(android.R.style.Theme_Holo_Light_NoActionBar);

    sessions = new ArrayList<BaseTvInputSessionImpl>();
    IntentFilter intentFilter = new IntentFilter();
    intentFilter.addAction(TvInputManager
      .ACTION_BLOCKED_RATINGS_CHANGED);
    intentFilter.addAction(TvInputManager
      .ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED);
    registerReceiver(broadcastReceiver, intentFilter);
}

Figure 1 : Cycle de vie de TvInputService

Pour en savoir plus sur l'utilisation de contenu bloqué et la configuration du contrôle parental, consultez Contrôler le contenu. Consultez TvInputManager pour découvrir d'autres actions pilotées par le système que vous pouvez gérer dans votre service d'entrée TV.

Le TvInputService crée un TvInputService.Session qui implémente Handler.Callback pour gérer les modifications de l'état du lecteur. Avec onSetSurface, le TvInputService.Session définit le Surface avec le contenu vidéo. Pour en savoir plus sur l'utilisation de Surface pour afficher des vidéos, consultez Intégrer un lecteur à une surface.

Le TvInputService.Session gère l'événement onTune lorsque l'utilisateur sélectionne une chaîne et informe l'application TV du système des modifications apportées au contenu et aux métadonnées du contenu. Ces méthodes notify sont décrites dans Contrôler le contenu et Gérer la sélection des pistes plus loin dans cette formation.

Définir votre activité de configuration

L'application TV du système fonctionne avec l'activité de configuration que vous définissez pour votre entrée TV. L'activité de configuration est obligatoire et doit fournir au moins un enregistrement de chaîne pour la base de données du système. L'application TV du système appelle l'activité de configuration lorsqu'elle ne trouve pas de chaîne pour l'entrée TV.

L'activité de configuration décrit à l'application TV du système les chaînes mises à disposition via l'entrée TV, comme illustré dans la leçon suivante, Créer et mettre à jour des données de chaîne.

Références supplémentaires