Android TV wykorzystuje interfejs wyszukiwania Androida do pobierania danych o treściach z zainstalowanych aplikacji i dostarczania użytkownikowi wyników wyszukiwania. W tych wynikach możesz uwzględniać dane o treści aplikacji, aby zapewnić użytkownikom natychmiastowy dostęp do treści w Twojej aplikacji.
Aplikacja musi udostępniać na Androidzie TV pola danych, na podstawie których Android TV może generować sugerowane wyniki wyszukiwania, gdy użytkownik wpisuje znaki w oknie wyszukiwania. W tym celu musisz wdrożyć w aplikacji dostawcę treści, który będzie wyświetlał sugestie wraz z plikiem konfiguracyjnym
searchable.xml
opisującym dostawcę treści i inne ważne informacje dotyczące Androida TV. Potrzebujesz też działania obsługującego zamiar wywoływany, gdy użytkownik wybierze sugerowany wynik wyszukiwania. Więcej informacji znajdziesz w artykule Dodawanie niestandardowych sugestii 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 wyjaśnione w przewodniku po interfejsie Search API. Zapoznaj się też z artykułem Dodawanie funkcji wyszukiwania.
Przykładowy kod w tym przewodniku pochodzi z przykładowej aplikacji Leanback.
Określ kolumny
SearchManager
opisuje oczekiwane pola danych, przedstawiając je jako kolumny lokalnej bazy danych. Niezależnie od formatu danych musisz zmapować pola danych na te kolumny, zwykle w klasie, która ma dostęp do danych o treści. Informacje o tworzeniu klasy, która mapuje istniejące dane na pola wymagane, znajdziesz w artykule o
tworzeniu tabeli sugestii.
Klasa SearchManager
zawiera kilka kolumn dotyczących Androida TV. Niektóre z najważniejszych kolumn zostały opisane w tej 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 Twoich treści odpowiadają wartościom dotyczącym tych samych treści pochodzących od innych dostawców znalezionych przez serwery Google, system poda w widoku szczegółów precyzyjny link do Twojej aplikacji wraz z linkami do aplikacji innych dostawców. Więcej informacji znajdziesz 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; ...
Podczas tworzenia mapy na podstawie kolumn SearchManager
na pola danych musisz też określić _ID
, aby nadać każdemu wierszowi unikalny identyfikator.
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; } ...
W poprzednim przykładzie zwróć uwagę na mapowanie pola SUGGEST_COLUMN_INTENT_DATA_ID
. Jest to część identyfikatora URI wskazująca treść unikalną dla danych w danym wierszu – ostatnia część identyfikatora URI, która określa miejsce przechowywania treści. Pierwsza część identyfikatora URI, jeśli jest wspólna dla wszystkich wierszy w tabeli, jest ustawiana w pliku searchable.xml
jako atrybut
android:searchSuggestIntentData
, jak opisano w sekcji Obsługa sugestii wyszukiwania.
Jeśli pierwsza część identyfikatora URI jest inna w każdym wierszu tabeli, zmapuj tę wartość na pole SUGGEST_COLUMN_INTENT_DATA
.
Gdy użytkownik wybiera te treści, uruchamiająca się intencja dostarcza danych o zamiarach pochodzących z kombinacji atrybutu SUGGEST_COLUMN_INTENT_DATA_ID
i atrybutu android:searchSuggestIntentData
lub wartości pola SUGGEST_COLUMN_INTENT_DATA
.
Podaj dane sugestii wyszukiwania
Zaimplementuj dostawcę treści, aby zwracać sugestie wyszukiwanych haseł w oknie wyszukiwania na Androidzie TV. System wysyła zapytania do dostawcy treści w poszukiwaniu sugestii, wywołując metodę query()
przy każdym pisaniu litery. W Twojej implementacji funkcji query()
dostawca treści przeszukuje dane sugestii i zwraca element Cursor
wskazujący wiersze, które wskażesz 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. Opis nie jest oznaczany jako aktywność, ale opisany jako <provider>
. Dostawca zawiera atrybut android:authorities
, który informuje system o przestrzeni nazw dostawcy treści. Dodatkowo musisz ustawić atrybut android:exported
na "true"
, aby wyszukiwanie globalne Androida mogło używać zwróconych wyników.
<provider android:name="com.example.android.tvleanback.VideoContentProvider" android:authorities="com.example.android.tvleanback" android:exported="true" />
Obsługa sugestii wyszukiwania
Aby można było skonfigurować ustawienia sugestii wyszukiwania, aplikacja musi zawierać plik
res/xml/searchable.xml
.
W pliku res/xml/searchable.xml
umieść atrybut
android:searchSuggestAuthority
, aby wskazać systemowi przestrzeń nazw dostawcy treści. Musi on być zgodny z wartością ciągu znaków określoną w atrybucie android:authorities
elementu <provider>
w pliku AndroidManifest.xml
.
Dołącz też etykietę, która jest nazwą aplikacji. Ustawienia wyszukiwania w systemie używają tej etykiety przy wyliczaniu aplikacji dostępnych do przeszukiwania.
Plik searchable.xml
musi też zawierać obiekt
android:searchSuggestIntentAction
z wartością "android.intent.action.VIEW"
, aby zdefiniować działanie intencji przekazywania niestandardowej sugestii. Różni się to od intencji polegającej na przesłaniu wyszukiwanego hasła, co zostało opisane w następnej sekcji.
Inne sposoby deklarowania działania intencji na potrzeby sugestii znajdziesz w artykule Deklarowanie działania intencji.
Wraz z działaniem intencji aplikacja musi dostarczać dane intencji, które określasz za pomocą atrybutu
android:searchSuggestIntentData
. Jest to pierwsza część identyfikatora URI wskazująca treść. Opisuje część identyfikatora URI wspólną dla wszystkich wierszy w tabeli mapowania dotyczącej danej treści. Unikalna część identyfikatora URI, która jest unikalna dla każdego wiersza, jest tworzona w polu SUGGEST_COLUMN_INTENT_DATA_ID
zgodnie z opisem w sekcji Identyfikowanie kolumn.
Inne sposoby deklarowania danych intencji na potrzeby sugestii znajdziesz w sekcji Deklarowanie danych intencji.
Atrybut android:searchSuggestSelection=" ?"
określa wartość przekazywaną jako parametr selection
metody query()
. Wartość znaku zapytania (?
) jest zastępowana tekstem zapytania.
Na koniec musisz też dodać atrybut
android:includeInGlobalSearch
o wartości "true"
. Oto przykładowy 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, które odpowiada wartości w jednej z kolumn aplikacji (zgodnie z opisem w sekcji Identyfikowanie kolumn), system uruchomi intencję ACTION_SEARCH
.
Działanie w aplikacji, które obsługuje tę intencję, wyszukuje w repozytorium kolumny z danym słowem w wartościach i zwraca listę elementów treści z tymi kolumnami. W pliku AndroidManifest.xml
określasz działanie, które obsługuje intencję 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 pliku searchable.xml
.
Aby skorzystać z okna wyszukiwania globalnego, plik manifestu musi zawierać informacje o aktywności, która ma otrzymywać wyszukiwane hasła. Plik manifestu musi też opisywać element <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 Obsługa sugestii dotyczących wyszukiwania oraz zmapowane pola SUGGEST_COLUMN_TEXT_1
, SUGGEST_COLUMN_PRODUCTION_YEAR
i SUGGEST_COLUMN_DURATION
zgodnie z opisem w sekcji Identyfikowanie kolumn,
precyzyjny link do działania dotyczącego oglądania Twoich treści pojawi się na ekranie szczegółów, który wyświetli się, gdy użytkownik wybierze wynik wyszukiwania:
![Precyzyjny link na ekranie szczegółów](https://developer.android.google.cn/static/images/tv/deep-link.png?authuser=2&hl=pl)
Gdy użytkownik kliknie link do aplikacji oznaczony przyciskiem **Dostępny w** na ekranie szczegółów, system uruchomi aktywność, która obsługuje obiekt ACTION_VIEW
ustawiony jako
android:searchSuggestIntentAction
z wartością "android.intent.action.VIEW"
w pliku searchable.xml
.
Możesz też skonfigurować niestandardową intencję uruchomioną przez Twoją aktywność. Zostało to pokazane w
przykładowej aplikacji Leanback. Pamiętaj, że przykładowa aplikacja uruchamia własną właściwość LeanbackDetailsFragment
, która wyświetla szczegóły wybranych multimediów. W aplikacji uruchom działanie, które spowoduje natychmiastowe odtworzenie multimediów, aby zapisać użytkownika jeszcze raz lub dwa kliknięcia.
Sposób wyszukiwania informacji
W Androidzie TV wyszukiwanie jest dostępne na ekranie głównym oraz w aplikacji. W tych 2 przypadkach wyniki wyszukiwania różnią się.
Wyszukuj z ekranu głównego
Gdy użytkownik przeprowadza wyszukiwanie na ekranie głównym, na karcie elementu wyświetla się pierwszy wynik. Jeśli masz aplikacje, które mogą odtwarzać treści, u dołu karty znajdziesz link do każdej z nich:
![Odtwarzanie wyników wyszukiwania programów telewizyjnych](https://developer.android.google.cn/static/images/tv/tv-search-playback.png?authuser=2&hl=pl)
Nie można automatycznie umieszczać aplikacji na karcie jednostki. Aby aplikacja mogła pojawić się jako opcja odtwarzania, wyniki wyszukiwania muszą pasować do tytułu, roku i czasu trwania szukanych treści.
Pod kartą może być dostępnych więcej wyników wyszukiwania. Aby je zobaczyć, użytkownik musi nacisnąć przycisk na pilocie i przewinąć w dół. Wyniki dla poszczególnych aplikacji pojawią się w osobnym wierszu. Nie da się kontrolować kolejności wierszy. Aplikacje, które obsługują działania zegarka, są wymienione na początku.
![Wyniki wyszukiwania programów telewizyjnych](https://developer.android.google.cn/static/images/tv/tv-search-results.gif?authuser=2&hl=pl)
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.
![Wyniki wyszukiwania w aplikacji TV](https://developer.android.google.cn/static/images/tv/tv-search-in-app.png?authuser=2&hl=pl)
Więcej informacji
Więcej informacji o wyszukiwaniu w aplikacji na telewizor znajdziesz w artykułach Integrowanie funkcji wyszukiwania na Androidzie z aplikacją i Dodawanie funkcji wyszukiwania.
Więcej informacji o dostosowywaniu wyszukiwania w aplikacji za pomocą SearchFragment
znajdziesz w artykule Wyszukiwanie w aplikacjach na telewizory.