Android TV utilise l'interface de recherche Android pour récupérer les données de contenu des applications installées et fournir des résultats de recherche à l'utilisateur. Les données de contenu de votre application peuvent être incluses dans ces résultats pour permettre à l'utilisateur d'accéder instantanément au contenu de votre application.
Votre application doit fournir à Android TV les champs de données à partir desquels elle peut générer des résultats de recherche suggérés lorsque l'utilisateur saisit des caractères dans la boîte de dialogue de recherche. Pour ce faire, votre application doit implémenter un fournisseur de contenu qui renvoie les suggestions, ainsi qu'un fichier de configuration
searchable.xml
décrivant le fournisseur de contenu et d'autres informations essentielles pour Android TV. Vous avez également besoin d'une activité qui gère l'intent qui se déclenche lorsque l'utilisateur sélectionne un résultat de recherche suggéré. Pour en savoir plus, consultez Ajouter des suggestions de recherche personnalisées. Ce guide décrit les points principaux spécifiques aux applications Android TV.
Avant de lire ce guide, assurez-vous de connaître les concepts décrits dans le guide de l'API Search. Consultez également Ajouter une fonctionnalité de recherche.
L'exemple de code présenté dans ce guide provient de l' application exemple Leanback.
Identifier les colonnes
SearchManager
décrit les champs de données attendus en les représentant en tant que 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 à ces colonnes, généralement dans la classe qui accède à vos données de contenu. Pour savoir comment créer une classe qui mappe vos données existantes aux champs obligatoires, consultez
Créer une table de suggestions.
La classe SearchManager
comprend plusieurs colonnes pour Android TV. Certaines des 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, affiche ou couverture de votre contenu |
SUGGEST_COLUMN_CONTENT_TYPE |
Type MIME de votre média |
SUGGEST_COLUMN_VIDEO_WIDTH |
Largeur de la résolution de votre contenu multimédia |
SUGGEST_COLUMN_VIDEO_HEIGHT |
Hauteur de résolution de votre contenu multimédia |
SUGGEST_COLUMN_PRODUCTION_YEAR |
L'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 nécessite les colonnes suivantes:
Lorsque les valeurs de ces colonnes pour votre contenu correspondent aux valeurs du même contenu provenant d'autres fournisseurs trouvés par les serveurs Google, le système fournit un lien profond vers votre application dans la vue détaillée du contenu, ainsi que des liens vers les applications d'autres fournisseurs. Pour en savoir plus, consultez la section Lien profond vers votre application sur 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 correspondance à partir des colonnes SearchManager
avec vos champs de données, vous devez également spécifier le _ID
pour attribuer un ID unique à chaque ligne.
Kotlin
companion object { .... private fun buildColumnMap(): Map<String, 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 le champ SUGGEST_COLUMN_INTENT_DATA_ID
. Il s'agit de la partie de l'URI qui pointe vers le contenu propre aux données de cette ligne. Il s'agit de la dernière partie de l'URI, qui décrit l'emplacement de stockage du contenu. La première partie de l'URI, lorsqu'elle est commune à toutes les lignes de la table, est définie dans le fichier searchable.xml
en tant qu'attribut
android:searchSuggestIntentData
, comme décrit dans la section Gérer les suggestions de recherche.
Si la première partie de l'URI est différente pour chaque ligne de la table, 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 la combinaison de SUGGEST_COLUMN_INTENT_DATA_ID
et de l'attribut android:searchSuggestIntentData
ou de la valeur du champ SUGGEST_COLUMN_INTENT_DATA
.
Fournir des données de suggestion de recherche
Implémentez 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 fournisseur de contenu pour obtenir des suggestions en appelant la méthode query()
chaque fois qu'une lettre est saisie. Dans votre implémentation de query()
, votre fournisseur de contenu recherche vos données de suggestion et renvoie un Cursor
qui pointe vers les lignes que vous avez désignées pour des 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 fait l'objet d'un traitement spécial. Plutôt que d'être taguée en tant qu'activité, elle est décrite comme une <provider>
. Le fournisseur inclut l'attribut android:authorities
pour indiquer au système l'espace de noms 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 renvoyés.
<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 fichier
res/xml/searchable.xml
pour configurer les paramètres des suggestions de recherche.
Dans le fichier res/xml/searchable.xml
, incluez l'attribut
android:searchSuggestAuthority
pour indiquer au système l'espace de noms de votre fournisseur de contenu. Elle doit correspondre à la valeur de chaîne que vous spécifiez dans l'attribut android:authorities
de l'élément <provider>
de votre fichier AndroidManifest.xml
.
Incluez également un libellé, qui correspond au nom de l'application. Les paramètres de recherche du système utilisent ce libellé pour énumérer les applications pouvant faire l'objet d'une recherche.
Le fichier searchable.xml
doit également inclure
android:searchSuggestIntentAction
avec la valeur "android.intent.action.VIEW"
pour définir l'action d'intent permettant de fournir une suggestion personnalisée. Cette opération est différente de l'action d'intent pour fournir un terme de recherche, comme décrit dans la section suivante.
Pour découvrir d'autres façons de déclarer l'action d'intent pour obtenir des suggestions, consultez la section Déclarer l'action d'intent.
Avec l'action d'intent, votre application doit fournir les données d'intent, que vous spécifiez avec l'attribut
android:searchSuggestIntentData
. Il s'agit de la première partie de l'URI qui pointe vers le contenu. Elle décrit la partie de l'URI commune à toutes les lignes de la table de mappage pour ce contenu. La partie de l'URI propre à chaque ligne est établie avec le champ SUGGEST_COLUMN_INTENT_DATA_ID
, comme décrit dans la section Identifier les colonnes.
Pour découvrir d'autres façons de déclarer des données d'intent afin d'obtenir des suggestions, consultez la section Déclarer les données d'intent.
L'attribut android:searchSuggestSelection=" ?"
spécifie la valeur transmise en tant que paramètre selection
de la méthode query()
. La valeur du point d'interrogation (?
) est remplacée par le texte de la requête.
Enfin, vous devez également inclure l'attribut
android:includeInGlobalSearch
avec la valeur "true"
. Voici un exemple de 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 qui correspond à la valeur de l'une des colonnes de votre application, comme décrit dans la section Identifier les colonnes, le système déclenche l'intent ACTION_SEARCH
.
L'activité de votre application qui gère cet intent recherche dans le dépôt les colonnes contenant le mot donné dans leurs valeurs, puis renvoie une liste d'éléments de contenu avec ces colonnes. Dans votre fichier AndroidManifest.xml
, vous désignez l'activité qui gère l'intent ACTION_SEARCH
, comme indiqué 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 en faisant référence au fichier searchable.xml
.
Pour utiliser la boîte de dialogue de recherche globale, le fichier manifeste doit décrire l'activité qui doit recevoir les requêtes de recherche. Le fichier manifeste doit également décrire l'élément <provider>
exactement tel qu'il est décrit dans le fichier searchable.xml
.
Lien profond vers votre appli sur l'écran d'informations
Si vous avez configuré la configuration de recherche comme décrit dans la section Gérer les suggestions de recherche et mappé les champs SUGGEST_COLUMN_TEXT_1
, SUGGEST_COLUMN_PRODUCTION_YEAR
et SUGGEST_COLUMN_DURATION
comme décrit dans la section Identifier les colonnes, un
lien profond vers une action de visionnage pour votre contenu apparaît dans l'écran d'informations qui s'ouvre lorsque l'utilisateur sélectionne un résultat de recherche:
Lorsque l'utilisateur sélectionne le lien de votre application (identifié par le bouton **Disponible le** sur l'écran de détails), le système lance l'activité qui gère l'élément ACTION_VIEW
défini sur
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 l'
application exemple Leanback. Notez que l'application exemple lance son propre LeanbackDetailsFragment
pour afficher les détails du contenu multimédia sélectionné. Dans vos applications, lancez immédiatement l'activité qui lit le contenu multimédia pour enregistrer un ou deux clics de l'utilisateur.
Comportement de recherche
La recherche est disponible sur Android TV depuis l'écran d'accueil et dans l'application. Les 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é. Si des applications peuvent lire le contenu, un lien vers chacune d'elles s'affiche au bas de la fiche:
Vous ne pouvez pas placer une application dans la fiche d'entité par programmation. Pour être incluses en tant qu'option de lecture, les résultats de recherche d'une application doivent correspondre au titre, à l'année et à la durée du contenu recherché.
D'autres résultats de recherche peuvent s'afficher sous la fiche. Pour les voir, l'utilisateur doit appuyer sur la télécommande et faire défiler la page 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. Les applications qui acceptent les actions de visionnage sont listées en premier.
Rechercher depuis votre application
L'utilisateur peut également lancer une recherche depuis votre application en lançant le micro à partir de la télécommande ou de la 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.
En savoir plus
Pour en savoir plus sur la recherche dans une application TV, consultez Intégrer les fonctionnalités de recherche Android dans votre application et Ajouter une fonctionnalité de recherche.
Pour savoir comment personnaliser l'expérience de recherche dans l'application avec un SearchFragment
, consultez Rechercher dans les applications TV.