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