TV-Apps suchbar machen

Android TV verwendet die Android-Suchoberfläche um Inhaltsdaten aus installierten Apps abzurufen und dem Nutzer Suchergebnisse zu liefern. Der können Inhaltsdaten in diese Ergebnisse aufgenommen werden, um dem Nutzer sofortigen Zugriff auf die Inhalte in für Ihre App.

Ihre App muss die Datenfelder an Android TV senden, aus denen Android TV die Vorschläge für die Suche generieren kann während der Nutzer Zeichen in das Suchdialogfeld eingibt. Dazu muss in Ihrer App ein Contentanbieter, der Ihre Inhalte bereitstellt die Vorschläge zusammen mit einem searchable.xml-Konfigurationsdatei, die den Inhalt beschreibt Anbieter und andere wichtige Informationen für Android TV. Sie benötigen auch eine Aktivität, Intent, der ausgelöst wird, wenn der Nutzer ein vorgeschlagenes Suchergebnis auswählt. Für erhalten Sie unter benutzerdefinierte Suchvorschläge In diesem Leitfaden werden die wichtigsten Punkte zu Android TV-Apps behandelt.

Machen Sie sich vor dem Lesen dieses Leitfadens mit den Konzepten vertraut, die in den Leitfaden zur Search API Lesen Sie auch den Hilfeartikel Suchfunktion hinzufügen.

Der Beispielcode in diesem Handbuch stammt aus dem <ph type="x-smartling-placeholder"></ph> Leanback-Beispiel-App .

Spalten identifizieren

In SearchManager werden die erwarteten Datenfelder beschrieben, indem sie als Spalten einer lokalen Datenbank. Unabhängig vom Format der Daten müssen Sie Ihre Datenfelder in der Regel in der Klasse, die auf Ihre Inhaltsdaten zugreift. Informationen zum Erstellen eine Klasse erstellen, die Ihre vorhandenen Daten den erforderlichen Feldern zuordnet, siehe <ph type="x-smartling-placeholder"></ph> Tabelle mit Vorschlägen erstellen

Die Klasse SearchManager enthält mehrere Spalten für Android TV. Einige der wichtige Spalten in der folgenden Tabelle beschrieben.

Wert Beschreibung
SUGGEST_COLUMN_TEXT_1 Der Name des Inhalts (erforderlich)
SUGGEST_COLUMN_TEXT_2 Eine Textbeschreibung deiner Inhalte
SUGGEST_COLUMN_RESULT_CARD_IMAGE Ein Bild, Poster oder Cover für deine Inhalte
SUGGEST_COLUMN_CONTENT_TYPE Der MIME-Typ Ihrer Medien
SUGGEST_COLUMN_VIDEO_WIDTH Die Auflösungsbreite deiner Medien
SUGGEST_COLUMN_VIDEO_HEIGHT Die Auflösungshöhe deiner Medien
SUGGEST_COLUMN_PRODUCTION_YEAR Das Produktionsjahr deiner Inhalte (erforderlich)
SUGGEST_COLUMN_DURATION Die Dauer des Mediums in Millisekunden (erforderlich)

Für das Such-Framework sind folgende Spalten erforderlich:

Wenn die Werte dieser Spalten für Ihren Content mit den Werten für denselben Content aus anderen von Google-Servern gefunden werden, stellt das System eine Deeplink zu Ihrer App in den Details sowie Links zu den Apps anderer Anbieter. Dies wird ausführlicher in im Abschnitt Deeplink zu Ihrer App.

Die Datenbankklasse Ihrer Anwendung könnte die Spalten wie folgt definieren:

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;
...

Wenn Sie die Zuordnung der SearchManager-Spalten zu Ihren Datenfeldern erstellen, muss auch die _ID angeben, damit jeder Zeile eine eindeutige ID zugewiesen wird.

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;
  }
...

Beachten Sie im vorherigen Beispiel die Zuordnung zur SUGGEST_COLUMN_INTENT_DATA_ID. ein. Dies ist der Teil der URI, der auf den eindeutigen Inhalt für die Daten in dieser Zeile: Der letzte Teil des URI, der den Speicherort des Inhalts beschreibt. Der erste Teil des URI, für alle Zeilen in der Tabelle gilt, wird in der searchable.xml als <ph type="x-smartling-placeholder"></ph> android:searchSuggestIntentData, wie in den Umgang mit Suchvorschlägen.

Wenn sich der erste Teil des URI für jede Zeile in der ordnen Sie diesen Wert dem Feld SUGGEST_COLUMN_INTENT_DATA zu. Wenn der Nutzer diesen Inhalt auswählt, liefert der ausgelöste Intent die Intent-Daten aus dem Kombination aus SUGGEST_COLUMN_INTENT_DATA_ID und entweder das Attribut android:searchSuggestIntentData oder SUGGEST_COLUMN_INTENT_DATA-Feldwert.

Daten zu Suchvorschlägen angeben

Contentanbieter implementieren , um Vorschläge für Suchbegriffe im Suchdialog von Android TV zurückzugeben. Ihre Inhalte werden vom System abgefragt. Anbieter für Vorschläge durch Aufrufen der Methode query() jedes Mal ein Buchstabe eingegeben wird. Bei der Implementierung von query() werden Ihre Inhalte sucht der Anbieter nach Ihren Vorschlagsdaten und gibt eine Cursor zurück, die auf den Zeilen, die Sie für Vorschläge vorgesehen haben.

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);
}
...

In Ihrer Manifestdatei wird der Contentanbieter gesondert behandelt. Anstelle von wird sie als Aktivität bezeichnet, <provider> Die Der Anbieter verwendet das Attribut android:authorities, um dem System mitzuteilen, -Namespace Ihres Contentanbieters. Außerdem müssen Sie das Attribut android:exported auf "true" verwenden, damit die globale Android-Suche die von ihr zurückgegebenen Ergebnisse verwenden kann.

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

Suchvorschläge verarbeiten

Deine App muss ein enthalten. res/xml/searchable.xml, um die Einstellungen für Suchvorschläge zu konfigurieren.

Fügen Sie in die Datei res/xml/searchable.xml das android:searchSuggestAuthority, um dem System den Namespace Ihres Contentanbieter. Er muss mit dem Stringwert übereinstimmen, den Sie in den android:authorities <provider>-Attribut -Element in der Datei AndroidManifest.xml enthalten.

Fügen Sie außerdem ein Label, Dies ist der Name der Anwendung. In den Einstellungen der Systemsuche wird dieses Label bei der Aufzählung verwendet durchsuchbaren Apps.

Die Datei searchable.xml muss auch den android:searchSuggestIntentAction mit dem Wert "android.intent.action.VIEW" , um die Intent-Aktion für einen benutzerdefinierten Vorschlag zu definieren. Dies unterscheidet sich von der Absicht, Aktion zur Angabe eines Suchbegriffs, wie im folgenden Abschnitt beschrieben. Weitere Möglichkeiten zum Deklarieren der Intent-Aktion für Vorschläge siehe Deklarieren der Intent-Aktion.

Zusammen mit der Intent-Aktion muss Ihre App die Intent-Daten bereitstellen, die Sie mit dem <ph type="x-smartling-placeholder"></ph> android:searchSuggestIntentData. Dies ist der erste Teil des URI, der auf an den Inhalt, wodurch der Teil des URI beschrieben wird, der allen Zeilen in der Zuordnungstabelle gemeinsam ist. Inhalte. Der Teil des URI, der für jede Zeile eindeutig ist, wird über das Feld SUGGEST_COLUMN_INTENT_DATA_ID erstellt. enthalten, wie im Abschnitt Spalten identifizieren beschrieben. Weitere Möglichkeiten zum Deklarieren der Intent-Daten für Vorschläge findest du unter Angaben die Intent-Daten.

Das Attribut android:searchSuggestSelection=" ?" gibt den übergebenen Wert an als selection-Parameter von query() . Das Fragezeichen (?) wird durch den Abfragetext ersetzt.

Abschließend müssen Sie auch den Parameter android:includeInGlobalSearch-Attribut mit dem Wert "true". Hier ist ein Beispiel searchable.xml-Datei:

<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>

Suchbegriffe verarbeiten

Sobald das Suchdialogfeld ein Wort enthält, das mit dem Wert in einer der Spalten Ihrer App übereinstimmt, Spalten identifizieren beschrieben, löst das System die ACTION_SEARCH Intent. Die Aktivität in Ihrer App, die dies verarbeitet Intent durchsucht das Repository nach Spalten, die das angegebene Wort in ihren Werten enthalten, und gibt eine Liste zurück mit diesen Spalten zu sortieren. In der Datei AndroidManifest.xml legen Sie fest, die die ACTION_SEARCH Intents wie im folgenden Beispiel gezeigt:

...
  <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" />
...

Die Aktivität muss auch die durchsuchbare Konfiguration mit einem Verweis auf die searchable.xml-Datei. So verwenden Sie das Dialogfeld für die globale Suche: Im Manifest muss angegeben sein, für welche Aktivität Suchanfragen erfasst werden sollen. Das Manifest muss außerdem <provider> beschreiben -Element enthalten, genau so, wie es in der Datei searchable.xml beschrieben ist.

Deeplink zu Ihrer App auf dem Detailbildschirm

Wenn Sie die Suchkonfiguration wie im Abschnitt zur Verarbeitung der Suche Vorschläge hinzugefügt und SUGGEST_COLUMN_TEXT_1, SUGGEST_COLUMN_PRODUCTION_YEAR und SUGGEST_COLUMN_DURATION-Felder, wie in im Abschnitt Spalten identifizieren, ein Deeplink zu einer Wiedergabeaktion für deine Inhalte wird auf dem Bildschirm „Details“ angezeigt, der beim Der Nutzer wählt ein Suchergebnis aus:

Deeplink auf dem Detailbildschirm

Wenn der Nutzer den Link für Ihre App auswählt, gekennzeichnet durch die Schaltfläche **Verfügbar auf** in der Details angezeigt, startet das System die Aktivität, mit der die ACTION_VIEW festlegen als android:searchSuggestIntentAction mit dem Wert "android.intent.action.VIEW" in die Datei searchable.xml.

Sie können auch einen benutzerdefinierten Intent einrichten, um Ihre Aktivität zu starten. Dies wird in den <ph type="x-smartling-placeholder"></ph> Leanback-Beispiel-App . Die Beispiel-App startet eine eigene LeanbackDetailsFragment, um Details zum ausgewählten Medium aufrufen in Ihren Apps starten Sie die Aktivität, mit der die Medien wiedergegeben werden. um einen weiteren Klick für den Nutzer zu speichern.

Suchverhalten

Die Suchfunktion in Android TV ist auf dem Startbildschirm und in Ihrer App verfügbar. Suchergebnisse in diesen beiden Fällen unterschiedlich sind.

Vom Startbildschirm aus suchen

Wenn der Nutzer eine Suche auf dem Startbildschirm durchführt, wird das erste Ergebnis in einer Entitätskarte angezeigt. Wenn es Apps, die die Inhalte abspielen können, wird unten auf der Karte ein Link zu jeder App angezeigt:

Wiedergabe von TV-Suchergebnissen

Es ist nicht möglich, eine App programmatisch in die Entitätskarte einzufügen. Einschließlich Wiedergabeoption ausgewählt haben, müssen die Suchergebnisse einer App mit dem Titel, dem Jahr und der Dauer der durchsuchte Inhalte.

Möglicherweise sind unter der Karte weitere Suchergebnisse zu sehen. Um sie zu sehen, muss der Nutzer auf die und scrollen Sie nach unten. Die Ergebnisse für jede Anwendung werden in einer separaten Zeile angezeigt. Sie können die Zeilensortierung. Apps mit Unterstützung Watch Actions werden zuerst aufgeführt.

TV-Suchergebnisse

Über die App suchen

Nutzer können eine Suche auch in Ihrer App starten, indem sie das Mikrofon über die Fernbedienung oder Gamepad-Controller. Die Suchergebnisse werden in einer einzelnen Zeile über dem App-Inhalt angezeigt. Ihre App generiert Suchergebnisse mithilfe ihres eigenen globalen Suchanbieters.

TV-In-App-Suchergebnisse

Weitere Informationen

Weitere Informationen zur Suche nach einer TV-App findest du unter Integriere Android-Suchfunktionen in deine App und Suchfunktion hinzufügen

Weitere Informationen zum Anpassen der In-App-Suche mit einem SearchFragment findest du unter In TV-Apps suchen.