Android TV, yüklü uygulamalardan içerik verileri almak ve arama sonuçlarını kullanıcıya sunmak için Android arama arayüzünü kullanır. Uygulamanızın içerik verileri bu sonuçlara dahil edilebilir ve kullanıcının uygulamanızdaki içeriğe anında erişmesini sağlayabilirsiniz.
Uygulamanız, kullanıcı arama iletişim kutusuna karakter girerken Android TV'nin önerilen arama sonuçlarını oluşturabileceği veri alanlarını Android TV'ye sağlamalıdır. Bunun için uygulamanızın, önerileri sunan bir İçerik Sağlayıcı'nın yanı sıra içerik sağlayıcıyı ve Android TV için diğer önemli bilgileri açıklayan bir
searchable.xml
yapılandırma dosyası uygulaması gerekir. Ayrıca, kullanıcı önerilen bir arama sonucunu seçtiğinde tetiklenen amacı işleyen bir etkinliğe ihtiyacınız vardır. Daha ayrıntılı bilgi için Özel arama önerileri ekleme bölümüne bakın. Bu kılavuz, Android TV uygulamalarıyla ilgili ana noktaları kapsar.
Bu kılavuzu okumadan önce, Search API kılavuzunda açıklanan kavramları bildiğinizden emin olun. Ayrıca, Arama işlevi ekleme konusunu inceleyin.
Bu kılavuzdaki örnek kod, Leanback örnek uygulamasından alınmıştır.
Sütunları tanımlama
SearchManager
, beklediği veri alanlarını yerel bir veritabanının sütunları olarak temsil ederek tanımlar. Verilerinizin biçiminden bağımsız olarak, veri alanlarınızı genellikle içerik verilerinize erişen sınıfta yer alan bu sütunlarla eşlemeniz gerekir. Mevcut verilerinizi gerekli alanlarla eşleyen bir sınıf oluşturma hakkında bilgi edinmek için
Öneri tablosu oluşturma bölümüne bakın.
SearchManager
sınıfı, Android TV için birkaç sütun içerir. Daha önemli olan sütunlardan bazıları aşağıdaki tabloda açıklanmıştır.
Değer | Açıklama |
---|---|
SUGGEST_COLUMN_TEXT_1 |
İçeriğinizin adı (zorunlu) |
SUGGEST_COLUMN_TEXT_2 |
İçeriğinizin metin açıklaması |
SUGGEST_COLUMN_RESULT_CARD_IMAGE |
İçeriğiniz için resim, poster veya kapak |
SUGGEST_COLUMN_CONTENT_TYPE |
Medyanızın MIME türü |
SUGGEST_COLUMN_VIDEO_WIDTH |
Medyanızın çözünürlük genişliği |
SUGGEST_COLUMN_VIDEO_HEIGHT |
Medyanızın çözünürlük yüksekliği |
SUGGEST_COLUMN_PRODUCTION_YEAR |
İçeriğinizin yapım yılı (zorunlu) |
SUGGEST_COLUMN_DURATION |
Medyanızın milisaniye cinsinden süresi (gerekli) |
Arama çerçevesi için aşağıdaki sütunlar gerekir:
İçeriğinize ilişkin bu sütunların değerleri Google sunucuları tarafından bulunan diğer sağlayıcılardan alınan aynı içeriğin değerleriyle eşleştiğinde, sistem içerik için ayrıntılar görünümünde uygulamanıza yönelik bir derin bağlantı ve diğer sağlayıcıların uygulamalarının bağlantılarını sağlar. Bu konu, Ayrıntılar ekranında uygulamanızın derin bağlantısı bölümünde daha ayrıntılı bir şekilde ele alınmaktadır.
Uygulamanızın veritabanı sınıfı, sütunları şu şekilde tanımlayabilir:
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; ...
SearchManager
sütunlarından veri alanlarınızla harita oluştururken her satıra benzersiz bir kimlik vermek için _ID
değerini de belirtmeniz gerekir.
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; } ...
Önceki örnekte, SUGGEST_COLUMN_INTENT_DATA_ID
alanıyla eşlemeye dikkat edin. Bu, URI'nın bu satırdaki verilere özgü içeriğe işaret eden kısmıdır. URI'nın, içeriğin nerede depolandığını açıklayan son kısmıdır. URI'nın ilk bölümü (tablodaki tüm satırlarda ortak olduğunda), Arama önerilerini işleme bölümünde açıklandığı gibi searchable.xml
dosyasında
android:searchSuggestIntentData
özelliği olarak ayarlanır.
URI'nın ilk kısmı tablodaki her satır için farklıysa bu değeri SUGGEST_COLUMN_INTENT_DATA
alanıyla eşleyin.
Kullanıcı bu içeriği seçtiğinde tetiklenen amaç, SUGGEST_COLUMN_INTENT_DATA_ID
ve android:searchSuggestIntentData
özelliği ya da SUGGEST_COLUMN_INTENT_DATA
alanı değerinin kombinasyonundan elde edilen amaç verilerini sağlar.
Arama önerisi verilerini sağlayın
Android TV arama iletişim kutusuna arama terimi önerileri döndürmek için bir İçerik Sağlayıcı uygulayın. Sistem, her harf yazıldığında query()
yöntemini çağırarak öneri için içerik sağlayıcınızı sorgular. query()
uygulamanızda, içerik sağlayıcınız öneri verilerinizi arar ve öneriler için belirttiğiniz satırlara işaret eden bir Cursor
döndürür.
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); } ...
İçerik sağlayıcı, manifest dosyanızda özel olarak muamele görür. Etkinlik olarak etiketlenmek yerine <provider>
olarak tanımlanır. Sağlayıcı, sisteme içerik sağlayıcınızın ad alanını bildirmek için android:authorities
özelliğini içerir. Ayrıca, Android genel aramasının bu aramadan döndürülen sonuçları kullanabilmesi için android:exported
özelliğini "true"
olarak ayarlamanız gerekir.
<provider android:name="com.example.android.tvleanback.VideoContentProvider" android:authorities="com.example.android.tvleanback" android:exported="true" />
Arama önerilerini işleme
Arama önerileri ayarlarını yapılandırmak için uygulamanızın bir
res/xml/searchable.xml
dosyası içermesi gerekir.
Sisteme içerik sağlayıcınızın ad alanını bildirmek için res/xml/searchable.xml
dosyasına
android:searchSuggestAuthority
özelliğini ekleyin. Bu değer, AndroidManifest.xml
dosyanızdaki <provider>
öğesinin android:authorities
özelliğinde belirttiğiniz dize değeriyle eşleşmelidir.
Ayrıca, uygulamanın adı olan bir etiket de ekleyin. Sistem arama ayarları, aranabilir uygulamaları numaralandırırken bu etiketi kullanır.
searchable.xml
dosyası, özel bir öneri sağlama intent işlemini tanımlamak için "android.intent.action.VIEW"
değerine sahip
android:searchSuggestIntentAction
öğesini de içermelidir. Bu, aşağıdaki bölümde açıklandığı gibi, bir arama terimi sağlamaya yönelik amaç işleminden farklıdır.
Öneriler için amaç işlemini bildirmenin diğer yolları için Amaç işlemini bildirme bölümüne bakın.
Uygulamanız, intent işlemiyle birlikte
android:searchSuggestIntentData
özelliğiyle belirttiğiniz amaç verilerini sağlamalıdır. Bu, URI'nın içeriği işaret eden ilk bölümüdür. Bu kısım, URI'nın söz konusu içeriğe ait eşleme tablosundaki tüm satırlarda ortak olan kısmını açıklar. URI'nın her satır için benzersiz olan kısmı, Sütunları tanımlama bölümünde açıklandığı gibi SUGGEST_COLUMN_INTENT_DATA_ID
alanı ile oluşturulur.
Öneriler için amaç verilerini bildirmenin diğer yollarını öğrenmek üzere Amaç verilerini bildirme bölümüne bakın.
android:searchSuggestSelection=" ?"
özelliği, query()
yönteminin selection
parametresi olarak iletilen değeri belirtir. Soru işareti (?
) değeri sorgu metniyle değiştirilir.
Son olarak, "true"
değeriyle
android:includeInGlobalSearch
özelliğini de eklemeniz gerekir. Örnek bir searchable.xml
dosyası:
<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>
Arama terimlerini işleme
Arama iletişim kutusunda Sütunları tanımlama bölümünde açıklandığı gibi, uygulamanızın sütunlarından birindeki değerle eşleşen bir kelime olur olmaz sistem ACTION_SEARCH
amacını tetikler.
Uygulamanızda bu amacı işleyen etkinlik, depoda belirtilen kelimeyi değerlerinde içeren sütunları arar ve bu sütunlara sahip içerik öğelerinin listesini döndürür. AndroidManifest.xml
dosyanızda, aşağıdaki örnekte gösterildiği gibi ACTION_SEARCH
amacını işleyen etkinliği belirtirsiniz:
... <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" /> ...
Etkinlik, searchable.xml
dosyasına referans vererek aranabilir yapılandırmayı da tanımlamalıdır.
Genel arama iletişim kutusunu kullanmak için manifest, hangi etkinliğin arama sorgularını alması gerektiğini açıklamalıdır. Manifest, <provider>
öğesini de tam olarak searchable.xml
dosyasında açıklandığı gibi açıklamalıdır.
Ayrıntılar ekranında uygulamanıza derin bağlantı oluşturun
Arama yapılandırmasını Arama önerilerini işleme bölümünde açıklandığı şekilde ayarladıysanız ve SUGGEST_COLUMN_TEXT_1
, SUGGEST_COLUMN_PRODUCTION_YEAR
ve SUGGEST_COLUMN_DURATION
alanlarını Sütun tanımlama bölümünde açıklandığı gibi eşlediyseniz ayrıntılar ekranında, kullanıcı bir arama sonucunu seçtiğinde açılan ayrıntılar ekranında içeriğinizle ilgili bir izleme işleminin
derin bağlantısı görünür:
![Ayrıntılar ekranında derin bağlantı](https://developer.android.com/static/images/tv/deep-link.png?authuser=5&hl=tr)
Kullanıcı, ayrıntılar ekranındaki **Kullanılabilir:** düğmesiyle tanımlanan bağlantıyı seçtiğinde sistem, searchable.xml
dosyasında "android.intent.action.VIEW"
değerine sahip
android:searchSuggestIntentAction
olarak ayarlanmış ACTION_VIEW
işlemini gerçekleştiren etkinliği başlatır.
Etkinliğinizi başlatmak için özel bir amaç da oluşturabilirsiniz. Bu,
Leanback örnek uygulamasında
gösterilmiştir. Örnek uygulamanın, seçilen medyanın ayrıntılarını göstermek için kendi LeanbackDetailsFragment
kampanyasını başlattığını unutmayın. Uygulamalarınızda, kullanıcının bir veya iki tıklama daha kazanabilmesi için medyayı hemen oynatan etkinliği başlatın.
Arama davranışı
Arama, Android TV'de ana ekrandan ve uygulamanızın içinden kullanılabilir. Arama sonuçları bu iki durum için farklıdır.
Ana ekrandan arama yapın
Kullanıcı ana ekrandan arama yaptığında ilk sonuç bir varlık kartında görüntülenir. İçeriği oynatabilecek uygulamalar varsa kartın alt kısmında her birinin bağlantısı görünür:
![TV Arama Sonucunu Oynatma](https://developer.android.com/static/images/tv/tv-search-playback.png?authuser=5&hl=tr)
Bir uygulamayı varlık kartına programlı bir şekilde yerleştiremezsiniz. Oynatma seçeneğine dahil edilebilmesi için bir uygulamanın arama sonuçlarının, aranan içeriğin başlığı, yılı ve süresi ile eşleşmesi gerekir.
Kartın altında daha fazla arama sonucu gösterilebilir. Bunları görmek için kullanıcının uzaktan kumandaya basması ve aşağı kaydırması gerekir. Her uygulamaya ait sonuçlar ayrı bir satırda görünür. Satır sırasını kontrol edemezsiniz. İzleme işlemlerini destekleyen uygulamalar listenin başındadır.
![TV Arama Sonuçları](https://developer.android.com/static/images/tv/tv-search-results.gif?authuser=5&hl=tr)
Uygulamanızdan arama yapın
Kullanıcı ayrıca uzaktan kumandadan veya oyun kumandası kumandasından mikrofonu başlatarak uygulamanızın içinden arama başlatabilir. Arama sonuçları, uygulama içeriğinin üstünde tek bir satırda görüntülenir. Uygulamanız, arama sonuçlarını kendi genel arama sağlayıcısını kullanarak oluşturur.
![TV Uygulama İçi Arama Sonuçları](https://developer.android.com/static/images/tv/tv-search-in-app.png?authuser=5&hl=tr)
Daha fazla bilgi
TV uygulamasında arama yapma hakkında daha fazla bilgi edinmek için Android arama özelliklerini uygulamanıza entegre etme ve Arama işlevi ekleme konularını okuyun.
SearchFragment
ile uygulama içi arama deneyimini özelleştirme hakkında daha fazla bilgi için TV uygulamalarında arama yapma bölümünü okuyun.