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