Permettre les recherches dans les applications TV

Android TV utilise l'interface de recherche d'Android. pour récupérer les données de contenu des applications installées et fournir des résultats de recherche à l'utilisateur. Votre application des données de contenu peuvent être incluses dans ces résultats pour permettre à l'utilisateur d'accéder instantanément au contenu votre application.

Votre appli doit fournir à Android TV les champs de données à partir desquels Android TV peut générer des suggestions de recherche à mesure que l'utilisateur saisit des caractères dans la boîte de dialogue de recherche. Pour ce faire, votre application doit implémenter Fournisseur de contenu qui diffuse les suggestions ainsi qu'un searchable.xml, qui décrit le contenu sur le fournisseur d'accès et d'autres informations essentielles pour Android TV. Vous avez également besoin d'une activité qui gère qui se déclenche lorsque l'utilisateur sélectionne un résultat de recherche suggéré. Pour pour en savoir plus, consultez la section Ajouter les suggestions de recherche personnalisées. Ce guide aborde les principaux points spécifiques aux applications Android TV.

Avant de lire ce guide, assurez-vous de bien maîtriser les concepts expliqués dans le Guide de l'API Search Consultez également Ajouter une fonctionnalité de recherche.

L'exemple de code présenté dans ce guide provient du <ph type="x-smartling-placeholder"></ph> Exemple d'application Leanback pour en savoir plus.

Identifier les colonnes

Le SearchManager décrit les champs de données attendus en les représentant comme colonnes d'une base de données locale. Quel que soit le format de vos données, vous devez mapper vos champs de données avec ces colonnes, généralement dans la classe qui accède à vos données de contenu. Pour en savoir plus sur la création une classe qui mappe vos données existantes aux champs obligatoires, consultez <ph type="x-smartling-placeholder"></ph> Créer une table de suggestions

La classe SearchManager comprend plusieurs colonnes pour Android TV. Certaines des les colonnes les plus importantes sont décrites dans le tableau suivant.

Valeur Description
SUGGEST_COLUMN_TEXT_1 Nom de votre contenu (obligatoire)
SUGGEST_COLUMN_TEXT_2 Une description textuelle de votre contenu
SUGGEST_COLUMN_RESULT_CARD_IMAGE Image, poster ou couverture de votre contenu
SUGGEST_COLUMN_CONTENT_TYPE Type MIME de votre contenu multimédia.
SUGGEST_COLUMN_VIDEO_WIDTH La largeur et la résolution de votre contenu multimédia
SUGGEST_COLUMN_VIDEO_HEIGHT La hauteur et la résolution de votre contenu multimédia
SUGGEST_COLUMN_PRODUCTION_YEAR Année de production de votre contenu (obligatoire)
SUGGEST_COLUMN_DURATION Durée de votre contenu multimédia en millisecondes (obligatoire)

Le framework de recherche requiert les colonnes suivantes:

Lorsque les valeurs de ces colonnes pour votre contenu correspondent aux valeurs du même contenu provenant d'autres par les serveurs Google, le système fournit lien profond vers votre application dans les détails consulter le contenu, ainsi que des liens vers les applications d'autres fournisseurs. Ce sujet est abordé plus en détail dans la section Lien profond vers votre application dans l'écran d'informations.

La classe de base de données de votre application peut définir les colonnes comme suit:

Kotlin

class VideoDatabase {
    companion object {
        // The columns we'll include in the video database table
        val KEY_NAME = SearchManager.SUGGEST_COLUMN_TEXT_1
        val KEY_DESCRIPTION = SearchManager.SUGGEST_COLUMN_TEXT_2
        val KEY_ICON = SearchManager.SUGGEST_COLUMN_RESULT_CARD_IMAGE
        val KEY_DATA_TYPE = SearchManager.SUGGEST_COLUMN_CONTENT_TYPE
        val KEY_IS_LIVE = SearchManager.SUGGEST_COLUMN_IS_LIVE
        val KEY_VIDEO_WIDTH = SearchManager.SUGGEST_COLUMN_VIDEO_WIDTH
        val KEY_VIDEO_HEIGHT = SearchManager.SUGGEST_COLUMN_VIDEO_HEIGHT
        val KEY_AUDIO_CHANNEL_CONFIG = SearchManager.SUGGEST_COLUMN_AUDIO_CHANNEL_CONFIG
        val KEY_PURCHASE_PRICE = SearchManager.SUGGEST_COLUMN_PURCHASE_PRICE
        val KEY_RENTAL_PRICE = SearchManager.SUGGEST_COLUMN_RENTAL_PRICE
        val KEY_RATING_STYLE = SearchManager.SUGGEST_COLUMN_RATING_STYLE
        val KEY_RATING_SCORE = SearchManager.SUGGEST_COLUMN_RATING_SCORE
        val KEY_PRODUCTION_YEAR = SearchManager.SUGGEST_COLUMN_PRODUCTION_YEAR
        val KEY_COLUMN_DURATION = SearchManager.SUGGEST_COLUMN_DURATION
        val KEY_ACTION = SearchManager.SUGGEST_COLUMN_INTENT_ACTION
        ...
    }
    ...
}

Java

public class VideoDatabase {
    // The columns we'll include in the video database table
    public static final String KEY_NAME = SearchManager.SUGGEST_COLUMN_TEXT_1;
    public static final String KEY_DESCRIPTION = SearchManager.SUGGEST_COLUMN_TEXT_2;
    public static final String KEY_ICON = SearchManager.SUGGEST_COLUMN_RESULT_CARD_IMAGE;
    public static final String KEY_DATA_TYPE = SearchManager.SUGGEST_COLUMN_CONTENT_TYPE;
    public static final String KEY_IS_LIVE = SearchManager.SUGGEST_COLUMN_IS_LIVE;
    public static final String KEY_VIDEO_WIDTH = SearchManager.SUGGEST_COLUMN_VIDEO_WIDTH;
    public static final String KEY_VIDEO_HEIGHT = SearchManager.SUGGEST_COLUMN_VIDEO_HEIGHT;
    public static final String KEY_AUDIO_CHANNEL_CONFIG =
            SearchManager.SUGGEST_COLUMN_AUDIO_CHANNEL_CONFIG;
    public static final String KEY_PURCHASE_PRICE = SearchManager.SUGGEST_COLUMN_PURCHASE_PRICE;
    public static final String KEY_RENTAL_PRICE = SearchManager.SUGGEST_COLUMN_RENTAL_PRICE;
    public static final String KEY_RATING_STYLE = SearchManager.SUGGEST_COLUMN_RATING_STYLE;
    public static final String KEY_RATING_SCORE = SearchManager.SUGGEST_COLUMN_RATING_SCORE;
    public static final String KEY_PRODUCTION_YEAR = SearchManager.SUGGEST_COLUMN_PRODUCTION_YEAR;
    public static final String KEY_COLUMN_DURATION = SearchManager.SUGGEST_COLUMN_DURATION;
    public static final String KEY_ACTION = SearchManager.SUGGEST_COLUMN_INTENT_ACTION;
...

Lorsque vous créez la carte à partir des colonnes SearchManager vers vos champs de données, vous doit également spécifier _ID pour attribuer un identifiant unique à chaque ligne.

Kotlin

companion object {
    ....
    private fun buildColumnMap(): MapS<tring, String> {
        return mapOf(
          KEY_NAME to KEY_NAME,
          KEY_DESCRIPTION to KEY_DESCRIPTION,
          KEY_ICON to KEY_ICON,
          KEY_DATA_TYPE to KEY_DATA_TYPE,
          KEY_IS_LIVE to KEY_IS_LIVE,
          KEY_VIDEO_WIDTH to KEY_VIDEO_WIDTH,
          KEY_VIDEO_HEIGHT to KEY_VIDEO_HEIGHT,
          KEY_AUDIO_CHANNEL_CONFIG to KEY_AUDIO_CHANNEL_CONFIG,
          KEY_PURCHASE_PRICE to KEY_PURCHASE_PRICE,
          KEY_RENTAL_PRICE to KEY_RENTAL_PRICE,
          KEY_RATING_STYLE to KEY_RATING_STYLE,
          KEY_RATING_SCORE to KEY_RATING_SCORE,
          KEY_PRODUCTION_YEAR to KEY_PRODUCTION_YEAR,
          KEY_COLUMN_DURATION to KEY_COLUMN_DURATION,
          KEY_ACTION to KEY_ACTION,
          BaseColumns._ID to ("rowid AS " + BaseColumns._ID),
          SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID to ("rowid AS " + SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID),
          SearchManager.SUGGEST_COLUMN_SHORTCUT_ID to ("rowid AS " + SearchManager.SUGGEST_COLUMN_SHORTCUT_ID)
        )
    }
}

Java

...
  private static HashMap<String, String> buildColumnMap() {
    HashMap<String, String> map = new HashMap<String, String>();
    map.put(KEY_NAME, KEY_NAME);
    map.put(KEY_DESCRIPTION, KEY_DESCRIPTION);
    map.put(KEY_ICON, KEY_ICON);
    map.put(KEY_DATA_TYPE, KEY_DATA_TYPE);
    map.put(KEY_IS_LIVE, KEY_IS_LIVE);
    map.put(KEY_VIDEO_WIDTH, KEY_VIDEO_WIDTH);
    map.put(KEY_VIDEO_HEIGHT, KEY_VIDEO_HEIGHT);
    map.put(KEY_AUDIO_CHANNEL_CONFIG, KEY_AUDIO_CHANNEL_CONFIG);
    map.put(KEY_PURCHASE_PRICE, KEY_PURCHASE_PRICE);
    map.put(KEY_RENTAL_PRICE, KEY_RENTAL_PRICE);
    map.put(KEY_RATING_STYLE, KEY_RATING_STYLE);
    map.put(KEY_RATING_SCORE, KEY_RATING_SCORE);
    map.put(KEY_PRODUCTION_YEAR, KEY_PRODUCTION_YEAR);
    map.put(KEY_COLUMN_DURATION, KEY_COLUMN_DURATION);
    map.put(KEY_ACTION, KEY_ACTION);
    map.put(BaseColumns._ID, "rowid AS " +
            BaseColumns._ID);
    map.put(SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID, "rowid AS " +
            SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID);
    map.put(SearchManager.SUGGEST_COLUMN_SHORTCUT_ID, "rowid AS " +
            SearchManager.SUGGEST_COLUMN_SHORTCUT_ID);
    return map;
  }
...

Dans l'exemple précédent, notez le mappage avec SUGGEST_COLUMN_INTENT_DATA_ID. . Il s'agit de la partie de l'URI qui pointe vers le contenu unique aux données de cette ligne qui correspond à la dernière partie de l'URI, décrivant l'emplacement de stockage du contenu. La première partie de l'URI, lorsqu'il est commun à toutes les lignes de la table, est défini dans searchable.xml en tant que <ph type="x-smartling-placeholder"></ph> android:searchSuggestIntentData, comme décrit dans les Gérer les suggestions de recherche.

Si la première partie de l'URI est différente pour chaque ligne de mappez cette valeur avec le champ SUGGEST_COLUMN_INTENT_DATA. Lorsque l'utilisateur sélectionne ce contenu, l'intent qui se déclenche fournit les données d'intent à partir de combinaison de SUGGEST_COLUMN_INTENT_DATA_ID et soit l'attribut android:searchSuggestIntentData, soit Valeur du champ SUGGEST_COLUMN_INTENT_DATA.

Fournir des données de suggestion de recherche

Implémenter un fournisseur de contenu pour renvoyer des suggestions de termes de recherche dans la boîte de dialogue de recherche d'Android TV. Le système interroge votre contenu pour obtenir des suggestions en appelant la méthode query() à chaque fois une lettre est tapée. Dans votre implémentation de query(), votre contenu Le fournisseur recherche vos données de suggestion et renvoie un Cursor qui pointe vers les lignes que vous avez désignées pour les suggestions.

Kotlin

fun query(uri: Uri, projection: Array<String>, selection: String, selectionArgs: Array<String>,
        sortOrder: String): Cursor {
    // Use the UriMatcher to see what kind of query we have and format the db query accordingly
    when (URI_MATCHER.match(uri)) {
        SEARCH_SUGGEST -> {
            Log.d(TAG, "search suggest: ${selectionArgs[0]} URI: $uri")
            if (selectionArgs == null) {
                throw IllegalArgumentException(
                        "selectionArgs must be provided for the Uri: $uri")
            }
            return getSuggestions(selectionArgs[0])
        }
        else -> throw IllegalArgumentException("Unknown Uri: $uri")
    }
}

private fun getSuggestions(query: String): Cursor {
    val columns = arrayOf<String>(
            BaseColumns._ID,
            VideoDatabase.KEY_NAME,
            VideoDatabase.KEY_DESCRIPTION,
            VideoDatabase.KEY_ICON,
            VideoDatabase.KEY_DATA_TYPE,
            VideoDatabase.KEY_IS_LIVE,
            VideoDatabase.KEY_VIDEO_WIDTH,
            VideoDatabase.KEY_VIDEO_HEIGHT,
            VideoDatabase.KEY_AUDIO_CHANNEL_CONFIG,
            VideoDatabase.KEY_PURCHASE_PRICE,
            VideoDatabase.KEY_RENTAL_PRICE,
            VideoDatabase.KEY_RATING_STYLE,
            VideoDatabase.KEY_RATING_SCORE,
            VideoDatabase.KEY_PRODUCTION_YEAR,
            VideoDatabase.KEY_COLUMN_DURATION,
            VideoDatabase.KEY_ACTION,
            SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID
    )
    return videoDatabase.getWordMatch(query.toLowerCase(), columns)
}

Java

@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
        String sortOrder) {
    // Use the UriMatcher to see what kind of query we have and format the db query accordingly
    switch (URI_MATCHER.match(uri)) {
        case SEARCH_SUGGEST:
            Log.d(TAG, "search suggest: " + selectionArgs[0] + " URI: " + uri);
            if (selectionArgs == null) {
                throw new IllegalArgumentException(
                        "selectionArgs must be provided for the Uri: " + uri);
            }
            return getSuggestions(selectionArgs[0]);
        default:
            throw new IllegalArgumentException("Unknown Uri: " + uri);
    }
}

private Cursor getSuggestions(String query) {
    query = query.toLowerCase();
    String[] columns = new String[]{
        BaseColumns._ID,
        VideoDatabase.KEY_NAME,
        VideoDatabase.KEY_DESCRIPTION,
        VideoDatabase.KEY_ICON,
        VideoDatabase.KEY_DATA_TYPE,
        VideoDatabase.KEY_IS_LIVE,
        VideoDatabase.KEY_VIDEO_WIDTH,
        VideoDatabase.KEY_VIDEO_HEIGHT,
        VideoDatabase.KEY_AUDIO_CHANNEL_CONFIG,
        VideoDatabase.KEY_PURCHASE_PRICE,
        VideoDatabase.KEY_RENTAL_PRICE,
        VideoDatabase.KEY_RATING_STYLE,
        VideoDatabase.KEY_RATING_SCORE,
        VideoDatabase.KEY_PRODUCTION_YEAR,
        VideoDatabase.KEY_COLUMN_DURATION,
        VideoDatabase.KEY_ACTION,
        SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID
    };
    return videoDatabase.getWordMatch(query, columns);
}
...

Dans votre fichier manifeste, le fournisseur de contenu reçoit un traitement spécial. Plutôt que d'être marquée comme une activité, elle est décrite comme <provider> La Le fournisseur inclut l'attribut android:authorities pour indiquer au système de votre fournisseur de contenu. Vous devez également définir son attribut android:exported sur "true" afin que la recherche globale Android puisse utiliser les résultats qu'elle renvoie.

<provider android:name="com.example.android.tvleanback.VideoContentProvider"
    android:authorities="com.example.android.tvleanback"
    android:exported="true" />

Gérer les suggestions de recherche

Votre application doit inclure un élément . res/xml/searchable.xml pour configurer les paramètres des suggestions de recherche.

Dans le fichier res/xml/searchable.xml, incluez le android:searchSuggestAuthority pour indiquer au système l'espace de noms de votre fournisseur de contenu. Il doit correspondre à la valeur de chaîne spécifiée dans android:authorities attribut du <provider> dans votre fichier AndroidManifest.xml.

Incluez également un libellé, qui est le nom de l'application. Les paramètres de recherche du système utilisent ce libellé pour l'énumération dans les applications de recherche.

Le fichier searchable.xml doit également inclure le paramètre android:searchSuggestIntentAction avec la valeur "android.intent.action.VIEW" pour définir l'action d'intent pour fournir une suggestion personnalisée. Ceci est différent de l'intent pour indiquer un terme de recherche, comme décrit dans la section suivante. Pour les autres façons de déclarer l'action d'intent pour les suggestions, consultez la section Déclarer une action d'intent.

En plus de l'action d'intent, votre application doit fournir les données d'intent que vous spécifiez à l'aide de l'attribut <ph type="x-smartling-placeholder"></ph> android:searchSuggestIntentData. Il s'agit de la première partie de l'URI qui pointe au contenu, qui décrit la partie de l'URI commune à toutes les lignes de la table de mappage pour cette contenus. La partie de l'URI unique à chaque ligne est établie à l'aide du champ SUGGEST_COLUMN_INTENT_DATA_ID, comme décrit dans la section Identifier les colonnes. Pour connaître d'autres façons de déclarer les données d'intent pour les suggestions, consultez Déclaration les données d'intent.

L'attribut android:searchSuggestSelection=" ?" spécifie la valeur transmise. en tant que paramètre selection de query(). . La valeur du point d'interrogation (?) est remplacée par le texte de la requête.

Enfin, vous devez également inclure le paramètre android:includeInGlobalSearch avec la valeur "true". Voici un exemple : Fichier searchable.xml:

<searchable xmlns:android="http://schemas.android.com/apk/res/android"
    android:label="@string/search_label"
    android:hint="@string/search_hint"
    android:searchSettingsDescription="@string/settings_description"
    android:searchSuggestAuthority="com.example.android.tvleanback"
    android:searchSuggestIntentAction="android.intent.action.VIEW"
    android:searchSuggestIntentData="content://com.example.android.tvleanback/video_database_leanback"
    android:searchSuggestSelection=" ?"
    android:searchSuggestThreshold="1"
    android:includeInGlobalSearch="true">
</searchable>

Gérer les termes de recherche

Dès que la boîte de dialogue de recherche contient un mot correspondant à la valeur de l'une des colonnes de votre application, comme décrit dans la section Identifier les colonnes, le système déclenche la Intent ACTION_SEARCH. L'activité dans votre application qui gère cela intent recherche dans le dépôt les colonnes contenant le mot donné dans ses valeurs et renvoie une liste d'éléments de contenu avec ces colonnes. Dans votre fichier AndroidManifest.xml, vous désignez activité qui gère les ACTION_SEARCH comme illustré dans l'exemple suivant:

...
  <activity
      android:name="com.example.android.tvleanback.DetailsActivity"
      android:exported="true">

      <!-- Receives the search request. -->
      <intent-filter>
          <action android:name="android.intent.action.SEARCH" />
          <!-- No category needed, because the Intent will specify this class component -->
      </intent-filter>

      <!-- Points to searchable meta data. -->
      <meta-data android:name="android.app.searchable"
          android:resource="@xml/searchable" />
  </activity>
...
  <!-- Provides search suggestions for keywords against video meta data. -->
  <provider android:name="com.example.android.tvleanback.VideoContentProvider"
      android:authorities="com.example.android.tvleanback"
      android:exported="true" />
...

L'activité doit également décrire la configuration incluse dans l'index de recherche et faire référence au searchable.xml. Pour utiliser la boîte de dialogue de recherche globale : le fichier manifeste doit décrire l'activité qui doit recevoir des requêtes de recherche. Le fichier manifeste doit également décrivez <provider> , tel qu'il est décrit dans le fichier searchable.xml.

Créer un lien profond vers votre application dans l'écran d'informations

Si vous avez défini la configuration de la recherche comme décrit dans la section Gérer la recherche suggestions et a mis en correspondance les SUGGEST_COLUMN_TEXT_1, SUGGEST_COLUMN_PRODUCTION_YEAR et Champs SUGGEST_COLUMN_DURATION comme décrit dans la section Identifier les colonnes, un un lien profond vers une action de visionnage pour votre contenu apparaît dans l'écran d'informations qui se lance lorsque l'utilisateur sélectionne un résultat de recherche:

Lien profond dans l&#39;écran d&#39;informations

Lorsque l'utilisateur sélectionne le lien vers votre application, identifié par le bouton **Disponible le** dans le écran d'informations, le système lance l'activité qui gère le ACTION_VIEW Définir en tant que android:searchSuggestIntentAction avec la valeur "android.intent.action.VIEW" dans le fichier searchable.xml.

Vous pouvez également configurer un intent personnalisé pour lancer votre activité. Cela est illustré dans la <ph type="x-smartling-placeholder"></ph> Exemple d'application Leanback pour en savoir plus. Notez que l'application exemple lance son propre LeanbackDetailsFragment pour afficher les détails du média sélectionné ; dans vos applications, lancez l'activité qui lit le contenu multimédia immédiatement pour faire gagner un ou deux clics à l'utilisateur.

Comportement de recherche

La recherche est disponible sur Android TV depuis l'écran d'accueil et depuis votre application. Résultats de recherche sont différents dans ces deux cas.

Rechercher depuis l'écran d'accueil

Lorsque l'utilisateur effectue une recherche depuis l'écran d'accueil, le premier résultat apparaît dans une fiche d'entité. S'il y a applications pouvant lire ce contenu, un lien vers chacune d'elles s'affiche au bas de la fiche:

Lecture des résultats de recherche TV

Vous ne pouvez pas placer une application par programmation dans la fiche de l'entité. À inclure en tant que l'option de lecture, les résultats de recherche d'une application doivent correspondre au titre, à l'année et à la durée le contenu recherché.

D'autres résultats de recherche peuvent être disponibles sous la carte. Pour les voir, l'utilisateur doit appuyer sur le et faites défiler l'écran vers le bas. Les résultats pour chaque application s'affichent sur une ligne distincte. Vous ne pouvez pas contrôler l’ordre des lignes. Applis compatibles actions de visionnage sont répertoriées en premier.

Résultats de recherche TV

Effectuer une recherche depuis votre application

L'utilisateur peut également lancer une recherche depuis votre application en activant le micro à partir de la télécommande ou manette de jeu. Les résultats de recherche sont affichés sur une seule ligne au-dessus du contenu de l'application. Votre application génère des résultats de recherche à l'aide de son propre moteur de recherche global.

Résultats de recherche dans l&#39;application TV

En savoir plus

Pour en savoir plus sur la recherche d'une appli TV, consultez Intégrer les fonctionnalités de recherche Android dans votre application Ajoutez une fonctionnalité de recherche.

Pour en savoir plus sur la personnalisation de l'expérience de recherche dans l'application avec un SearchFragment, consultez Effectuer des recherches dans les applications TV