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