Android TV używa interfejsu wyszukiwania na Androidzie TV do pobierania danych o treściach z zainstalowanych aplikacji i dostarczania użytkownikowi wyników wyszukiwania. W przypadku Twojej aplikacji do tych wyników można dołączyć dane o treści, aby umożliwić użytkownikowi natychmiastowy dostęp do treści do aplikacji.
Aplikacja musi udostępniać na Androidzie TV pola danych, na podstawie których Android TV może generować sugerowane wyszukiwania
gdy użytkownik wpisuje znaki w oknie wyszukiwania. Aby to zrobić, aplikacja musi zaimplementować komponent
Dostawca treści, który udostępnia treści
w górę sugestii wraz z przyciskiem
plik konfiguracji searchable.xml
, który opisuje treść.
dostawcy usług i innych ważnych informacji na temat Androida TV. Potrzebujesz też aktywności, która obsługuje
intencja, która jest uruchamiana, gdy użytkownik wybierze sugerowany wynik wyszukiwania. Dla:
więcej informacji można znaleźć w sekcji Dodawanie
niestandardowe sugestie wyszukiwania. W tym przewodniku omawiamy główne zagadnienia związane z aplikacjami na Androida TV.
Zanim przeczytasz ten przewodnik, upewnij się, że znasz pojęcia omówione w Przewodnik po interfejsie Search API. Zapoznaj się też z artykułem Dodawanie funkcji wyszukiwania.
Przykładowy kod w tym przewodniku pochodzi z Przykładowa aplikacja Leanback ,
Określ kolumny
Atrybut SearchManager
opisuje pola danych, których oczekuje, przedstawiając je jako
w lokalnej bazie danych. Niezależnie od formatu danych musisz zmapować pola danych na
te kolumny, zwykle w ramach klasy, która uzyskuje dostęp do danych o treści. Informacje o tworzeniu
klasy, która mapuje istniejące dane na wymagane pola, zobacz
Tworzę tabelę sugestii.
Klasa SearchManager
zawiera kilka kolumn dotyczących Androida TV. Niektóre
ważniejsze kolumny zostały opisane w poniższej tabeli.
Wartość | Opis |
---|---|
SUGGEST_COLUMN_TEXT_1 |
Nazwa treści (wymagana) |
SUGGEST_COLUMN_TEXT_2 |
Opis treści |
SUGGEST_COLUMN_RESULT_CARD_IMAGE |
Obraz, plakat lub okładka treści |
SUGGEST_COLUMN_CONTENT_TYPE |
Typ MIME multimediów |
SUGGEST_COLUMN_VIDEO_WIDTH |
Rozdzielczość – szerokość multimediów |
SUGGEST_COLUMN_VIDEO_HEIGHT |
Wysokość rozdzielczości multimediów. |
SUGGEST_COLUMN_PRODUCTION_YEAR |
Rok produkcji treści (wymagany) |
SUGGEST_COLUMN_DURATION |
Czas trwania multimediów w milisekundach (wymagany) |
Struktura wyszukiwania wymaga tych kolumn:
Gdy wartości w tych kolumnach dla Twojej treści są zgodne z wartościami dla tej samej treści w innych kolumnach znalezionych przez serwery Google, system udostępnia precyzyjny link do aplikacji w szczegółach aplikacji. wraz z linkami do aplikacji innych dostawców. Jest to bardziej omówione w sekcji Precyzyjny link do aplikacji na ekranie szczegółów.
Klasa bazy danych aplikacji może definiować kolumny w następujący sposób:
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; ...
Kiedy tworzysz mapę z kolumn SearchManager
na pola danych,
musi też określać _ID
, aby nadać każdemu wierszowi unikalny identyfikator.
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; } ...
W poprzednim przykładzie zwróć uwagę na mapowanie elementu SUGGEST_COLUMN_INTENT_DATA_ID
. Jest to część identyfikatora URI wskazująca treść unikalną dla danych w tym
wiersz – ostatnia część identyfikatora URI, określająca miejsce przechowywania treści. Pierwsza część identyfikatora URI
jeśli jest wspólne dla wszystkich wierszy w tabeli, jest ustawiany w
searchable.xml
jako
.
android:searchSuggestIntentData
, jak opisano w
Sekcja Obsługa sugestii wyszukiwania.
Jeśli pierwsza część identyfikatora URI jest inna dla każdego wiersza w
, zmapuj tę wartość na pole SUGGEST_COLUMN_INTENT_DATA
.
Gdy użytkownik wybiera te treści, uruchamiająca się intencja dostarcza danych o zamiarach
kombinacja funkcji SUGGEST_COLUMN_INTENT_DATA_ID
i atrybut android:searchSuggestIntentData
lub
Wartość pola SUGGEST_COLUMN_INTENT_DATA
.
Podaj dane sugestii wyszukiwania
Wdróż dostawcę treści.
aby w oknie wyszukiwania w Androidzie TV zwracać sugestie wyszukiwanych haseł. System wysyła zapytania dotyczące treści
dostawcy, aby otrzymywać sugestie, wywołując metodę query()
za każdym razem
wpisana litera. W ramach implementacji: query()
dostawca przeszukuje dane sugestii i zwraca Cursor
wskazujący na
wiersze oznaczone na potrzeby sugestii.
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); } ...
W pliku manifestu dostawca treści jest traktowany w specjalny sposób. Zamiast być
jest otagowana jako aktywność, można to opisać jako
<provider>
dostawca zawiera atrybut android:authorities
, który informuje system o
przestrzeni nazw dostawcy treści. Dodatkowo musisz ustawić wartość atrybutu android:exported
na
"true"
, aby wyszukiwanie globalne Androida mogło używać zwróconych z niej wyników.
<provider android:name="com.example.android.tvleanback.VideoContentProvider" android:authorities="com.example.android.tvleanback" android:exported="true" />
Obsługa sugestii wyszukiwania
Aplikacja musi zawierać
res/xml/searchable.xml
, aby skonfigurować ustawienia sugestii wyszukiwania.
W pliku res/xml/searchable.xml
umieść
android:searchSuggestAuthority
, aby wskazać systemowi przestrzeń nazw
dostawcy treści. Musi ona być zgodna z wartością ciągu znaków określoną w atrybucie
android:authorities
atrybut zbioru danych <provider>
w pliku AndroidManifest.xml
.
Dodaj też etykietę, czyli nazwa aplikacji. Ustawienia wyszukiwania w systemie używają tej etykiety przy wyliczaniu które można przeszukiwać.
Plik searchable.xml
musi również zawierać
android:searchSuggestIntentAction
o wartości "android.intent.action.VIEW"
, aby zdefiniować działanie związane z przesyłaniem sugestii niestandardowej. To coś innego niż intencja
czynność umożliwiającą podanie wyszukiwanego hasła, jak opisano w następnej sekcji.
Inne sposoby deklarowania działania intencji dla sugestii:
Więcej informacji znajdziesz w sekcji Deklarowanie
działanie intencji.
Wraz z działaniem intencji aplikacja musi dostarczać dane intencji, które określasz w polu
android:searchSuggestIntentData
. Jest to pierwsza część identyfikatora URI wskazujący
do treści, która opisuje część identyfikatora URI wspólną dla wszystkich wierszy w tabeli mapowania dla danego
treści. Fragment identyfikatora URI, który jest unikalny dla każdego wiersza, jest określany w polu SUGGEST_COLUMN_INTENT_DATA_ID
,
zgodnie z opisem w sekcji Identyfikowanie kolumn.
Inne sposoby deklarowania danych intencji na potrzeby sugestii znajdziesz tutaj
Deklarowanie
dane o zamiarach.
Atrybut android:searchSuggestSelection=" ?"
określa przekazaną wartość
jako parametr selection
funkcji query()
. Wartość znaku zapytania (?
) jest zastępowana tekstem zapytania.
Na koniec musisz również dodać
android:includeInGlobalSearch
o wartości "true"
. Oto przykład:
Plik 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>
Obsługa wyszukiwanych słów
Gdy w oknie wyszukiwania pojawi się słowo pasujące do wartości w jednej z kolumn aplikacji,
opisane w sekcji Identyfikacja kolumn, system uruchamia tag
Intencja ACTION_SEARCH
.
Aktywność w aplikacji, która obsługuje te działania
intencja przeszukuje repozytorium w poszukiwaniu kolumn z danym słowem w wartościach i zwraca listę
elementów treści z tymi kolumnami. W pliku AndroidManifest.xml
wyznaczasz
aktywność, która obsługuje ACTION_SEARCH
Jak w tym przykładzie:
... <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" /> ...
Aktywność musi też opisywać konfigurację dostępną do wyszukiwania w odniesieniu do
searchable.xml
.
Aby skorzystać z okna wyszukiwania globalnego,
Plik manifestu musi zawierać informacje o aktywności, która ma otrzymywać zapytania. Plik manifestu musi też
opisz <provider>
dokładnie w sposób opisany w pliku searchable.xml
.
Precyzyjny link do aplikacji na ekranie szczegółów
Jeśli konfiguracja wyszukiwania została skonfigurowana zgodnie z opisem w sekcji Wyszukiwanie z nickami
sugestii i zmapowano SUGGEST_COLUMN_TEXT_1
,
SUGGEST_COLUMN_PRODUCTION_YEAR
i
SUGGEST_COLUMN_DURATION
pól zgodnie z opisem w
sekcji Określ kolumny,
precyzyjny link do działania związanego z oglądaniem treści pojawi się na ekranie z informacjami, który wyświetli się,
użytkownik wybiera wynik wyszukiwania:
Gdy użytkownik kliknie link do aplikacji, co będzie oznaczone przyciskiem **Dostępny w**
ekranu szczegółów, system uruchomi aktywność, która obsługuje obiekt ACTION_VIEW
ustaw jako
android:searchSuggestIntentAction
o wartości "android.intent.action.VIEW"
w
plik searchable.xml
.
Możesz też skonfigurować niestandardową intencję uruchomioną przez Twoją aktywność. Widać to w
Przykładowa aplikacja Leanback
, Zwróć uwagę, że przykładowa aplikacja uruchamia własną LeanbackDetailsFragment
, aby
wyświetlić szczegóły wybranych multimediów; w aplikacjach, uruchom aktywność, która odtwarza multimedia.
i zapisać użytkownikowi
kolejne kliknięcie.
Sposób wyszukiwania informacji
Na Androidzie TV wyszukiwanie jest dostępne na ekranie głównym i w aplikacji. Wyniki wyszukiwania są różne w tych dwóch przypadkach.
Wyszukuj z ekranu głównego
Gdy użytkownik przeprowadza wyszukiwanie na ekranie głównym, na karcie elementu wyświetla się pierwszy wynik. Jeśli które mogą odtwarzać treści, link do każdej z nich znajduje się u dołu karty:
Nie można automatycznie umieszczać aplikacji na karcie jednostki. Być uwzględniony w odtwarzania, wyniki wyszukiwania aplikacji muszą pasować do tytułu, roku i czasu trwania w wynikach wyszukiwania.
Pod kartą może być dostępnych więcej wyników wyszukiwania. Aby je wyświetlić, użytkownik musi nacisnąć i przewiń w dół. Wyniki dla poszczególnych aplikacji pojawią się w osobnym wierszu. Nie możesz kontrolować kolejność wierszy. Obsługiwane aplikacje zegarki są wymienione jako pierwsze.
Wyszukiwanie z poziomu aplikacji
Użytkownik może też rozpocząć wyszukiwanie z poziomu aplikacji, uruchamiając mikrofon za pomocą pilota lub kontrolera pada do gier. Wyniki wyszukiwania są wyświetlane w jednym wierszu nad treścią aplikacji. Aplikacja generuje wyniki wyszukiwania, korzystając z usług własnego globalnego dostawcy wyszukiwania.
Więcej informacji
Aby dowiedzieć się więcej o wyszukiwaniu aplikacji TV, przeczytaj artykuł zintegruj ze swoją aplikacją funkcje wyszukiwania na Androidzie oraz Dodaj funkcję wyszukiwania.
Więcej informacji o dostosowywaniu wyszukiwania w aplikacji za pomocą SearchFragment
znajdziesz w artykule
Wyszukiwanie w aplikacjach TV