Android TV usa la interfaz de búsqueda de Android. para recuperar datos de contenido de las apps instaladas y enviar los resultados de la búsqueda al usuario. La duración de se pueden incluir datos de contenido en estos resultados para brindar al usuario acceso instantáneo al contenido en tu app.
Tu app debe proporcionar a Android TV los campos de datos a partir de los cuales Android TV puede generar búsquedas sugeridas.
los resultados a medida que el usuario ingresa caracteres en el diálogo de búsqueda. Para ello, tu app debe implementar una
Proveedor de contenido que publica
las sugerencias junto con un
searchable.xml
que describe el contenido
y otra información vital para Android TV. También necesitas una actividad que controle las
que se activa cuando el usuario selecciona un resultado de la búsqueda sugerido. Para
más detalles, consulta Agregar
sugerencias de búsqueda personalizadas. En esta guía, se abordan los puntos principales específicos de las apps para Android TV.
Antes de leer esta guía, asegúrate de familiarizarte con los conceptos que se explican en el Guía de la API de Búsqueda. Además, consulta el artículo Cómo agregar funcionalidad de búsqueda.
El código de muestra de esta guía proviene del App de ejemplo de Leanback de Google Cloud.
Cómo identificar columnas
El SearchManager
describe los campos de datos esperados representándolos de la siguiente manera:
columnas de una base de datos local. Sin importar el formato de tus datos, debes asignar tus campos de datos a
estas columnas, generalmente en la clase que accede a tus datos de contenido. Para obtener información sobre la creación de
una clase que asigna tus datos existentes a los campos obligatorios, consulta
Cómo crear una tabla de sugerencias
La clase SearchManager
incluye varias columnas para Android TV. Algunos de los
las columnas más importantes se describen en la siguiente tabla.
Valor | Descripción |
---|---|
SUGGEST_COLUMN_TEXT_1 |
El nombre de tu contenido (obligatorio) |
SUGGEST_COLUMN_TEXT_2 |
Una descripción del contenido |
SUGGEST_COLUMN_RESULT_CARD_IMAGE |
Una imagen, un póster o una portada para el contenido |
SUGGEST_COLUMN_CONTENT_TYPE |
El tipo de MIME del contenido multimedia |
SUGGEST_COLUMN_VIDEO_WIDTH |
El ancho de la resolución del contenido multimedia |
SUGGEST_COLUMN_VIDEO_HEIGHT |
La altura de la resolución del contenido multimedia |
SUGGEST_COLUMN_PRODUCTION_YEAR |
El año de producción de tu contenido (obligatorio) |
SUGGEST_COLUMN_DURATION |
La duración en milisegundos del contenido multimedia (obligatorio) |
Para el marco de trabajo de búsqueda, se necesitan las siguientes columnas:
Cuando los valores de estas columnas para tu contenido coincidan con los valores para el mismo contenido de otras de infraestructura que encuentran los servidores de Google, el sistema proporciona vínculo directo a tu aplicación en los detalles. para ver el contenido, junto con vínculos a las apps de otros proveedores. Esto se analiza más detalladamente en La sección Vínculo directo a tu app en la pantalla de detalles.
La clase de la base de datos de tu app puede definir las columnas de la siguiente manera:
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; ...
Cuando creas el mapa desde las columnas SearchManager
hasta tus campos de datos,
también debes especificar el _ID
para asignar a cada fila un ID único.
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; } ...
En el ejemplo anterior, observa la asignación al SUGGEST_COLUMN_INTENT_DATA_ID
.
. Esta es la parte del URI que apunta al contenido único de los datos en este
fila: la última parte del URI, que describe dónde se almacena el contenido. La primera parte del URI,
cuando es común a todas las filas de la tabla, se establece en
archivo searchable.xml
como el
android:searchSuggestIntentData
, como se describe en el
Sección Cómo controlar las sugerencias de búsqueda.
Si la primera parte del URI es diferente para cada fila de la
asigna ese valor al campo SUGGEST_COLUMN_INTENT_DATA
.
Cuando el usuario selecciona este contenido, el intent que se activa proporciona los datos de intent del
combinación de SUGGEST_COLUMN_INTENT_DATA_ID
y el atributo android:searchSuggestIntentData
o el
Valor del campo SUGGEST_COLUMN_INTENT_DATA
.
Cómo proporcionar datos de sugerencias de búsqueda
Implementa un proveedor de contenido
para mostrar sugerencias de términos de búsqueda en el diálogo de búsqueda de Android TV. El sistema consulta tu contenido
para obtener sugerencias llamando al método query()
cada vez
una letra está escrita. En la implementación de query()
, tu contenido
proveedor busca tus datos de sugerencias y devuelve un Cursor
que apunta a
las filas que designaste para las sugerencias.
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); } ...
En tu archivo de manifiesto, el proveedor de contenido recibe un tratamiento especial. En lugar de ser
etiquetada como una actividad, se describe como un
<provider>
El
incluye el atributo android:authorities
para indicarle al sistema la
espacio de nombres de tu proveedor de contenido. Además, debes establecer su atributo android:exported
en
"true"
para que la búsqueda global de Android pueda usar los resultados que se muestran.
<provider android:name="com.example.android.tvleanback.VideoContentProvider" android:authorities="com.example.android.tvleanback" android:exported="true" />
Cómo controlar sugerencias de búsqueda
Tu aplicación debe incluir un
res/xml/searchable.xml
para configurar los parámetros de las sugerencias de búsqueda.
En el archivo res/xml/searchable.xml
, incluye
el
android:searchSuggestAuthority
para indicarle al sistema cuál es el espacio de nombres de tu
proveedor de contenido. Debe coincidir con el valor de cadena que especifiques en la
android:authorities
atributo de <provider>
en tu archivo AndroidManifest.xml
.
También incluye una etiqueta, que es el nombre de la aplicación. La configuración de búsqueda del sistema usa esta etiqueta para la enumeración en las que se pueden buscar.
El archivo searchable.xml
también debe incluir el
android:searchSuggestIntentAction
con el valor "android.intent.action.VIEW"
para definir la acción de intent a fin de proporcionar una sugerencia personalizada. Esto es diferente del intent
para proporcionar un término de búsqueda, como se describe en la siguiente sección.
Para conocer otras maneras de declarar la acción de intent para sugerencias,
consulta Declaración del
acción de intent.
Junto con la acción de intent, tu app debe proporcionar los datos del intent, que se especifican con el
atributo android:searchSuggestIntentData
. Esta es la primera parte del URI que apunta
al contenido, que describe la parte del URI común a todas las filas de la tabla de asignación para ese
contenido. La parte del URI que es única para cada fila se establece con el campo SUGGEST_COLUMN_INTENT_DATA_ID
.
como se describe en la sección Cómo identificar columnas.
Si quieres conocer otras maneras de declarar los datos de intents para sugerencias, consulta la sección
Declaración
los datos del intent.
El atributo android:searchSuggestSelection=" ?"
especifica el valor que se pasa.
como el parámetro selection
de query()
. El valor del signo de interrogación (?
) se reemplaza por el texto de la consulta.
Por último, también debes incluir
El atributo android:includeInGlobalSearch
con el valor "true"
Aquí hay un ejemplo
Archivo 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>
Cómo controlar términos de búsqueda
En cuanto el diálogo de búsqueda tenga una palabra que coincida con el valor de una de las columnas de tu app,
descritos en la sección Cómo identificar columnas, el sistema activa
ACTION_SEARCH
.
La actividad de tu app que controla lo siguiente
el intent busca columnas con la palabra determinada en sus valores en el repositorio y muestra una lista
de elementos de contenido con esas columnas. En tu archivo AndroidManifest.xml
, designas el
que controla la ACTION_SEARCH
como se muestra en el siguiente ejemplo:
... <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" /> ...
La actividad también debe describir la configuración de búsqueda con una referencia al
Archivo searchable.xml
.
Para usar el diálogo de búsqueda global, haz lo siguiente:
el manifiesto debe describir qué actividad debe recibir consultas de búsqueda. El manifiesto también debe
describe los <provider>
tal como se describe en el archivo searchable.xml
.
Vínculo directo a tu app en la pantalla de detalles
Si estableciste la configuración de búsqueda como se describe en Administra la búsqueda
sugerencias y asignaste el SUGGEST_COLUMN_TEXT_1
,
SUGGEST_COLUMN_PRODUCTION_YEAR
y
SUGGEST_COLUMN_DURATION
, como se describe en
la sección Identificar columnas, una
vínculo directo a una acción de reproducción de tu contenido aparece en la pantalla de detalles que se inicia cuando
el usuario selecciona un resultado de la búsqueda:
Cuando el usuario selecciona el vínculo a tu app, que se identifica con el botón **Disponible en** en la
Details, el sistema inicia la actividad que controla ACTION_VIEW
establecer como
android:searchSuggestIntentAction
con el valor "android.intent.action.VIEW"
en
el archivo searchable.xml
.
También puedes configurar un intent personalizado para que inicie tu actividad. Esto se demuestra en el
App de ejemplo de Leanback
de Google Cloud. Ten en cuenta que la app de ejemplo inicia su propio LeanbackDetailsFragment
para
mostrar los detalles de los medios seleccionados de tus apps, inicia la actividad que reproduce el contenido multimedia
inmediatamente para ahorrarle al usuario otro clic o dos.
Comportamiento de búsqueda
En Android TV, la búsqueda está disponible desde la pantalla principal y desde la app. Resultados de la búsqueda son diferentes en estos dos casos.
Cómo buscar desde la pantalla principal
Cuando el usuario realiza una búsqueda desde la pantalla principal, el primer resultado aparece en una tarjeta de entidad. Si hay que pueden reproducir el contenido, aparecerá un vínculo a cada una en la parte inferior de la tarjeta:
No puedes colocar una app de manera programática en la tarjeta de entidad. Se debe incluir como opción de reproducción, los resultados de la búsqueda de una aplicación deben coincidir con el título, el año y la duración del contenido buscado.
Es posible que haya más resultados de la búsqueda disponibles debajo de la tarjeta. Para verlos, el usuario debe presionar y desplázate hacia abajo. Los resultados para cada app aparecen en una fila separada. No puedes controlar el el orden de las filas. Apps compatibles watch actions aparecen primero.
Cómo buscar desde tu app
El usuario también puede iniciar una búsqueda desde tu app iniciando el micrófono desde el control remoto o control de control de juegos. Los resultados de la búsqueda se muestran en una sola fila, encima del contenido de la app. Tu app genera resultados de la búsqueda usando su propio proveedor de búsqueda global.
Más información
Para obtener más información sobre cómo buscar en una app para TV, lee lo siguiente: Integra las funciones de búsqueda de Android a tu app y Agrega funcionalidad de búsqueda.
Para obtener más información sobre cómo personalizar la experiencia de búsqueda en la app con un SearchFragment
, lee
Cómo hacer búsquedas en apps para TV