SearchFragment.java revision 490691bd414dbb2093dc6cb56ae63ae9b04e70e3
1/* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 * in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the License 10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 * or implied. See the License for the specific language governing permissions and limitations under 12 * the License. 13 */ 14package android.support.v17.leanback.app; 15 16import android.app.Fragment; 17import android.content.Intent; 18import android.graphics.drawable.Drawable; 19import android.os.Bundle; 20import android.os.Handler; 21import android.speech.SpeechRecognizer; 22import android.speech.RecognizerIntent; 23import android.support.v17.leanback.widget.ObjectAdapter; 24import android.support.v17.leanback.widget.ObjectAdapter.DataObserver; 25import android.support.v17.leanback.widget.OnItemClickedListener; 26import android.support.v17.leanback.widget.OnItemSelectedListener; 27import android.support.v17.leanback.widget.OnItemViewClickedListener; 28import android.support.v17.leanback.widget.OnItemViewSelectedListener; 29import android.support.v17.leanback.widget.Row; 30import android.support.v17.leanback.widget.RowPresenter; 31import android.support.v17.leanback.widget.SearchBar; 32import android.support.v17.leanback.widget.VerticalGridView; 33import android.support.v17.leanback.widget.Presenter.ViewHolder; 34import android.support.v17.leanback.widget.SpeechRecognitionCallback; 35import android.util.Log; 36import android.view.LayoutInflater; 37import android.view.View; 38import android.view.ViewGroup; 39import android.widget.FrameLayout; 40import android.support.v17.leanback.R; 41 42import java.util.ArrayList; 43import 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 RowsFragment}, in the same way that they are in a {@link 50 * BrowseFragment}. 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 */ 63public class SearchFragment extends Fragment { 64 private static final String TAG = SearchFragment.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 = SearchFragment.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 SearchFragment to update the results.</p> 87 * 88 * @return ObjectAdapter The result object adapter. 89 */ 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 */ 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 */ 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 (mRowsFragment != null 133 && mRowsFragment.getAdapter() != mResultAdapter) { 134 if (!(mRowsFragment.getAdapter() == null && mResultAdapter.size() == 0)) { 135 mRowsFragment.setAdapter(mResultAdapter); 136 mRowsFragment.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 (mRowsFragment == 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 mRowsFragment.setAdapter(mResultAdapter); 173 } 174 executePendingQuery(); 175 } 176 updateSearchBarNextFocusId(); 177 178 if (DEBUG) Log.v(TAG, "mAutoStartRecognition " + mAutoStartRecognition + 179 " mResultAdapter " + mResultAdapter + 180 " adapter " + mRowsFragment.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 RowsFragment mRowsFragment; 199 private SearchBar mSearchBar; 200 private SearchResultProvider mProvider; 201 private String mPendingQuery = null; 202 203 private OnItemSelectedListener mOnItemSelectedListener; 204 private OnItemClickedListener mOnItemClickedListener; 205 private OnItemViewSelectedListener mOnItemViewSelectedListener; 206 private OnItemViewClickedListener mOnItemViewClickedListener; 207 private ObjectAdapter mResultAdapter; 208 private SpeechRecognitionCallback mSpeechRecognitionCallback; 209 210 private String mTitle; 211 private Drawable mBadgeDrawable; 212 private ExternalQuery mExternalQuery; 213 214 private SpeechRecognizer mSpeechRecognizer; 215 216 private int mStatus; 217 private boolean mAutoStartRecognition = true; 218 219 /** 220 * @param args Bundle to use for the arguments, if null a new Bundle will be created. 221 */ 222 public static Bundle createArgs(Bundle args, String query) { 223 return createArgs(args, query, null); 224 } 225 226 public static Bundle createArgs(Bundle args, String query, String title) { 227 if (args == null) { 228 args = new Bundle(); 229 } 230 args.putString(ARG_QUERY, query); 231 args.putString(ARG_TITLE, title); 232 return args; 233 } 234 235 /** 236 * Create a search fragment with a given search query. 237 * 238 * <p>You should only use this if you need to start the search fragment with a 239 * pre-filled query. 240 * 241 * @param query The search query to begin with. 242 * @return A new SearchFragment. 243 */ 244 public static SearchFragment newInstance(String query) { 245 SearchFragment fragment = new SearchFragment(); 246 Bundle args = createArgs(null, query); 247 fragment.setArguments(args); 248 return fragment; 249 } 250 251 @Override 252 public void onCreate(Bundle savedInstanceState) { 253 if (mAutoStartRecognition) { 254 mAutoStartRecognition = savedInstanceState == null; 255 } 256 super.onCreate(savedInstanceState); 257 } 258 259 @Override 260 public View onCreateView(LayoutInflater inflater, ViewGroup container, 261 Bundle savedInstanceState) { 262 View root = inflater.inflate(R.layout.lb_search_fragment, container, false); 263 264 FrameLayout searchFrame = (FrameLayout) root.findViewById(R.id.lb_search_frame); 265 mSearchBar = (SearchBar) searchFrame.findViewById(R.id.lb_search_bar); 266 mSearchBar.setSearchBarListener(new SearchBar.SearchBarListener() { 267 @Override 268 public void onSearchQueryChange(String query) { 269 if (DEBUG) Log.v(TAG, String.format("onSearchQueryChange %s %s", query, 270 null == mProvider ? "(null)" : mProvider)); 271 if (null != mProvider) { 272 retrieveResults(query); 273 } else { 274 mPendingQuery = query; 275 } 276 } 277 278 @Override 279 public void onSearchQuerySubmit(String query) { 280 if (DEBUG) Log.v(TAG, String.format("onSearchQuerySubmit %s", query)); 281 submitQuery(query); 282 } 283 284 @Override 285 public void onKeyboardDismiss(String query) { 286 if (DEBUG) Log.v(TAG, String.format("onKeyboardDismiss %s", query)); 287 queryComplete(); 288 } 289 }); 290 mSearchBar.setSpeechRecognitionCallback(mSpeechRecognitionCallback); 291 applyExternalQuery(); 292 293 readArguments(getArguments()); 294 if (null != mBadgeDrawable) { 295 setBadgeDrawable(mBadgeDrawable); 296 } 297 if (null != mTitle) { 298 setTitle(mTitle); 299 } 300 301 // Inject the RowsFragment in the results container 302 if (getChildFragmentManager().findFragmentById(R.id.lb_results_frame) == null) { 303 mRowsFragment = new RowsFragment(); 304 getChildFragmentManager().beginTransaction() 305 .replace(R.id.lb_results_frame, mRowsFragment).commit(); 306 } else { 307 mRowsFragment = (RowsFragment) getChildFragmentManager() 308 .findFragmentById(R.id.lb_results_frame); 309 } 310 mRowsFragment.setOnItemViewSelectedListener(new OnItemViewSelectedListener() { 311 @Override 312 public void onItemSelected(ViewHolder itemViewHolder, Object item, 313 RowPresenter.ViewHolder rowViewHolder, Row row) { 314 int position = mRowsFragment.getVerticalGridView().getSelectedPosition(); 315 if (DEBUG) Log.v(TAG, String.format("onItemSelected %d", position)); 316 mSearchBar.setVisibility(0 >= position ? View.VISIBLE : View.GONE); 317 if (null != mOnItemSelectedListener) { 318 mOnItemSelectedListener.onItemSelected(item, row); 319 } 320 if (null != mOnItemViewSelectedListener) { 321 mOnItemViewSelectedListener.onItemSelected(itemViewHolder, item, 322 rowViewHolder, row); 323 } 324 } 325 }); 326 mRowsFragment.setOnItemViewClickedListener(new OnItemViewClickedListener() { 327 @Override 328 public void onItemClicked(ViewHolder itemViewHolder, Object item, 329 RowPresenter.ViewHolder rowViewHolder, Row row) { 330 int position = mRowsFragment.getVerticalGridView().getSelectedPosition(); 331 if (DEBUG) Log.v(TAG, String.format("onItemClicked %d", position)); 332 if (null != mOnItemClickedListener) { 333 mOnItemClickedListener.onItemClicked(item, row); 334 } 335 if (null != mOnItemViewClickedListener) { 336 mOnItemViewClickedListener.onItemClicked(itemViewHolder, item, 337 rowViewHolder, row); 338 } 339 } 340 }); 341 mRowsFragment.setExpand(true); 342 if (null != mProvider) { 343 onSetSearchResultProvider(); 344 } 345 return root; 346 } 347 348 private void resultsAvailable() { 349 if ((mStatus & QUERY_COMPLETE) != 0) { 350 focusOnResults(); 351 } 352 updateSearchBarNextFocusId(); 353 } 354 355 @Override 356 public void onStart() { 357 super.onStart(); 358 359 VerticalGridView list = mRowsFragment.getVerticalGridView(); 360 int mContainerListAlignTop = 361 getResources().getDimensionPixelSize(R.dimen.lb_search_browse_rows_align_top); 362 list.setItemAlignmentOffset(0); 363 list.setItemAlignmentOffsetPercent(VerticalGridView.ITEM_ALIGN_OFFSET_PERCENT_DISABLED); 364 list.setWindowAlignmentOffset(mContainerListAlignTop); 365 list.setWindowAlignmentOffsetPercent(VerticalGridView.WINDOW_ALIGN_OFFSET_PERCENT_DISABLED); 366 list.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_NO_EDGE); 367 } 368 369 @Override 370 public void onResume() { 371 super.onResume(); 372 if (mSpeechRecognitionCallback == null && null == mSpeechRecognizer) { 373 mSpeechRecognizer = SpeechRecognizer.createSpeechRecognizer(getActivity()); 374 mSearchBar.setSpeechRecognizer(mSpeechRecognizer); 375 } 376 // Ensure search bar state consistency when using external recognizer 377 mSearchBar.stopRecognition(); 378 } 379 380 @Override 381 public void onPause() { 382 releaseRecognizer(); 383 super.onPause(); 384 } 385 386 @Override 387 public void onDestroy() { 388 releaseAdapter(); 389 super.onDestroy(); 390 } 391 392 private void releaseRecognizer() { 393 if (null != mSpeechRecognizer) { 394 mSearchBar.setSpeechRecognizer(null); 395 mSpeechRecognizer.destroy(); 396 mSpeechRecognizer = null; 397 } 398 } 399 400 /** 401 * Starts speech recognition. Typical use case is that 402 * activity receives onNewIntent() call when user clicks a MIC button. 403 * Note that SearchFragment automatically starts speech recognition 404 * at first time created, there is no need to call startRecognition() 405 * when fragment is created. 406 */ 407 public void startRecognition() { 408 mSearchBar.startRecognition(); 409 } 410 411 /** 412 * Set the search provider that is responsible for returning results for the 413 * search query. 414 */ 415 public void setSearchResultProvider(SearchResultProvider searchResultProvider) { 416 if (mProvider != searchResultProvider) { 417 mProvider = searchResultProvider; 418 onSetSearchResultProvider(); 419 } 420 } 421 422 /** 423 * Sets an item selection listener for the results. 424 * 425 * @param listener The item selection listener to be invoked when an item in 426 * the search results is selected. 427 * @deprecated Use {@link #setOnItemViewSelectedListener(OnItemViewSelectedListener)} 428 */ 429 public void setOnItemSelectedListener(OnItemSelectedListener listener) { 430 mOnItemSelectedListener = listener; 431 } 432 433 /** 434 * Sets an item clicked listener for the results. 435 * 436 * @param listener The item clicked listener to be invoked when an item in 437 * the search results is clicked. 438 * @deprecated Use {@link #setOnItemViewClickedListener(OnItemViewClickedListener)} 439 */ 440 public void setOnItemClickedListener(OnItemClickedListener listener) { 441 mOnItemClickedListener = listener; 442 } 443 444 /** 445 * Sets an item selection listener for the results. 446 * 447 * @param listener The item selection listener to be invoked when an item in 448 * the search results is selected. 449 */ 450 public void setOnItemViewSelectedListener(OnItemViewSelectedListener listener) { 451 mOnItemViewSelectedListener = listener; 452 } 453 454 /** 455 * Sets an item clicked listener for the results. 456 * 457 * @param listener The item clicked listener to be invoked when an item in 458 * the search results is clicked. 459 */ 460 public void setOnItemViewClickedListener(OnItemViewClickedListener listener) { 461 mOnItemViewClickedListener = listener; 462 } 463 464 /** 465 * Sets the title string to be be shown in an empty search bar. The title 466 * may be placed in a call-to-action, such as "Search <i>title</i>" or 467 * "Speak to search <i>title</i>". 468 */ 469 public void setTitle(String title) { 470 mTitle = title; 471 if (null != mSearchBar) { 472 mSearchBar.setTitle(title); 473 } 474 } 475 476 /** 477 * Returns the title set in the search bar. 478 */ 479 public String getTitle() { 480 if (null != mSearchBar) { 481 return mSearchBar.getTitle(); 482 } 483 return null; 484 } 485 486 /** 487 * Sets the badge drawable that will be shown inside the search bar next to 488 * the title. 489 */ 490 public void setBadgeDrawable(Drawable drawable) { 491 mBadgeDrawable = drawable; 492 if (null != mSearchBar) { 493 mSearchBar.setBadgeDrawable(drawable); 494 } 495 } 496 497 /** 498 * Returns the badge drawable in the search bar. 499 */ 500 public Drawable getBadgeDrawable() { 501 if (null != mSearchBar) { 502 return mSearchBar.getBadgeDrawable(); 503 } 504 return null; 505 } 506 507 /** 508 * Display the completions shown by the IME. An application may provide 509 * a list of query completions that the system will show in the IME. 510 * 511 * @param completions A list of completions to show in the IME. Setting to 512 * null or empty will clear the list. 513 */ 514 public void displayCompletions(List<String> completions) { 515 mSearchBar.displayCompletions(completions); 516 } 517 518 /** 519 * Set this callback to have the fragment pass speech recognition requests 520 * to the activity rather than using an internal recognizer. 521 */ 522 public void setSpeechRecognitionCallback(SpeechRecognitionCallback callback) { 523 mSpeechRecognitionCallback = callback; 524 if (mSearchBar != null) { 525 mSearchBar.setSpeechRecognitionCallback(mSpeechRecognitionCallback); 526 } 527 if (callback != null) { 528 releaseRecognizer(); 529 } 530 } 531 532 /** 533 * Sets the text of the search query and optionally submits the query. Either 534 * {@link SearchResultProvider#onQueryTextChange onQueryTextChange} or 535 * {@link SearchResultProvider#onQueryTextSubmit onQueryTextSubmit} will be 536 * called on the provider if it is set. 537 * 538 * @param query The search query to set. 539 * @param submit Whether to submit the query. 540 */ 541 public void setSearchQuery(String query, boolean submit) { 542 if (DEBUG) Log.v(TAG, "setSearchQuery " + query + " submit " + submit); 543 if (query == null) { 544 return; 545 } 546 mExternalQuery = new ExternalQuery(query, submit); 547 applyExternalQuery(); 548 if (mAutoStartRecognition) { 549 mAutoStartRecognition = false; 550 mHandler.removeCallbacks(mStartRecognitionRunnable); 551 } 552 } 553 554 /** 555 * Sets the text of the search query based on the {@link RecognizerIntent#EXTRA_RESULTS} in 556 * the given intent, and optionally submit the query. If more than one result is present 557 * in the results list, the first will be used. 558 * 559 * @param intent Intent received from a speech recognition service. 560 * @param submit Whether to submit the query. 561 */ 562 public void setSearchQuery(Intent intent, boolean submit) { 563 ArrayList<String> matches = intent.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS); 564 if (matches != null && matches.size() > 0) { 565 setSearchQuery(matches.get(0), submit); 566 } 567 } 568 569 /** 570 * Returns an intent that can be used to request speech recognition. 571 * Built from the base {@link RecognizerIntent#ACTION_RECOGNIZE_SPEECH} plus 572 * extras: 573 * 574 * <ul> 575 * <li>{@link RecognizerIntent#EXTRA_LANGUAGE_MODEL} set to 576 * {@link RecognizerIntent#LANGUAGE_MODEL_FREE_FORM}</li> 577 * <li>{@link RecognizerIntent#EXTRA_PARTIAL_RESULTS} set to true</li> 578 * <li>{@link RecognizerIntent#EXTRA_PROMPT} set to the search bar hint text</li> 579 * </ul> 580 * 581 * For handling the intent returned from the service, see 582 * {@link #setSearchQuery(Intent, boolean)}. 583 */ 584 public Intent getRecognizerIntent() { 585 Intent recognizerIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH); 586 recognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, 587 RecognizerIntent.LANGUAGE_MODEL_FREE_FORM); 588 recognizerIntent.putExtra(RecognizerIntent.EXTRA_PARTIAL_RESULTS, true); 589 if (mSearchBar != null && mSearchBar.getHint() != null) { 590 recognizerIntent.putExtra(RecognizerIntent.EXTRA_PROMPT, mSearchBar.getHint()); 591 } 592 recognizerIntent.putExtra(EXTRA_LEANBACK_BADGE_PRESENT, mBadgeDrawable != null); 593 return recognizerIntent; 594 } 595 596 private void retrieveResults(String searchQuery) { 597 if (DEBUG) Log.v(TAG, "retrieveResults " + searchQuery); 598 if (mProvider.onQueryTextChange(searchQuery)) { 599 mStatus &= ~QUERY_COMPLETE; 600 } 601 } 602 603 private void submitQuery(String query) { 604 queryComplete(); 605 if (null != mProvider) { 606 mProvider.onQueryTextSubmit(query); 607 } 608 } 609 610 private void queryComplete() { 611 if (DEBUG) Log.v(TAG, "queryComplete"); 612 mStatus |= QUERY_COMPLETE; 613 focusOnResults(); 614 } 615 616 private void updateSearchBarNextFocusId() { 617 if (mSearchBar == null || mResultAdapter == null) { 618 return; 619 } 620 final int viewId = (mResultAdapter.size() == 0 || mRowsFragment == null || 621 mRowsFragment.getVerticalGridView() == null) ? 0 : 622 mRowsFragment.getVerticalGridView().getId(); 623 mSearchBar.setNextFocusDownId(viewId); 624 } 625 626 private void updateFocus() { 627 if (mResultAdapter != null && mResultAdapter.size() > 0 && 628 mRowsFragment != null && mRowsFragment.getAdapter() == mResultAdapter) { 629 focusOnResults(); 630 } else { 631 mSearchBar.requestFocus(); 632 } 633 } 634 635 private void focusOnResults() { 636 if (mRowsFragment == null || 637 mRowsFragment.getVerticalGridView() == null || 638 mResultAdapter.size() == 0) { 639 return; 640 } 641 if (mRowsFragment.getVerticalGridView().requestFocus()) { 642 mStatus &= ~RESULTS_CHANGED; 643 } 644 } 645 646 private void onSetSearchResultProvider() { 647 mHandler.removeCallbacks(mSetSearchResultProvider); 648 mHandler.post(mSetSearchResultProvider); 649 } 650 651 private void releaseAdapter() { 652 if (mResultAdapter != null) { 653 mResultAdapter.unregisterObserver(mAdapterObserver); 654 mResultAdapter = null; 655 } 656 } 657 658 private void executePendingQuery() { 659 if (null != mPendingQuery && null != mResultAdapter) { 660 String query = mPendingQuery; 661 mPendingQuery = null; 662 retrieveResults(query); 663 } 664 } 665 666 private void applyExternalQuery() { 667 if (mExternalQuery == null || mSearchBar == null) { 668 return; 669 } 670 mSearchBar.setSearchQuery(mExternalQuery.mQuery); 671 if (mExternalQuery.mSubmit) { 672 submitQuery(mExternalQuery.mQuery); 673 } 674 mExternalQuery = null; 675 } 676 677 private void readArguments(Bundle args) { 678 if (null == args) { 679 return; 680 } 681 if (args.containsKey(ARG_QUERY)) { 682 setSearchQuery(args.getString(ARG_QUERY)); 683 } 684 685 if (args.containsKey(ARG_TITLE)) { 686 setTitle(args.getString(ARG_TITLE)); 687 } 688 } 689 690 private void setSearchQuery(String query) { 691 mSearchBar.setSearchQuery(query); 692 } 693 694 static class ExternalQuery { 695 String mQuery; 696 boolean mSubmit; 697 698 ExternalQuery(String query, boolean submit) { 699 mQuery = query; 700 mSubmit = submit; 701 } 702 } 703} 704