Android TV menggunakan antarmuka penelusuran Android untuk mengambil data konten dari aplikasi yang terinstal dan menampilkan hasil penelusuran kepada pengguna. Data konten aplikasi Anda dapat disertakan dengan hasil ini untuk memberi pengguna akses instan ke konten di aplikasi Anda.
Aplikasi Anda harus menyediakan kolom data yang dapat digunakan Android TV untuk menghasilkan hasil penelusuran
yang disarankan saat pengguna memasukkan karakter dalam dialog penelusuran. Untuk melakukannya, aplikasi Anda harus mengimplementasikan
Penyedia Konten yang menampilkan
saran bersama dengan file konfigurasi
searchable.xml
yang mendeskripsikan penyedia
konten dan informasi penting lainnya untuk Android TV. Anda juga memerlukan aktivitas yang menangani
intent yang diaktifkan saat pengguna memilih hasil penelusuran yang disarankan. Untuk detail selengkapnya, lihat Menambahkan saran penelusuran kustom. Panduan ini membahas poin-poin utama khusus untuk aplikasi Android TV.
Sebelum membaca panduan ini, pastikan Anda sudah memahami konsep yang dijelaskan dalam panduan Search API. Selain itu, tinjau Menambahkan fungsi penelusuran.
Kode contoh dalam panduan ini berasal dari aplikasi contoh Leanback .
Mengidentifikasi kolom
SearchManager
mendeskripsikan kolom data yang diharapkan dengan mewakilinya sebagai kolom database lokal. Terlepas dari format data, Anda harus memetakan kolom data ke
kolom tersebut, biasanya di class yang mengakses data konten Anda. Untuk informasi tentang cara mem-build class yang memetakan data yang ada ke kolom wajib diisi, lihat
Membuat tabel saran.
Class SearchManager
menyertakan beberapa kolom untuk Android TV. Beberapa
kolom yang lebih penting dijelaskan dalam tabel berikut.
Nilai | Deskripsi |
---|---|
SUGGEST_COLUMN_TEXT_1 |
Nama konten Anda (wajib) |
SUGGEST_COLUMN_TEXT_2 |
Deskripsi teks dari konten Anda |
SUGGEST_COLUMN_RESULT_CARD_IMAGE |
Gambar, poster, atau sampul untuk konten Anda |
SUGGEST_COLUMN_CONTENT_TYPE |
Jenis MIME media Anda |
SUGGEST_COLUMN_VIDEO_WIDTH |
Lebar resolusi media Anda |
SUGGEST_COLUMN_VIDEO_HEIGHT |
Tinggi resolusi media Anda |
SUGGEST_COLUMN_PRODUCTION_YEAR |
Tahun produksi konten Anda (wajib) |
SUGGEST_COLUMN_DURATION |
Durasi media Anda dalam milidetik (wajib) |
Framework penelusuran membutuhkan kolom berikut:
Ketika nilai kolom ini untuk konten Anda cocok dengan nilai untuk konten yang sama dari penyedia lain yang ditemukan oleh server Google, sistem akan menyediakan deep link ke aplikasi Anda dalam tampilan detail untuk konten tersebut, bersama dengan link ke aplikasi dari penyedia lain. Hal ini dibahas lebih lanjut di bagian Deep link ke aplikasi Anda di layar detail.
Class database aplikasi Anda mungkin menentukan kolom sebagai berikut:
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; ...
Saat membuat peta dari kolom SearchManager
ke kolom data, Anda juga harus menentukan _ID
untuk memberikan ID unik ke setiap baris.
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; } ...
Di contoh sebelumnya, perhatikan pemetaan ke kolom SUGGEST_COLUMN_INTENT_DATA_ID
. Ini adalah bagian dari URI yang mengarah ke konten unik untuk data di
baris ini—bagian terakhir dari URI, yang menjelaskan tempat konten disimpan. Bagian pertama URI,
jika bersifat umum untuk semua baris dalam tabel, ditetapkan dalam
file searchable.xml
sebagai
atribut
android:searchSuggestIntentData
, seperti yang dijelaskan di bagian
Menangani saran penelusuran.
Jika bagian pertama URI berbeda untuk setiap baris dalam
tabel, petakan nilai tersebut dengan kolom SUGGEST_COLUMN_INTENT_DATA
.
Saat pengguna memilih konten ini, intent yang diaktifkan akan memberikan data intent dari
kombinasi SUGGEST_COLUMN_INTENT_DATA_ID
dan atribut android:searchSuggestIntentData
atau nilai kolom
SUGGEST_COLUMN_INTENT_DATA
.
Memberikan data saran penelusuran
Implementasikan Penyedia Konten
untuk menampilkan saran istilah penelusuran ke dialog penelusuran Android TV. Sistem mengkueri penyedia konten
Anda untuk saran dengan memanggil metode query()
setiap kali
huruf diketik. Dalam implementasi query()
, penyedia konten
akan menelusuri data saran dan menampilkan Cursor
yang mengarah ke
baris yang Anda tetapkan untuk saran.
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); } ...
Dalam file manifes Anda, penyedia konten akan menerima perlakuan khusus. Bukannya
diberi tag sebagai aktivitas, aktivitas tersebut dideskripsikan sebagai
<provider>
. Penyedia
menyertakan atribut android:authorities
untuk memberi tahu sistem
tentang namespace penyedia konten Anda. Selain itu, Anda harus menetapkan atribut android:exported
ke
"true"
agar penelusuran global Android dapat menggunakan hasil yang ditampilkan darinya.
<provider android:name="com.example.android.tvleanback.VideoContentProvider" android:authorities="com.example.android.tvleanback" android:exported="true" />
Menangani saran penelusuran
Aplikasi Anda harus menyertakan file
res/xml/searchable.xml
untuk mengonfigurasi setelan saran penelusuran.
Dalam file res/xml/searchable.xml
, sertakan
atribut
android:searchSuggestAuthority
untuk memberi tahu sistem tentang namespace
penyedia konten Anda. Ini harus cocok dengan nilai string yang Anda tentukan dalam atribut
android:authorities
dari elemen <provider>
di file AndroidManifest.xml
Anda.
Sertakan juga label, yang merupakan nama aplikasi. Setelan penelusuran sistem menggunakan label ini saat menghitung aplikasi yang dapat ditelusuri.
File searchable.xml
juga harus menyertakan
android:searchSuggestIntentAction
dengan nilai "android.intent.action.VIEW"
untuk menentukan tindakan intent guna memberikan saran kustom. Hal ini berbeda dengan tindakan intent
untuk memberikan istilah penelusuran, seperti yang dijelaskan di bagian berikut.
Guna mengetahui cara lain untuk mendeklarasikan tindakan intent untuk saran,
lihat Mendeklarasikan
tindakan intent.
Bersama dengan tindakan intent, aplikasi Anda harus menyediakan data intent, yang Anda tentukan dengan
atribut
android:searchSuggestIntentData
. Ini adalah bagian pertama URI yang mengarah
ke konten, yang menjelaskan bagian URI yang umum bagi semua baris dalam tabel pemetaan untuk
konten tersebut. Bagian URI yang unik untuk setiap baris ditetapkan dengan kolom SUGGEST_COLUMN_INTENT_DATA_ID
,
seperti yang dijelaskan di bagian Mengidentifikasi kolom.
Untuk mengetahui cara lain mendeklarasikan data intent untuk saran, lihat
Mendeklarasikan
data intent.
Atribut android:searchSuggestSelection=" ?"
menentukan nilai yang diteruskan
sebagai parameter selection
dari
metode query()
. Nilai tanda tanya (?
) diganti dengan teks kueri.
Terakhir, Anda juga harus menyertakan atribut
android:includeInGlobalSearch
dengan nilai "true"
. Berikut adalah contoh
file 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>
Menangani istilah penelusuran
Segera setelah dialog penelusuran memiliki kata yang cocok dengan nilai di salah satu kolom aplikasi Anda, seperti
yang dijelaskan di bagian Identifikasi kolom, sistem akan mengaktifkan
intent ACTION_SEARCH
.
Aktivitas di aplikasi Anda yang menangani
intent tersebut menelusuri repositori untuk kolom dengan kata yang diberikan dalam nilainya dan menampilkan daftar
item konten dengan kolom tersebut. Dalam file AndroidManifest.xml
, Anda menetapkan
aktivitas yang menangani intent ACTION_SEARCH
seperti yang ditunjukkan dalam contoh berikut:
... <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" /> ...
Aktivitas tersebut juga harus mendeskripsikan konfigurasi yang dapat ditelusuri dengan referensi ke file
searchable.xml
.
Untuk menggunakan dialog penelusuran global,
manifes harus menjelaskan aktivitas mana yang harus menerima kueri penelusuran. Manifes juga harus
menjelaskan elemen <provider>
, persis seperti yang dijelaskan dalam file searchable.xml
.
Deep link ke aplikasi Anda di layar detail
Jika Anda telah menyiapkan konfigurasi penelusuran seperti yang dijelaskan di bagian Menangani saran
penelusuran dan memetakan kolom SUGGEST_COLUMN_TEXT_1
,
SUGGEST_COLUMN_PRODUCTION_YEAR
, dan
SUGGEST_COLUMN_DURATION
seperti yang dijelaskan di
bagian Identifikasi kolom,
deep link ke tindakan menonton untuk konten Anda akan muncul di layar detail yang diluncurkan saat
pengguna memilih hasil penelusuran:
Saat pengguna memilih link untuk aplikasi Anda, yang diidentifikasi dengan tombol **Available On** di
layar detail, sistem akan meluncurkan aktivitas yang menangani ACTION_VIEW
yang ditetapkan sebagai
android:searchSuggestIntentAction
dengan nilai "android.intent.action.VIEW"
di
file searchable.xml
.
Anda juga dapat menyiapkan intent kustom untuk meluncurkan aktivitas Anda. Hal ini ditunjukkan dalam
aplikasi contoh Leanback
. Perhatikan bahwa aplikasi contoh meluncurkan LeanbackDetailsFragment
-nya sendiri untuk
menampilkan detail media yang dipilih; di aplikasi Anda, luncurkan aktivitas yang langsung memutar
media untuk memudahkan pengguna.
Perilaku penelusuran
Penelusuran tersedia di Android TV dari layar utama dan dari dalam aplikasi Anda. Hasil penelusuran berbeda untuk kedua kasus ini.
Penelusuran dari layar utama
Saat pengguna melakukan penelusuran dari layar utama, hasil pertama akan muncul di kartu entity. Jika ada aplikasi yang dapat memutar konten tersebut, link ke setiap aplikasi akan muncul di bagian bawah kartu:
Anda tidak dapat menempatkan aplikasi secara terprogram ke dalam kartu entity. Agar disertakan sebagai opsi pemutaran, hasil penelusuran aplikasi harus cocok dengan judul, tahun, dan durasi konten yang ditelusuri.
Hasil penelusuran lainnya mungkin tersedia di bawah kartu. Untuk melihatnya, pengguna harus menekan remote dan men-scroll ke bawah. Hasil untuk setiap aplikasi muncul di baris terpisah. Anda tidak dapat mengontrol urutan baris. Aplikasi yang mendukung tindakan menonton akan dicantumkan terlebih dahulu.
Penelusuran dari aplikasi Anda
Pengguna juga dapat memulai penelusuran dari dalam aplikasi Anda dengan memulai mikrofon dari remote atau pengontrol game pad. Hasil penelusuran ditampilkan dalam satu baris di atas konten aplikasi. Aplikasi Anda menyusun hasil penelusuran menggunakan penyedia penelusuran global-nya sendiri.
Pelajari lebih lanjut
Untuk mempelajari penelusuran aplikasi TV lebih lanjut, baca Mengintegrasikan fitur penelusuran Android ke dalam aplikasi dan Menambahkan fungsi penelusuran.
Untuk mengetahui informasi selengkapnya tentang cara menyesuaikan pengalaman penelusuran dalam aplikasi dengan SearchFragment
, baca
Menelusuri dalam aplikasi TV.