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