Développer un service d'entrée TV

Un service d'entrée TV représente une source de flux multimédia. Il vous permet de présenter votre contenu multimédia à la télévision de manière linéaire sous forme de chaînes et de programmes. Avec un service d'entrée TV, vous pouvez fournir des options de contrôle parental, des informations sur les guides de programmes et des classifications de contenu. Le service d'entrée TV fonctionne avec l'application Android System TV. Au final, cette application contrôle et affiche le contenu de la chaîne sur le téléviseur. L'application TV du système est développée spécifiquement pour l'appareil et immuable par des applications tierces. Pour en savoir plus sur l'architecture TIF (TV Input Framework) et ses composants, consultez la page TV Input Framework.

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

La bibliothèque associée TIF est un framework qui fournit des implémentations extensibles des fonctionnalités courantes du service d'entrée TV. Il est destiné aux OEM afin de créer des canaux pour Android 5.0 (niveau d'API 21) à Android 7.1 (niveau d'API 25).

Mettre à jour votre projet

La bibliothèque associée TIF est disponible pour les anciens OEM dans le dépôt androidtv-sample-inputs. Consultez ce dépôt pour savoir comment inclure 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 y accéder. La bibliothèque associée 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 votre déclaration de 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 de service, le filtre d'intent et la déclaration de métadonnées du service sont illustré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 de métadonnées du service doit inclure une interface de configuration qui décrit la configuration initiale et la recherche de chaînes de l'entrée TV. 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 Prendre en charge l'enregistrement de contenu.

Le fichier de métadonnées du service se trouve dans le répertoire de ressources XML de votre application et doit correspondre au nom de la ressource que vous avez déclarée dans le fichier manifeste. À l'aide des entrées du fichier manifeste de l'exemple précédent, vous devez créer le fichier XML à l'emplacement 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 les canaux et créer votre activité de configuration

Votre service d'entrée TV doit définir au moins un canal auquel les utilisateurs accèdent via l'application TV du système. Vous devez enregistrer vos chaînes dans la base de données 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 de programmation électronique (EPG) du système, dont les données incluent les canaux 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 sur 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" />

Ensuite, créez une classe qui étend la classe EpgSyncJobService. Cette classe abstraite facilite la création d'un service de tâches qui crée et met à jour des canaux dans la base de données système.

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

Pour chaque canal, le système appelle getProgramsForChannel() lorsqu'il a besoin d'une liste de programmes pouvant être consultés sur le canal pendant une période donnée. Renvoyez une liste d'objets Program pour le canal. 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 les informations du programme, telles que son type de vidéo. Si vous souhaitez que la chaîne répète en boucle un nombre limité de programmes, utilisez la méthode InternalProviderData.setRepeatable() avec la valeur true lorsque vous définissez les informations sur votre programme.

Après avoir implémenté le service de tâches, ajoutez-le au fichier manifeste de votre 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 de la chaîne et du programme. Pour ce faire, l'utilisateur peut le faire via l'interface utilisateur de l'activité. Vous pouvez également demander à l'application de le faire automatiquement au début de l'activité. Lorsque l'activité de configuration doit synchroniser les informations sur le canal et le programme, 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 faire en sorte que la période de demande soit relativement courte.

Utilisez la méthode setUpPeriodicSync() pour que le service de tâches synchronise régulièrement les données de canal et de programme 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 associée TIF fournit une méthode surchargée supplémentaire de requestImmediateSync() qui vous permet de spécifier la durée (en millisecondes) des données de canal à synchroniser. La méthode par défaut synchronise l'équivalent d'une heure de données de chaîne.

La bibliothèque associée 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 canal à 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 canaux et l'EPG, consultez Utiliser des données de canaux.

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

Lorsqu'un utilisateur sélectionne une chaîne spécifique, l'application TV du système utilise un Session, créé par votre application, pour se connecter à la chaîne demandée et lire du contenu. La bibliothèque associée TIF fournit plusieurs classes que vous pouvez étendre pour gérer les appels de canal et de session à 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, puis 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 se sert de 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 initialiser un canal spécial 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 commencer à lire le 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 augmenter la complexité de votre implémentation de BaseTvInputService.

Dans la méthode getTvPlayer() de votre session, renvoyez votre lecteur multimédia qui implémente TvPlayer. L'application exemple TV Input Service 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 associée TIF, vous devez implémenter les composants suivants:

  • TvInputService fournit une disponibilité de longue durée en arrière-plan pour l'entrée TV.
  • TvInputService.Session conserve l'état d'entrée TV et communique avec l'application hôte.
  • TvContract décrit les chaînes et les programmes disponibles sur l'entrée TV.
  • TvContract.Channels représente des informations sur une chaîne de télévision.
  • TvContract.Programs décrit un programme télévisé avec des données telles que son titre et son heure de début
  • TvTrackInfo représente une piste audio, vidéo ou de sous-titres
  • TvContentRating décrit une classification de contenu et permet de personnaliser les systèmes de classification du contenu.
  • 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 procéder comme suit:

  1. Déclarez votre service d'entrée TV dans le fichier manifeste, comme décrit dans la section 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 relatives à 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 devez étendre la classe TvInputService. Une implémentation TvInputService est un service lié où le service système est le client qui lui est lié. 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 HandlerThread, qui fournit un thread de processus distinct du thread UI pour gérer les actions du système. Dans l'exemple suivant, la méthode onCreate() initialise 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 en cas de modification de la liste des classifications bloquées.

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 savoir comment utiliser du contenu bloqué et activer le contrôle parental, consultez Contrôler le contenu. Consultez TvInputManager pour découvrir d'autres actions liées au système que vous pouvez gérer dans votre service d'entrée TV.

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

TvInputService.Session gère l'événement onTune() lorsque l'utilisateur sélectionne une chaîne et avertit l'application TV du système des modifications apportées aux métadonnées de contenu et de contenu. Ces méthodes notify() sont décrites dans les sections Contrôle du contenu et Gérer la sélection de titres plus loin dans cet entraînement.

Définir votre activité de configuration

L'application TV du système utilise 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 canal pour la base de données 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înes.

Autres références