• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // CHECKSTYLE:OFF Generated code
2 /* This file is auto-generated from SearchSupportFragment.java.  DO NOT MODIFY. */
3 
4 /*
5  * Copyright (C) 2014 The Android Open Source Project
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
8  * in compliance with the License. You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software distributed under the License
13  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
14  * or implied. See the License for the specific language governing permissions and limitations under
15  * the License.
16  */
17 package androidx.leanback.app;
18 
19 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
20 
21 import android.Manifest;
22 import android.app.Fragment;
23 import android.content.Intent;
24 import android.graphics.drawable.Drawable;
25 import android.os.Bundle;
26 import android.os.Handler;
27 import android.speech.RecognizerIntent;
28 import android.speech.SpeechRecognizer;
29 import android.util.Log;
30 import android.view.LayoutInflater;
31 import android.view.View;
32 import android.view.ViewGroup;
33 import android.view.inputmethod.CompletionInfo;
34 import android.widget.FrameLayout;
35 
36 import androidx.leanback.R;
37 import androidx.leanback.widget.ObjectAdapter;
38 import androidx.leanback.widget.ObjectAdapter.DataObserver;
39 import androidx.leanback.widget.OnItemViewClickedListener;
40 import androidx.leanback.widget.OnItemViewSelectedListener;
41 import androidx.leanback.widget.Presenter.ViewHolder;
42 import androidx.leanback.widget.Row;
43 import androidx.leanback.widget.RowPresenter;
44 import androidx.leanback.widget.SearchBar;
45 import androidx.leanback.widget.SearchOrbView;
46 import androidx.leanback.widget.SpeechRecognitionCallback;
47 import androidx.leanback.widget.VerticalGridView;
48 
49 import java.util.ArrayList;
50 import java.util.List;
51 
52 /**
53  * A fragment to handle searches. An application will supply an implementation
54  * of the {@link SearchResultProvider} interface to handle the search and return
55  * an {@link ObjectAdapter} containing the results. The results are rendered
56  * into a {@link RowsFragment}, in the same way that they are in a {@link
57  * BrowseFragment}.
58  *
59  * <p>A SpeechRecognizer object will be created for which your application will need to declare
60  * android.permission.RECORD_AUDIO in AndroidManifest file. If app's target version is >= 23 and
61  * the device version is >= 23, a permission dialog will show first time using speech recognition.
62  * 0 will be used as requestCode in requestPermissions() call.
63  * {@link #setSpeechRecognitionCallback(SpeechRecognitionCallback)} is deprecated.
64  * </p>
65  * <p>
66  * Speech recognition is automatically started when fragment is created, but
67  * not when fragment is restored from an instance state.  Activity may manually
68  * call {@link #startRecognition()}, typically in onNewIntent().
69  * </p>
70  * @deprecated use {@link SearchSupportFragment}
71  */
72 @Deprecated
73 public class SearchFragment extends Fragment {
74     static final String TAG = SearchFragment.class.getSimpleName();
75     static final boolean DEBUG = false;
76 
77     private static final String EXTRA_LEANBACK_BADGE_PRESENT = "LEANBACK_BADGE_PRESENT";
78     private static final String ARG_PREFIX = SearchFragment.class.getCanonicalName();
79     private static final String ARG_QUERY =  ARG_PREFIX + ".query";
80     private static final String ARG_TITLE = ARG_PREFIX  + ".title";
81 
82     static final long SPEECH_RECOGNITION_DELAY_MS = 300;
83 
84     static final int RESULTS_CHANGED = 0x1;
85     static final int QUERY_COMPLETE = 0x2;
86 
87     static final int AUDIO_PERMISSION_REQUEST_CODE = 0;
88 
89     /**
90      * Search API to be provided by the application.
91      */
92     public static interface SearchResultProvider {
93         /**
94          * <p>Method invoked some time prior to the first call to onQueryTextChange to retrieve
95          * an ObjectAdapter that will contain the results to future updates of the search query.</p>
96          *
97          * <p>As results are retrieved, the application should use the data set notification methods
98          * on the ObjectAdapter to instruct the SearchFragment to update the results.</p>
99          *
100          * @return ObjectAdapter The result object adapter.
101          */
getResultsAdapter()102         public ObjectAdapter getResultsAdapter();
103 
104         /**
105          * <p>Method invoked when the search query is updated.</p>
106          *
107          * <p>This is called as soon as the query changes; it is up to the application to add a
108          * delay before actually executing the queries if needed.
109          *
110          * <p>This method might not always be called before onQueryTextSubmit gets called, in
111          * particular for voice input.
112          *
113          * @param newQuery The current search query.
114          * @return whether the results changed as a result of the new query.
115          */
onQueryTextChange(String newQuery)116         public boolean onQueryTextChange(String newQuery);
117 
118         /**
119          * Method invoked when the search query is submitted, either by dismissing the keyboard,
120          * pressing search or next on the keyboard or when voice has detected the end of the query.
121          *
122          * @param query The query entered.
123          * @return whether the results changed as a result of the query.
124          */
onQueryTextSubmit(String query)125         public boolean onQueryTextSubmit(String query);
126     }
127 
128     final DataObserver mAdapterObserver = new DataObserver() {
129         @Override
130         public void onChanged() {
131             // onChanged() may be called multiple times e.g. the provider add
132             // rows to ArrayObjectAdapter one by one.
133             mHandler.removeCallbacks(mResultsChangedCallback);
134             mHandler.post(mResultsChangedCallback);
135         }
136     };
137 
138     final Handler mHandler = new Handler();
139 
140     final Runnable mResultsChangedCallback = new Runnable() {
141         @Override
142         public void run() {
143             if (DEBUG) Log.v(TAG, "results changed, new size " + mResultAdapter.size());
144             if (mRowsFragment != null
145                     && mRowsFragment.getAdapter() != mResultAdapter) {
146                 if (!(mRowsFragment.getAdapter() == null && mResultAdapter.size() == 0)) {
147                     mRowsFragment.setAdapter(mResultAdapter);
148                     mRowsFragment.setSelectedPosition(0);
149                 }
150             }
151             updateSearchBarVisibility();
152             mStatus |= RESULTS_CHANGED;
153             if ((mStatus & QUERY_COMPLETE) != 0) {
154                 updateFocus();
155             }
156             updateSearchBarNextFocusId();
157         }
158     };
159 
160     /**
161      * Runs when a new provider is set AND when the fragment view is created.
162      */
163     private final Runnable mSetSearchResultProvider = new Runnable() {
164         @Override
165         public void run() {
166             if (mRowsFragment == null) {
167                 // We'll retry once we have a rows fragment
168                 return;
169             }
170             // Retrieve the result adapter
171             ObjectAdapter adapter = mProvider.getResultsAdapter();
172             if (DEBUG) Log.v(TAG, "Got results adapter " + adapter);
173             if (adapter != mResultAdapter) {
174                 boolean firstTime = mResultAdapter == null;
175                 releaseAdapter();
176                 mResultAdapter = adapter;
177                 if (mResultAdapter != null) {
178                     mResultAdapter.registerObserver(mAdapterObserver);
179                 }
180                 if (DEBUG) {
181                     Log.v(TAG, "mResultAdapter " + mResultAdapter + " size "
182                             + (mResultAdapter == null ? 0 : mResultAdapter.size()));
183                 }
184                 // delay the first time to avoid setting a empty result adapter
185                 // until we got first onChange() from the provider
186                 if (!(firstTime && (mResultAdapter == null || mResultAdapter.size() == 0))) {
187                     mRowsFragment.setAdapter(mResultAdapter);
188                 }
189                 executePendingQuery();
190             }
191             updateSearchBarNextFocusId();
192 
193             if (DEBUG) {
194                 Log.v(TAG, "mAutoStartRecognition " + mAutoStartRecognition
195                         + " mResultAdapter " + mResultAdapter
196                         + " adapter " + mRowsFragment.getAdapter());
197             }
198             if (mAutoStartRecognition) {
199                 mHandler.removeCallbacks(mStartRecognitionRunnable);
200                 mHandler.postDelayed(mStartRecognitionRunnable, SPEECH_RECOGNITION_DELAY_MS);
201             } else {
202                 updateFocus();
203             }
204         }
205     };
206 
207     final Runnable mStartRecognitionRunnable = new Runnable() {
208         @Override
209         public void run() {
210             mAutoStartRecognition = false;
211             mSearchBar.startRecognition();
212         }
213     };
214 
215     RowsFragment mRowsFragment;
216     SearchBar mSearchBar;
217     SearchResultProvider mProvider;
218     String mPendingQuery = null;
219 
220     OnItemViewSelectedListener mOnItemViewSelectedListener;
221     private OnItemViewClickedListener mOnItemViewClickedListener;
222     ObjectAdapter mResultAdapter;
223     private SpeechRecognitionCallback mSpeechRecognitionCallback;
224 
225     private String mTitle;
226     private Drawable mBadgeDrawable;
227     private ExternalQuery mExternalQuery;
228 
229     private SpeechRecognizer mSpeechRecognizer;
230 
231     int mStatus;
232     boolean mAutoStartRecognition = true;
233 
234     private boolean mIsPaused;
235     private boolean mPendingStartRecognitionWhenPaused;
236     private SearchBar.SearchBarPermissionListener mPermissionListener =
237             new SearchBar.SearchBarPermissionListener() {
238         @Override
239         public void requestAudioPermission() {
240             PermissionHelper.requestPermissions(SearchFragment.this,
241                     new String[]{Manifest.permission.RECORD_AUDIO}, AUDIO_PERMISSION_REQUEST_CODE);
242         }
243     };
244 
245     @Override
onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)246     public void onRequestPermissionsResult(int requestCode, String[] permissions,
247                                            int[] grantResults) {
248         if (requestCode == AUDIO_PERMISSION_REQUEST_CODE && permissions.length > 0) {
249             if (permissions[0].equals(Manifest.permission.RECORD_AUDIO)
250                     && grantResults[0] == PERMISSION_GRANTED) {
251                 startRecognition();
252             }
253         }
254     }
255 
256     /**
257      * @param args Bundle to use for the arguments, if null a new Bundle will be created.
258      */
createArgs(Bundle args, String query)259     public static Bundle createArgs(Bundle args, String query) {
260         return createArgs(args, query, null);
261     }
262 
createArgs(Bundle args, String query, String title)263     public static Bundle createArgs(Bundle args, String query, String title)  {
264         if (args == null) {
265             args = new Bundle();
266         }
267         args.putString(ARG_QUERY, query);
268         args.putString(ARG_TITLE, title);
269         return args;
270     }
271 
272     /**
273      * Creates a search fragment with a given search query.
274      *
275      * <p>You should only use this if you need to start the search fragment with a
276      * pre-filled query.
277      *
278      * @param query The search query to begin with.
279      * @return A new SearchFragment.
280      */
newInstance(String query)281     public static SearchFragment newInstance(String query) {
282         SearchFragment fragment = new SearchFragment();
283         Bundle args = createArgs(null, query);
284         fragment.setArguments(args);
285         return fragment;
286     }
287 
288     @Override
onCreate(Bundle savedInstanceState)289     public void onCreate(Bundle savedInstanceState) {
290         if (mAutoStartRecognition) {
291             mAutoStartRecognition = savedInstanceState == null;
292         }
293         super.onCreate(savedInstanceState);
294     }
295 
296     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)297     public View onCreateView(LayoutInflater inflater, ViewGroup container,
298                              Bundle savedInstanceState) {
299         View root = inflater.inflate(R.layout.lb_search_fragment, container, false);
300 
301         FrameLayout searchFrame = (FrameLayout) root.findViewById(R.id.lb_search_frame);
302         mSearchBar = (SearchBar) searchFrame.findViewById(R.id.lb_search_bar);
303         mSearchBar.setSearchBarListener(new SearchBar.SearchBarListener() {
304             @Override
305             public void onSearchQueryChange(String query) {
306                 if (DEBUG) Log.v(TAG, String.format("onSearchQueryChange %s %s", query,
307                         null == mProvider ? "(null)" : mProvider));
308                 if (null != mProvider) {
309                     retrieveResults(query);
310                 } else {
311                     mPendingQuery = query;
312                 }
313             }
314 
315             @Override
316             public void onSearchQuerySubmit(String query) {
317                 if (DEBUG) Log.v(TAG, String.format("onSearchQuerySubmit %s", query));
318                 submitQuery(query);
319             }
320 
321             @Override
322             public void onKeyboardDismiss(String query) {
323                 if (DEBUG) Log.v(TAG, String.format("onKeyboardDismiss %s", query));
324                 queryComplete();
325             }
326         });
327         mSearchBar.setSpeechRecognitionCallback(mSpeechRecognitionCallback);
328         mSearchBar.setPermissionListener(mPermissionListener);
329         applyExternalQuery();
330 
331         readArguments(getArguments());
332         if (null != mBadgeDrawable) {
333             setBadgeDrawable(mBadgeDrawable);
334         }
335         if (null != mTitle) {
336             setTitle(mTitle);
337         }
338 
339         // Inject the RowsFragment in the results container
340         if (getChildFragmentManager().findFragmentById(R.id.lb_results_frame) == null) {
341             mRowsFragment = new RowsFragment();
342             getChildFragmentManager().beginTransaction()
343                     .replace(R.id.lb_results_frame, mRowsFragment).commit();
344         } else {
345             mRowsFragment = (RowsFragment) getChildFragmentManager()
346                     .findFragmentById(R.id.lb_results_frame);
347         }
348         mRowsFragment.setOnItemViewSelectedListener(new OnItemViewSelectedListener() {
349             @Override
350             public void onItemSelected(ViewHolder itemViewHolder, Object item,
351                                        RowPresenter.ViewHolder rowViewHolder, Row row) {
352                 if (DEBUG) {
353                     int position = mRowsFragment.getSelectedPosition();
354                     Log.v(TAG, String.format("onItemSelected %d", position));
355                 }
356                 updateSearchBarVisibility();
357                 if (null != mOnItemViewSelectedListener) {
358                     mOnItemViewSelectedListener.onItemSelected(itemViewHolder, item,
359                             rowViewHolder, row);
360                 }
361             }
362         });
363         mRowsFragment.setOnItemViewClickedListener(mOnItemViewClickedListener);
364         mRowsFragment.setExpand(true);
365         if (null != mProvider) {
366             onSetSearchResultProvider();
367         }
368         return root;
369     }
370 
resultsAvailable()371     private void resultsAvailable() {
372         if ((mStatus & QUERY_COMPLETE) != 0) {
373             focusOnResults();
374         }
375         updateSearchBarNextFocusId();
376     }
377 
378     @Override
onStart()379     public void onStart() {
380         super.onStart();
381 
382         VerticalGridView list = mRowsFragment.getVerticalGridView();
383         int mContainerListAlignTop =
384                 getResources().getDimensionPixelSize(R.dimen.lb_search_browse_rows_align_top);
385         list.setItemAlignmentOffset(0);
386         list.setItemAlignmentOffsetPercent(VerticalGridView.ITEM_ALIGN_OFFSET_PERCENT_DISABLED);
387         list.setWindowAlignmentOffset(mContainerListAlignTop);
388         list.setWindowAlignmentOffsetPercent(VerticalGridView.WINDOW_ALIGN_OFFSET_PERCENT_DISABLED);
389         list.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_NO_EDGE);
390         // VerticalGridView should not be focusable (see b/26894680 for details).
391         list.setFocusable(false);
392         list.setFocusableInTouchMode(false);
393     }
394 
395     @Override
onResume()396     public void onResume() {
397         super.onResume();
398         mIsPaused = false;
399         if (mSpeechRecognitionCallback == null && null == mSpeechRecognizer) {
400             mSpeechRecognizer = SpeechRecognizer.createSpeechRecognizer(
401                     FragmentUtil.getContext(SearchFragment.this));
402             mSearchBar.setSpeechRecognizer(mSpeechRecognizer);
403         }
404         if (mPendingStartRecognitionWhenPaused) {
405             mPendingStartRecognitionWhenPaused = false;
406             mSearchBar.startRecognition();
407         } else {
408             // Ensure search bar state consistency when using external recognizer
409             mSearchBar.stopRecognition();
410         }
411     }
412 
413     @Override
onPause()414     public void onPause() {
415         releaseRecognizer();
416         mIsPaused = true;
417         super.onPause();
418     }
419 
420     @Override
onDestroy()421     public void onDestroy() {
422         releaseAdapter();
423         super.onDestroy();
424     }
425 
426     /**
427      * Returns RowsFragment that shows result rows. RowsFragment is initialized after
428      * SearchFragment.onCreateView().
429      *
430      * @return RowsFragment that shows result rows.
431      */
getRowsFragment()432     public RowsFragment getRowsFragment() {
433         return mRowsFragment;
434     }
435 
releaseRecognizer()436     private void releaseRecognizer() {
437         if (null != mSpeechRecognizer) {
438             mSearchBar.setSpeechRecognizer(null);
439             mSpeechRecognizer.destroy();
440             mSpeechRecognizer = null;
441         }
442     }
443 
444     /**
445      * Starts speech recognition.  Typical use case is that
446      * activity receives onNewIntent() call when user clicks a MIC button.
447      * Note that SearchFragment automatically starts speech recognition
448      * at first time created, there is no need to call startRecognition()
449      * when fragment is created.
450      */
startRecognition()451     public void startRecognition() {
452         if (mIsPaused) {
453             mPendingStartRecognitionWhenPaused = true;
454         } else {
455             mSearchBar.startRecognition();
456         }
457     }
458 
459     /**
460      * Sets the search provider that is responsible for returning results for the
461      * search query.
462      */
setSearchResultProvider(SearchResultProvider searchResultProvider)463     public void setSearchResultProvider(SearchResultProvider searchResultProvider) {
464         if (mProvider != searchResultProvider) {
465             mProvider = searchResultProvider;
466             onSetSearchResultProvider();
467         }
468     }
469 
470     /**
471      * Sets an item selection listener for the results.
472      *
473      * @param listener The item selection listener to be invoked when an item in
474      *        the search results is selected.
475      */
setOnItemViewSelectedListener(OnItemViewSelectedListener listener)476     public void setOnItemViewSelectedListener(OnItemViewSelectedListener listener) {
477         mOnItemViewSelectedListener = listener;
478     }
479 
480     /**
481      * Sets an item clicked listener for the results.
482      *
483      * @param listener The item clicked listener to be invoked when an item in
484      *        the search results is clicked.
485      */
setOnItemViewClickedListener(OnItemViewClickedListener listener)486     public void setOnItemViewClickedListener(OnItemViewClickedListener listener) {
487         if (listener != mOnItemViewClickedListener) {
488             mOnItemViewClickedListener = listener;
489             if (mRowsFragment != null) {
490                 mRowsFragment.setOnItemViewClickedListener(mOnItemViewClickedListener);
491             }
492         }
493     }
494 
495     /**
496      * Sets the title string to be be shown in an empty search bar. The title
497      * may be placed in a call-to-action, such as "Search <i>title</i>" or
498      * "Speak to search <i>title</i>".
499      */
setTitle(String title)500     public void setTitle(String title) {
501         mTitle = title;
502         if (null != mSearchBar) {
503             mSearchBar.setTitle(title);
504         }
505     }
506 
507     /**
508      * Returns the title set in the search bar.
509      */
getTitle()510     public String getTitle() {
511         if (null != mSearchBar) {
512             return mSearchBar.getTitle();
513         }
514         return null;
515     }
516 
517     /**
518      * Sets the badge drawable that will be shown inside the search bar next to
519      * the title.
520      */
setBadgeDrawable(Drawable drawable)521     public void setBadgeDrawable(Drawable drawable) {
522         mBadgeDrawable = drawable;
523         if (null != mSearchBar) {
524             mSearchBar.setBadgeDrawable(drawable);
525         }
526     }
527 
528     /**
529      * Returns the badge drawable in the search bar.
530      */
getBadgeDrawable()531     public Drawable getBadgeDrawable() {
532         if (null != mSearchBar) {
533             return mSearchBar.getBadgeDrawable();
534         }
535         return null;
536     }
537 
538     /**
539      * Sets background color of not-listening state search orb.
540      *
541      * @param colors SearchOrbView.Colors.
542      */
setSearchAffordanceColors(SearchOrbView.Colors colors)543     public void setSearchAffordanceColors(SearchOrbView.Colors colors) {
544         if (mSearchBar != null) {
545             mSearchBar.setSearchAffordanceColors(colors);
546         }
547     }
548 
549     /**
550      * Sets background color of listening state search orb.
551      *
552      * @param colors SearchOrbView.Colors.
553      */
setSearchAffordanceColorsInListening(SearchOrbView.Colors colors)554     public void setSearchAffordanceColorsInListening(SearchOrbView.Colors colors) {
555         if (mSearchBar != null) {
556             mSearchBar.setSearchAffordanceColorsInListening(colors);
557         }
558     }
559 
560     /**
561      * Displays the completions shown by the IME. An application may provide
562      * a list of query completions that the system will show in the IME.
563      *
564      * @param completions A list of completions to show in the IME. Setting to
565      *        null or empty will clear the list.
566      */
displayCompletions(List<String> completions)567     public void displayCompletions(List<String> completions) {
568         mSearchBar.displayCompletions(completions);
569     }
570 
571     /**
572      * Displays the completions shown by the IME. An application may provide
573      * a list of query completions that the system will show in the IME.
574      *
575      * @param completions A list of completions to show in the IME. Setting to
576      *        null or empty will clear the list.
577      */
displayCompletions(CompletionInfo[] completions)578     public void displayCompletions(CompletionInfo[] completions) {
579         mSearchBar.displayCompletions(completions);
580     }
581 
582     /**
583      * Sets this callback to have the fragment pass speech recognition requests
584      * to the activity rather than using a SpeechRecognizer object.
585      * @deprecated Launching voice recognition activity is no longer supported. App should declare
586      *             android.permission.RECORD_AUDIO in AndroidManifest file.
587      */
588     @Deprecated
setSpeechRecognitionCallback(SpeechRecognitionCallback callback)589     public void setSpeechRecognitionCallback(SpeechRecognitionCallback callback) {
590         mSpeechRecognitionCallback = callback;
591         if (mSearchBar != null) {
592             mSearchBar.setSpeechRecognitionCallback(mSpeechRecognitionCallback);
593         }
594         if (callback != null) {
595             releaseRecognizer();
596         }
597     }
598 
599     /**
600      * Sets the text of the search query and optionally submits the query. Either
601      * {@link SearchResultProvider#onQueryTextChange onQueryTextChange} or
602      * {@link SearchResultProvider#onQueryTextSubmit onQueryTextSubmit} will be
603      * called on the provider if it is set.
604      *
605      * @param query The search query to set.
606      * @param submit Whether to submit the query.
607      */
setSearchQuery(String query, boolean submit)608     public void setSearchQuery(String query, boolean submit) {
609         if (DEBUG) Log.v(TAG, "setSearchQuery " + query + " submit " + submit);
610         if (query == null) {
611             return;
612         }
613         mExternalQuery = new ExternalQuery(query, submit);
614         applyExternalQuery();
615         if (mAutoStartRecognition) {
616             mAutoStartRecognition = false;
617             mHandler.removeCallbacks(mStartRecognitionRunnable);
618         }
619     }
620 
621     /**
622      * Sets the text of the search query based on the {@link RecognizerIntent#EXTRA_RESULTS} in
623      * the given intent, and optionally submit the query.  If more than one result is present
624      * in the results list, the first will be used.
625      *
626      * @param intent Intent received from a speech recognition service.
627      * @param submit Whether to submit the query.
628      */
setSearchQuery(Intent intent, boolean submit)629     public void setSearchQuery(Intent intent, boolean submit) {
630         ArrayList<String> matches = intent.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
631         if (matches != null && matches.size() > 0) {
632             setSearchQuery(matches.get(0), submit);
633         }
634     }
635 
636     /**
637      * Returns an intent that can be used to request speech recognition.
638      * Built from the base {@link RecognizerIntent#ACTION_RECOGNIZE_SPEECH} plus
639      * extras:
640      *
641      * <ul>
642      * <li>{@link RecognizerIntent#EXTRA_LANGUAGE_MODEL} set to
643      * {@link RecognizerIntent#LANGUAGE_MODEL_FREE_FORM}</li>
644      * <li>{@link RecognizerIntent#EXTRA_PARTIAL_RESULTS} set to true</li>
645      * <li>{@link RecognizerIntent#EXTRA_PROMPT} set to the search bar hint text</li>
646      * </ul>
647      *
648      * For handling the intent returned from the service, see
649      * {@link #setSearchQuery(Intent, boolean)}.
650      */
getRecognizerIntent()651     public Intent getRecognizerIntent() {
652         Intent recognizerIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
653         recognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
654                 RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
655         recognizerIntent.putExtra(RecognizerIntent.EXTRA_PARTIAL_RESULTS, true);
656         if (mSearchBar != null && mSearchBar.getHint() != null) {
657             recognizerIntent.putExtra(RecognizerIntent.EXTRA_PROMPT, mSearchBar.getHint());
658         }
659         recognizerIntent.putExtra(EXTRA_LEANBACK_BADGE_PRESENT, mBadgeDrawable != null);
660         return recognizerIntent;
661     }
662 
retrieveResults(String searchQuery)663     void retrieveResults(String searchQuery) {
664         if (DEBUG) Log.v(TAG, "retrieveResults " + searchQuery);
665         if (mProvider.onQueryTextChange(searchQuery)) {
666             mStatus &= ~QUERY_COMPLETE;
667         }
668     }
669 
submitQuery(String query)670     void submitQuery(String query) {
671         queryComplete();
672         if (null != mProvider) {
673             mProvider.onQueryTextSubmit(query);
674         }
675     }
676 
queryComplete()677     void queryComplete() {
678         if (DEBUG) Log.v(TAG, "queryComplete");
679         mStatus |= QUERY_COMPLETE;
680         focusOnResults();
681     }
682 
updateSearchBarVisibility()683     void updateSearchBarVisibility() {
684         int position = mRowsFragment != null ? mRowsFragment.getSelectedPosition() : -1;
685         mSearchBar.setVisibility(position <=0 || mResultAdapter == null
686                 || mResultAdapter.size() == 0 ? View.VISIBLE : View.GONE);
687     }
688 
updateSearchBarNextFocusId()689     void updateSearchBarNextFocusId() {
690         if (mSearchBar == null || mResultAdapter == null) {
691             return;
692         }
693         final int viewId = (mResultAdapter.size() == 0 || mRowsFragment == null
694                 || mRowsFragment.getVerticalGridView() == null)
695                         ? 0 : mRowsFragment.getVerticalGridView().getId();
696         mSearchBar.setNextFocusDownId(viewId);
697     }
698 
updateFocus()699     void updateFocus() {
700         if (mResultAdapter != null && mResultAdapter.size() > 0
701                 && mRowsFragment != null && mRowsFragment.getAdapter() == mResultAdapter) {
702             focusOnResults();
703         } else {
704             mSearchBar.requestFocus();
705         }
706     }
707 
focusOnResults()708     private void focusOnResults() {
709         if (mRowsFragment == null || mRowsFragment.getVerticalGridView() == null
710                 || mResultAdapter.size() == 0) {
711             return;
712         }
713         if (mRowsFragment.getVerticalGridView().requestFocus()) {
714             mStatus &= ~RESULTS_CHANGED;
715         }
716     }
717 
onSetSearchResultProvider()718     private void onSetSearchResultProvider() {
719         mHandler.removeCallbacks(mSetSearchResultProvider);
720         mHandler.post(mSetSearchResultProvider);
721     }
722 
releaseAdapter()723     void releaseAdapter() {
724         if (mResultAdapter != null) {
725             mResultAdapter.unregisterObserver(mAdapterObserver);
726             mResultAdapter = null;
727         }
728     }
729 
executePendingQuery()730     void executePendingQuery() {
731         if (null != mPendingQuery && null != mResultAdapter) {
732             String query = mPendingQuery;
733             mPendingQuery = null;
734             retrieveResults(query);
735         }
736     }
737 
applyExternalQuery()738     private void applyExternalQuery() {
739         if (mExternalQuery == null || mSearchBar == null) {
740             return;
741         }
742         mSearchBar.setSearchQuery(mExternalQuery.mQuery);
743         if (mExternalQuery.mSubmit) {
744             submitQuery(mExternalQuery.mQuery);
745         }
746         mExternalQuery = null;
747     }
748 
readArguments(Bundle args)749     private void readArguments(Bundle args) {
750         if (null == args) {
751             return;
752         }
753         if (args.containsKey(ARG_QUERY)) {
754             setSearchQuery(args.getString(ARG_QUERY));
755         }
756 
757         if (args.containsKey(ARG_TITLE)) {
758             setTitle(args.getString(ARG_TITLE));
759         }
760     }
761 
setSearchQuery(String query)762     private void setSearchQuery(String query) {
763         mSearchBar.setSearchQuery(query);
764     }
765 
766     static class ExternalQuery {
767         String mQuery;
768         boolean mSubmit;
769 
ExternalQuery(String query, boolean submit)770         ExternalQuery(String query, boolean submit) {
771             mQuery = query;
772             mSubmit = submit;
773         }
774     }
775 }
776