> Making TV Apps Searchable
Android TV uses the Android search interface to retrieve content data from installed apps and deliver search results to the user. Your app's content data can be included with these results, to give the user instant access to the content in your app.
Your app must provide Android TV with the data fields from which it generates suggested search results as the user enters characters in the search dialog. To do that, your app must implement a Content Provider that serves up the suggestions along with a searchable.xml
configuration file that describes the content provider and other vital information for Android TV. You also need an activity that handles the intent that fires when the user selects a suggested search result. All of this is described in more detail in Adding Custom Suggestions.
The SearchManager
describes the data fields it expects by representing them as columns of an SQLite database. Regardless of your data's format, you must map your data fields to these columns, usually in the class that accessess your content data.
>The search framework requires the following columns:
SUGGEST_COLUMN_TEXT_1
SUGGEST_COLUMN_CONTENT_TYPE
SUGGEST_COLUMN_PRODUCTION_YEAR
SUGGEST_COLUMN_DURATION
res/xml/searchable.xml
file to configure the search suggestions settings. It inlcudes the
android:searchSuggestAuthority
attribute to tell the system the namespace of your content provider. This must match the string value you specify in the
android:authorities
attribute of the
<provider>
element in your
AndroidManifest.xml
file.
Along with the intent action, your app must provide the intent data, which you specify with theandroid:searchSuggestIntentData
attribute.
Also, note the android:searchSuggestSelection=" ?"
attribute which specifies the value passed as theselection
parameter of the query()
method where the question mark (?
) value is replaced with the query text.
Finally, you must also include the android:includeInGlobalSearch
attribute with the value "true"
. Here is an example searchable.xml
file:
<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>
When the user selects the link for your app, identified by the "Available On" button in the details screen, the system launches the activity which handles the ACTION_VIEW
(set as android:searchSuggestIntentAction
with the value "android.intent.action.VIEW"
in the searchable.xml
file).
You can also set up a custom intent to launch your activity, and this is demonstrated in the Android Leanback sample app. Note that the sample app launches its own LeanbackDetailsFragment
to show the details for the selected media, but you should launch the activity that plays the media immediately to save the user another click or two.
>Searching within TV Apps
> Users frequently have specific content in mind when using a media app on TV. If your app contains a large catalog of content, browsing for a specific title may not be the most efficient way for users to find what they are looking for. A search interface can help your users get to the content they want faster than browsing. When you use the BrowseFragment
class for a media browsing interface, you can enable a search interface as a standard part of the user interface. The search interface is an icon that appears in the layout when you setView.OnClickListener
on the BrowseFragment
object. The following sample code demonstrates this technique.
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.browse_activity); mBrowseFragment = (BrowseFragment) getFragmentManager().findFragmentById(R.id.browse_fragment); ... mBrowseFragment.setOnSearchClickedListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent = new Intent(BrowseActivity.this, SearchActivity.class); startActivity(intent); } }); mBrowseFragment.setAdapter(buildAdapter()); }> When a user selects the search icon, the system invokes a search activity via the defined intent. Your search activity should use a linear layout containing a
SearchFragment
. This fragment must also implement the
SearchFragment.SearchResultProvider
interface in order to display the results of a search.
public class MySearchFragment extends SearchFragment implements SearchFragment.SearchResultProvider { private static final int SEARCH_DELAY_MS = 300; private ArrayObjectAdapter mRowsAdapter; private Handler mHandler = new Handler(); private SearchRunnable mDelayedLoad; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter()); setSearchResultProvider(this); setOnItemClickedListener(getDefaultItemClickedListener()); mDelayedLoad = new SearchRunnable(); } @Override public ObjectAdapter getResultsAdapter() { return mRowsAdapter; } @Override public boolean onQueryTextChange(String newQuery) { mRowsAdapter.clear(); if (!TextUtils.isEmpty(newQuery)) { mDelayedLoad.setSearchQuery(newQuery); mHandler.removeCallbacks(mDelayedLoad); mHandler.postDelayed(mDelayedLoad, SEARCH_DELAY_MS); } return true; } @Override public boolean onQueryTextSubmit(String query) { mRowsAdapter.clear(); if (!TextUtils.isEmpty(query)) { mDelayedLoad.setSearchQuery(query); mHandler.removeCallbacks(mDelayedLoad); mHandler.postDelayed(mDelayedLoad, SEARCH_DELAY_MS); } return true; } }The example code shown above is meant to be used with a separate
SearchRunnable
class that runs the search query on a separate thread. This technique keeps potentially slow-running queries from blocking the main user interface thread.