One of the biggest difference between Android phone & Android TV is their input method. Since TV does not support touchpad, and we shouldn’t expect users to use keyboard for TV, inputting words for TV is troublesome.
Google suggests to use voice input for in Searching within TV Apps
BrowseFragment contains a design layout for search function as well, and showing in-app icon on your application is very easy. Just implement setOnSearchClickedListener, that’s all.
// Existence of this method make In-app search icon visible setOnSearchClickedListener(new View.OnClickListener() < @Override public void onClick(View view) < >>);
setSearchAffordanceColor method can be used for specifying search icon color.
// set search icon color setSearchAffordanceColor(getResources().getColor(R.color.search_opaque));
Search function need to be implemented to answer user’s search query. To show the search query input UI and search result UI, LeanbackLibrary provides SearchFragment. This time, when request search (by either voice search button or explicitly press search button icon), we invoke this SearchFragment from SearchActivity.
private void setupEventListeners() < setOnItemViewSelectedListener(new ItemViewSelectedListener()); setOnItemViewClickedListener(new ItemViewClickedListener()); // Existence of this method make In-app search icon visible setOnSearchClickedListener(new View.OnClickListener() < @Override public void onClick(View view) < Intent intent = new Intent(getActivity(), SearchActivity.class); startActivity(intent); >>); >
Start creating SearchActivity by right click on Package name, Create → New Activity → Blank Activity → type “SearchActivity”. After SearchActivity class and activity_search.xml layout have created, modify res/acitivity_search.xml as follows.
It attaches SearchFragment. Again, create SearchFragment by Create → New → Java class → SearchFragment, and make it a subclass of android.support.v17.leanback.app.SearchFragment.
If you build and run application here, application will crash because SearchFragment automatically start to get search query from internal speech recognizer.
Official doc says,
If you do not supply a callback via setSpeechRecognitionCallback(SpeechRecognitionCallback) , an internal speech recognizer will be used for which your application will need to request android.permission.RECORD_AUDIO.
So you need to do either
public class SearchFragment extends android.support.v17.leanback.app.SearchFragment < private static final String TAG = SearchFragment.class.getSimpleName(); private static final int REQUEST_SPEECH = 0x00000010; @Override public void onCreate(Bundle savedInstanceState) < super.onCreate(savedInstanceState); if (!Utils.hasPermission(getActivity(), Manifest.permission.RECORD_AUDIO)) < // SpeechRecognitionCallback is not required and if not provided recognition will be handled // using internal speech recognizer, in which case you must have RECORD_AUDIO permission setSpeechRecognitionCallback(new SpeechRecognitionCallback() < @Override public void recognizeSpeech() < Log.v(TAG, "recognizeSpeech"); try < startActivityForResult(getRecognizerIntent(), REQUEST_SPEECH); >catch (ActivityNotFoundException e) < Log.e(TAG, "Cannot find activity for speech recognizer", e); >> >); > >public static boolean hasPermission(final Context context, final String permission)
When user tries voice input search, onSearchRequested callback is executed and Google’s global contents search will be launched as default.
It is necessary to override this method if you want to activate in-app application search.
It is written in the description of startSearch method that
It is typically called from onSearchRequested(), either directly from Activity.onSearchRequested() or from an overridden version in any given Activity. If your goal is simply to activate search, it is preferred to call onSearchRequested(), which may have been overridden elsewhere in your Activity. If your goal is to inject specific data such as context data, it is preferred to override onSearchRequested(), so that any callers to it will benefit from the override.
We override onSearchRequested method for both MainActivity & SearchActivity.
@Override public boolean onSearchRequested()SearchResultProvider intereface is an intereface of Leanback library, used to listen search related event. We need to override 3 methods.
We need to register this SearchResultProvider by using setSearchResultProvider method, minimum implementation is like this,
public class SearchFragment extends android.support.v17.leanback.app.SearchFragment implements android.support.v17.leanback.app.SearchFragment.SearchResultProvider < private static final String TAG = SearchFragment.class.getSimpleName(); private static final int REQUEST_SPEECH = 0x00000010; private ArrayObjectAdapter mRowsAdapter; @Override public void onCreate(Bundle savedInstanceState) < super.onCreate(savedInstanceState); mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter()); setSearchResultProvider(this); . >. @Override public ObjectAdapter getResultsAdapter() < Log.d(TAG, "getResultsAdapter"); Log.d(TAG, mRowsAdapter.toString()); // It should return search result here, // but static Movie Item list will be returned here now for practice. ArrayListmItems = MovieProvider.getMovieItems(); ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(new CardPresenter()); listRowAdapter.addAll(0, mItems); HeaderItem header = new HeaderItem("Search results"); mRowsAdapter.add(new ListRow(header, listRowAdapter)); return mRowsAdapter; > @Override public boolean onQueryTextChange(String newQuery) < Log.i(TAG, String.format("Search Query Text Change %s", newQuery)); return true; >@Override public boolean onQueryTextSubmit(String query)SearchActivity can be launched in 2 ways,
*1 It is depending on the Android TV devices. For example, SONY BRAVIA provides touchpad remote controller and voice search can be done from this remote controller.
SearchFragment shows the mock search results now.
Source code is on github.
[Update on 1. 8. 2016]: I’m sorry that it seems the implementation is insufficient. We should implement OnItemViewClickedListener to define the action of clicking these search results.This OnItemViewClickedListener can be set it by
setOnItemViewClickedListener(new ItemViewClickedListener());
(in this case) in onCreate of BrowseFragment . Please refer Kim Johnsson’s helpful comment below.
Google’s AOSP implementation is explained in the next chapter.