1 /* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.app; 18 19 import org.xmlpull.v1.XmlPullParser; 20 import org.xmlpull.v1.XmlPullParserException; 21 22 import android.content.ComponentName; 23 import android.content.Context; 24 import android.content.pm.ActivityInfo; 25 import android.content.pm.PackageManager; 26 import android.content.pm.ProviderInfo; 27 import android.content.res.TypedArray; 28 import android.content.res.XmlResourceParser; 29 import android.os.Parcel; 30 import android.os.Parcelable; 31 import android.text.InputType; 32 import android.util.AttributeSet; 33 import android.util.Log; 34 import android.util.Xml; 35 import android.view.inputmethod.EditorInfo; 36 37 import java.io.IOException; 38 import java.util.HashMap; 39 40 /** 41 * Searchability meta-data for an activity. Only applications that search other applications 42 * should need to use this class. 43 * See <a href="{@docRoot}guide/topics/search/searchable-config.html">Searchable Configuration</a> 44 * for more information about declaring searchability meta-data for your application. 45 * 46 * @see SearchManager#getSearchableInfo(ComponentName) 47 * @see SearchManager#getSearchablesInGlobalSearch() 48 */ 49 public final class SearchableInfo implements Parcelable { 50 51 // general debugging support 52 private static final boolean DBG = false; 53 private static final String LOG_TAG = "SearchableInfo"; 54 55 // static strings used for XML lookups. 56 // TODO how should these be documented for the developer, in a more structured way than 57 // the current long wordy javadoc in SearchManager.java ? 58 private static final String MD_LABEL_SEARCHABLE = "android.app.searchable"; 59 private static final String MD_XML_ELEMENT_SEARCHABLE = "searchable"; 60 private static final String MD_XML_ELEMENT_SEARCHABLE_ACTION_KEY = "actionkey"; 61 62 // flags in the searchMode attribute 63 private static final int SEARCH_MODE_BADGE_LABEL = 0x04; 64 private static final int SEARCH_MODE_BADGE_ICON = 0x08; 65 private static final int SEARCH_MODE_QUERY_REWRITE_FROM_DATA = 0x10; 66 private static final int SEARCH_MODE_QUERY_REWRITE_FROM_TEXT = 0x20; 67 68 // true member variables - what we know about the searchability 69 private final int mLabelId; 70 private final ComponentName mSearchActivity; 71 private final int mHintId; 72 private final int mSearchMode; 73 private final int mIconId; 74 private final int mSearchButtonText; 75 private final int mSearchInputType; 76 private final int mSearchImeOptions; 77 private final boolean mIncludeInGlobalSearch; 78 private final boolean mQueryAfterZeroResults; 79 private final boolean mAutoUrlDetect; 80 private final int mSettingsDescriptionId; 81 private final String mSuggestAuthority; 82 private final String mSuggestPath; 83 private final String mSuggestSelection; 84 private final String mSuggestIntentAction; 85 private final String mSuggestIntentData; 86 private final int mSuggestThreshold; 87 // Maps key codes to action key information. auto-boxing is not so bad here, 88 // since keycodes for the hard keys are < 127. For such values, Integer.valueOf() 89 // uses shared Integer objects. 90 // This is not final, to allow lazy initialization. 91 private HashMap<Integer,ActionKeyInfo> mActionKeys = null; 92 private final String mSuggestProviderPackage; 93 94 // Flag values for Searchable_voiceSearchMode 95 private static final int VOICE_SEARCH_SHOW_BUTTON = 1; 96 private static final int VOICE_SEARCH_LAUNCH_WEB_SEARCH = 2; 97 private static final int VOICE_SEARCH_LAUNCH_RECOGNIZER = 4; 98 private final int mVoiceSearchMode; 99 private final int mVoiceLanguageModeId; // voiceLanguageModel 100 private final int mVoicePromptTextId; // voicePromptText 101 private final int mVoiceLanguageId; // voiceLanguage 102 private final int mVoiceMaxResults; // voiceMaxResults 103 104 /** 105 * Gets the search suggestion content provider authority. 106 * 107 * @return The search suggestions authority, or {@code null} if not set. 108 * @see android.R.styleable#Searchable_searchSuggestAuthority 109 */ 110 public String getSuggestAuthority() { 111 return mSuggestAuthority; 112 } 113 114 /** 115 * Gets the name of the package where the suggestion provider lives, 116 * or {@code null}. 117 */ 118 public String getSuggestPackage() { 119 return mSuggestProviderPackage; 120 } 121 122 /** 123 * Gets the component name of the searchable activity. 124 * 125 * @return A component name, never {@code null}. 126 */ 127 public ComponentName getSearchActivity() { 128 return mSearchActivity; 129 } 130 131 /** 132 * Checks whether the badge should be a text label. 133 * 134 * @see android.R.styleable#Searchable_searchMode 135 * 136 * @hide This feature is deprecated, no need to add it to the API. 137 */ 138 public boolean useBadgeLabel() { 139 return 0 != (mSearchMode & SEARCH_MODE_BADGE_LABEL); 140 } 141 142 /** 143 * Checks whether the badge should be an icon. 144 * 145 * @see android.R.styleable#Searchable_searchMode 146 * 147 * @hide This feature is deprecated, no need to add it to the API. 148 */ 149 public boolean useBadgeIcon() { 150 return (0 != (mSearchMode & SEARCH_MODE_BADGE_ICON)) && (mIconId != 0); 151 } 152 153 /** 154 * Checks whether the text in the query field should come from the suggestion intent data. 155 * 156 * @see android.R.styleable#Searchable_searchMode 157 */ 158 public boolean shouldRewriteQueryFromData() { 159 return 0 != (mSearchMode & SEARCH_MODE_QUERY_REWRITE_FROM_DATA); 160 } 161 162 /** 163 * Checks whether the text in the query field should come from the suggestion title. 164 * 165 * @see android.R.styleable#Searchable_searchMode 166 */ 167 public boolean shouldRewriteQueryFromText() { 168 return 0 != (mSearchMode & SEARCH_MODE_QUERY_REWRITE_FROM_TEXT); 169 } 170 171 /** 172 * Gets the resource id of the description string to use for this source in system search 173 * settings, or {@code 0} if none has been specified. 174 * 175 * @see android.R.styleable#Searchable_searchSettingsDescription 176 */ 177 public int getSettingsDescriptionId() { 178 return mSettingsDescriptionId; 179 } 180 181 /** 182 * Gets the content provider path for obtaining search suggestions. 183 * 184 * @return The suggestion path, or {@code null} if not set. 185 * @see android.R.styleable#Searchable_searchSuggestPath 186 */ 187 public String getSuggestPath() { 188 return mSuggestPath; 189 } 190 191 /** 192 * Gets the selection for obtaining search suggestions. 193 * 194 * @see android.R.styleable#Searchable_searchSuggestSelection 195 */ 196 public String getSuggestSelection() { 197 return mSuggestSelection; 198 } 199 200 /** 201 * Gets the optional intent action for use with these suggestions. This is 202 * useful if all intents will have the same action 203 * (e.g. {@link android.content.Intent#ACTION_VIEW}) 204 * 205 * This can be overriden in any given suggestion using the column 206 * {@link SearchManager#SUGGEST_COLUMN_INTENT_ACTION}. 207 * 208 * @return The default intent action, or {@code null} if not set. 209 * @see android.R.styleable#Searchable_searchSuggestIntentAction 210 */ 211 public String getSuggestIntentAction() { 212 return mSuggestIntentAction; 213 } 214 215 /** 216 * Gets the optional intent data for use with these suggestions. This is 217 * useful if all intents will have similar data URIs, 218 * but you'll likely need to provide a specific ID as well via the column 219 * {@link SearchManager#SUGGEST_COLUMN_INTENT_DATA_ID}, which will be appended to the 220 * intent data URI. 221 * 222 * This can be overriden in any given suggestion using the column 223 * {@link SearchManager#SUGGEST_COLUMN_INTENT_DATA}. 224 * 225 * @return The default intent data, or {@code null} if not set. 226 * @see android.R.styleable#Searchable_searchSuggestIntentData 227 */ 228 public String getSuggestIntentData() { 229 return mSuggestIntentData; 230 } 231 232 /** 233 * Gets the suggestion threshold. 234 * 235 * @return The suggestion threshold, or {@code 0} if not set. 236 * @see android.R.styleable#Searchable_searchSuggestThreshold 237 */ 238 public int getSuggestThreshold() { 239 return mSuggestThreshold; 240 } 241 242 /** 243 * Get the context for the searchable activity. 244 * 245 * @param context You need to supply a context to start with 246 * @return Returns a context related to the searchable activity 247 * @hide 248 */ 249 public Context getActivityContext(Context context) { 250 return createActivityContext(context, mSearchActivity); 251 } 252 253 /** 254 * Creates a context for another activity. 255 */ 256 private static Context createActivityContext(Context context, ComponentName activity) { 257 Context theirContext = null; 258 try { 259 theirContext = context.createPackageContext(activity.getPackageName(), 0); 260 } catch (PackageManager.NameNotFoundException e) { 261 Log.e(LOG_TAG, "Package not found " + activity.getPackageName()); 262 } catch (java.lang.SecurityException e) { 263 Log.e(LOG_TAG, "Can't make context for " + activity.getPackageName(), e); 264 } 265 266 return theirContext; 267 } 268 269 /** 270 * Get the context for the suggestions provider. 271 * 272 * @param context You need to supply a context to start with 273 * @param activityContext If we can determine that the provider and the activity are the 274 * same, we'll just return this one. 275 * @return Returns a context related to the suggestion provider 276 * @hide 277 */ 278 public Context getProviderContext(Context context, Context activityContext) { 279 Context theirContext = null; 280 if (mSearchActivity.getPackageName().equals(mSuggestProviderPackage)) { 281 return activityContext; 282 } 283 if (mSuggestProviderPackage != null) { 284 try { 285 theirContext = context.createPackageContext(mSuggestProviderPackage, 0); 286 } catch (PackageManager.NameNotFoundException e) { 287 // unexpected, but we deal with this by null-checking theirContext 288 } catch (java.lang.SecurityException e) { 289 // unexpected, but we deal with this by null-checking theirContext 290 } 291 } 292 return theirContext; 293 } 294 295 /** 296 * Constructor 297 * 298 * Given a ComponentName, get the searchability info 299 * and build a local copy of it. Use the factory, not this. 300 * 301 * @param activityContext runtime context for the activity that the searchable info is about. 302 * @param attr The attribute set we found in the XML file, contains the values that are used to 303 * construct the object. 304 * @param cName The component name of the searchable activity 305 * @throws IllegalArgumentException if the searchability info is invalid or insufficient 306 */ 307 private SearchableInfo(Context activityContext, AttributeSet attr, final ComponentName cName) { 308 mSearchActivity = cName; 309 310 TypedArray a = activityContext.obtainStyledAttributes(attr, 311 com.android.internal.R.styleable.Searchable); 312 mSearchMode = a.getInt(com.android.internal.R.styleable.Searchable_searchMode, 0); 313 mLabelId = a.getResourceId(com.android.internal.R.styleable.Searchable_label, 0); 314 mHintId = a.getResourceId(com.android.internal.R.styleable.Searchable_hint, 0); 315 mIconId = a.getResourceId(com.android.internal.R.styleable.Searchable_icon, 0); 316 mSearchButtonText = a.getResourceId( 317 com.android.internal.R.styleable.Searchable_searchButtonText, 0); 318 mSearchInputType = a.getInt(com.android.internal.R.styleable.Searchable_inputType, 319 InputType.TYPE_CLASS_TEXT | 320 InputType.TYPE_TEXT_VARIATION_NORMAL); 321 mSearchImeOptions = a.getInt(com.android.internal.R.styleable.Searchable_imeOptions, 322 EditorInfo.IME_ACTION_GO); 323 mIncludeInGlobalSearch = a.getBoolean( 324 com.android.internal.R.styleable.Searchable_includeInGlobalSearch, false); 325 mQueryAfterZeroResults = a.getBoolean( 326 com.android.internal.R.styleable.Searchable_queryAfterZeroResults, false); 327 mAutoUrlDetect = a.getBoolean( 328 com.android.internal.R.styleable.Searchable_autoUrlDetect, false); 329 330 mSettingsDescriptionId = a.getResourceId( 331 com.android.internal.R.styleable.Searchable_searchSettingsDescription, 0); 332 mSuggestAuthority = a.getString( 333 com.android.internal.R.styleable.Searchable_searchSuggestAuthority); 334 mSuggestPath = a.getString( 335 com.android.internal.R.styleable.Searchable_searchSuggestPath); 336 mSuggestSelection = a.getString( 337 com.android.internal.R.styleable.Searchable_searchSuggestSelection); 338 mSuggestIntentAction = a.getString( 339 com.android.internal.R.styleable.Searchable_searchSuggestIntentAction); 340 mSuggestIntentData = a.getString( 341 com.android.internal.R.styleable.Searchable_searchSuggestIntentData); 342 mSuggestThreshold = a.getInt( 343 com.android.internal.R.styleable.Searchable_searchSuggestThreshold, 0); 344 345 mVoiceSearchMode = 346 a.getInt(com.android.internal.R.styleable.Searchable_voiceSearchMode, 0); 347 // TODO this didn't work - came back zero from YouTube 348 mVoiceLanguageModeId = 349 a.getResourceId(com.android.internal.R.styleable.Searchable_voiceLanguageModel, 0); 350 mVoicePromptTextId = 351 a.getResourceId(com.android.internal.R.styleable.Searchable_voicePromptText, 0); 352 mVoiceLanguageId = 353 a.getResourceId(com.android.internal.R.styleable.Searchable_voiceLanguage, 0); 354 mVoiceMaxResults = 355 a.getInt(com.android.internal.R.styleable.Searchable_voiceMaxResults, 0); 356 357 a.recycle(); 358 359 // get package info for suggestions provider (if any) 360 String suggestProviderPackage = null; 361 if (mSuggestAuthority != null) { 362 PackageManager pm = activityContext.getPackageManager(); 363 ProviderInfo pi = pm.resolveContentProvider(mSuggestAuthority, 0); 364 if (pi != null) { 365 suggestProviderPackage = pi.packageName; 366 } 367 } 368 mSuggestProviderPackage = suggestProviderPackage; 369 370 // for now, implement some form of rules - minimal data 371 if (mLabelId == 0) { 372 throw new IllegalArgumentException("Search label must be a resource reference."); 373 } 374 } 375 376 /** 377 * Information about an action key in searchability meta-data. 378 * 379 * @see SearchableInfo#findActionKey(int) 380 * 381 * @hide This feature is used very little, and on many devices there are no reasonable 382 * keys to use for actions. 383 */ 384 public static class ActionKeyInfo implements Parcelable { 385 386 private final int mKeyCode; 387 private final String mQueryActionMsg; 388 private final String mSuggestActionMsg; 389 private final String mSuggestActionMsgColumn; 390 391 /** 392 * Create one object using attributeset as input data. 393 * @param activityContext runtime context of the activity that the action key information 394 * is about. 395 * @param attr The attribute set we found in the XML file, contains the values that are used to 396 * construct the object. 397 * @throws IllegalArgumentException if the action key configuration is invalid 398 */ 399 ActionKeyInfo(Context activityContext, AttributeSet attr) { 400 TypedArray a = activityContext.obtainStyledAttributes(attr, 401 com.android.internal.R.styleable.SearchableActionKey); 402 403 mKeyCode = a.getInt( 404 com.android.internal.R.styleable.SearchableActionKey_keycode, 0); 405 mQueryActionMsg = a.getString( 406 com.android.internal.R.styleable.SearchableActionKey_queryActionMsg); 407 mSuggestActionMsg = a.getString( 408 com.android.internal.R.styleable.SearchableActionKey_suggestActionMsg); 409 mSuggestActionMsgColumn = a.getString( 410 com.android.internal.R.styleable.SearchableActionKey_suggestActionMsgColumn); 411 a.recycle(); 412 413 // sanity check. 414 if (mKeyCode == 0) { 415 throw new IllegalArgumentException("No keycode."); 416 } else if ((mQueryActionMsg == null) && 417 (mSuggestActionMsg == null) && 418 (mSuggestActionMsgColumn == null)) { 419 throw new IllegalArgumentException("No message information."); 420 } 421 } 422 423 /** 424 * Instantiate a new ActionKeyInfo from the data in a Parcel that was 425 * previously written with {@link #writeToParcel(Parcel, int)}. 426 * 427 * @param in The Parcel containing the previously written ActionKeyInfo, 428 * positioned at the location in the buffer where it was written. 429 */ 430 private ActionKeyInfo(Parcel in) { 431 mKeyCode = in.readInt(); 432 mQueryActionMsg = in.readString(); 433 mSuggestActionMsg = in.readString(); 434 mSuggestActionMsgColumn = in.readString(); 435 } 436 437 /** 438 * Gets the key code that this action key info is for. 439 * @see android.R.styleable#SearchableActionKey_keycode 440 */ 441 public int getKeyCode() { 442 return mKeyCode; 443 } 444 445 /** 446 * Gets the action message to use for queries. 447 * @see android.R.styleable#SearchableActionKey_queryActionMsg 448 */ 449 public String getQueryActionMsg() { 450 return mQueryActionMsg; 451 } 452 453 /** 454 * Gets the action message to use for suggestions. 455 * @see android.R.styleable#SearchableActionKey_suggestActionMsg 456 */ 457 public String getSuggestActionMsg() { 458 return mSuggestActionMsg; 459 } 460 461 /** 462 * Gets the name of the column to get the suggestion action message from. 463 * @see android.R.styleable#SearchableActionKey_suggestActionMsgColumn 464 */ 465 public String getSuggestActionMsgColumn() { 466 return mSuggestActionMsgColumn; 467 } 468 469 public int describeContents() { 470 return 0; 471 } 472 473 public void writeToParcel(Parcel dest, int flags) { 474 dest.writeInt(mKeyCode); 475 dest.writeString(mQueryActionMsg); 476 dest.writeString(mSuggestActionMsg); 477 dest.writeString(mSuggestActionMsgColumn); 478 } 479 } 480 481 /** 482 * If any action keys were defined for this searchable activity, look up and return. 483 * 484 * @param keyCode The key that was pressed 485 * @return Returns the action key info, or {@code null} if none defined. 486 * 487 * @hide ActionKeyInfo is hidden 488 */ 489 public ActionKeyInfo findActionKey(int keyCode) { 490 if (mActionKeys == null) { 491 return null; 492 } 493 return mActionKeys.get(keyCode); 494 } 495 496 private void addActionKey(ActionKeyInfo keyInfo) { 497 if (mActionKeys == null) { 498 mActionKeys = new HashMap<Integer,ActionKeyInfo>(); 499 } 500 mActionKeys.put(keyInfo.getKeyCode(), keyInfo); 501 } 502 503 /** 504 * Gets search information for the given activity. 505 * 506 * @param context Context to use for reading activity resources. 507 * @param activityInfo Activity to get search information from. 508 * @return Search information about the given activity, or {@code null} if 509 * the activity has no or invalid searchability meta-data. 510 * 511 * @hide For use by SearchManagerService. 512 */ 513 public static SearchableInfo getActivityMetaData(Context context, ActivityInfo activityInfo) { 514 // for each component, try to find metadata 515 XmlResourceParser xml = 516 activityInfo.loadXmlMetaData(context.getPackageManager(), MD_LABEL_SEARCHABLE); 517 if (xml == null) { 518 return null; 519 } 520 ComponentName cName = new ComponentName(activityInfo.packageName, activityInfo.name); 521 522 SearchableInfo searchable = getActivityMetaData(context, xml, cName); 523 xml.close(); 524 525 if (DBG) { 526 if (searchable != null) { 527 Log.d(LOG_TAG, "Checked " + activityInfo.name 528 + ",label=" + searchable.getLabelId() 529 + ",icon=" + searchable.getIconId() 530 + ",suggestAuthority=" + searchable.getSuggestAuthority() 531 + ",target=" + searchable.getSearchActivity().getClassName() 532 + ",global=" + searchable.shouldIncludeInGlobalSearch() 533 + ",settingsDescription=" + searchable.getSettingsDescriptionId() 534 + ",threshold=" + searchable.getSuggestThreshold()); 535 } else { 536 Log.d(LOG_TAG, "Checked " + activityInfo.name + ", no searchable meta-data"); 537 } 538 } 539 return searchable; 540 } 541 542 /** 543 * Get the metadata for a given activity 544 * 545 * @param context runtime context 546 * @param xml XML parser for reading attributes 547 * @param cName The component name of the searchable activity 548 * 549 * @result A completely constructed SearchableInfo, or null if insufficient XML data for it 550 */ 551 private static SearchableInfo getActivityMetaData(Context context, XmlPullParser xml, 552 final ComponentName cName) { 553 SearchableInfo result = null; 554 Context activityContext = createActivityContext(context, cName); 555 if (activityContext == null) return null; 556 557 // in order to use the attributes mechanism, we have to walk the parser 558 // forward through the file until it's reading the tag of interest. 559 try { 560 int tagType = xml.next(); 561 while (tagType != XmlPullParser.END_DOCUMENT) { 562 if (tagType == XmlPullParser.START_TAG) { 563 if (xml.getName().equals(MD_XML_ELEMENT_SEARCHABLE)) { 564 AttributeSet attr = Xml.asAttributeSet(xml); 565 if (attr != null) { 566 try { 567 result = new SearchableInfo(activityContext, attr, cName); 568 } catch (IllegalArgumentException ex) { 569 Log.w(LOG_TAG, "Invalid searchable metadata for " + 570 cName.flattenToShortString() + ": " + ex.getMessage()); 571 return null; 572 } 573 } 574 } else if (xml.getName().equals(MD_XML_ELEMENT_SEARCHABLE_ACTION_KEY)) { 575 if (result == null) { 576 // Can't process an embedded element if we haven't seen the enclosing 577 return null; 578 } 579 AttributeSet attr = Xml.asAttributeSet(xml); 580 if (attr != null) { 581 try { 582 result.addActionKey(new ActionKeyInfo(activityContext, attr)); 583 } catch (IllegalArgumentException ex) { 584 Log.w(LOG_TAG, "Invalid action key for " + 585 cName.flattenToShortString() + ": " + ex.getMessage()); 586 return null; 587 } 588 } 589 } 590 } 591 tagType = xml.next(); 592 } 593 } catch (XmlPullParserException e) { 594 Log.w(LOG_TAG, "Reading searchable metadata for " + cName.flattenToShortString(), e); 595 return null; 596 } catch (IOException e) { 597 Log.w(LOG_TAG, "Reading searchable metadata for " + cName.flattenToShortString(), e); 598 return null; 599 } 600 601 return result; 602 } 603 604 /** 605 * Gets the "label" (user-visible name) of this searchable context. This must be 606 * read using the searchable Activity's resources. 607 * 608 * @return A resource id, or {@code 0} if no label was specified. 609 * @see android.R.styleable#Searchable_label 610 * 611 * @hide deprecated functionality 612 */ 613 public int getLabelId() { 614 return mLabelId; 615 } 616 617 /** 618 * Gets the resource id of the hint text. This must be 619 * read using the searchable Activity's resources. 620 * 621 * @return A resource id, or {@code 0} if no hint was specified. 622 * @see android.R.styleable#Searchable_hint 623 */ 624 public int getHintId() { 625 return mHintId; 626 } 627 628 /** 629 * Gets the icon id specified by the Searchable_icon meta-data entry. This must be 630 * read using the searchable Activity's resources. 631 * 632 * @return A resource id, or {@code 0} if no icon was specified. 633 * @see android.R.styleable#Searchable_icon 634 * 635 * @hide deprecated functionality 636 */ 637 public int getIconId() { 638 return mIconId; 639 } 640 641 /** 642 * Checks if the searchable activity wants the voice search button to be shown. 643 * 644 * @see android.R.styleable#Searchable_voiceSearchMode 645 */ 646 public boolean getVoiceSearchEnabled() { 647 return 0 != (mVoiceSearchMode & VOICE_SEARCH_SHOW_BUTTON); 648 } 649 650 /** 651 * Checks if voice search should start web search. 652 * 653 * @see android.R.styleable#Searchable_voiceSearchMode 654 */ 655 public boolean getVoiceSearchLaunchWebSearch() { 656 return 0 != (mVoiceSearchMode & VOICE_SEARCH_LAUNCH_WEB_SEARCH); 657 } 658 659 /** 660 * Checks if voice search should start in-app search. 661 * 662 * @see android.R.styleable#Searchable_voiceSearchMode 663 */ 664 public boolean getVoiceSearchLaunchRecognizer() { 665 return 0 != (mVoiceSearchMode & VOICE_SEARCH_LAUNCH_RECOGNIZER); 666 } 667 668 /** 669 * Gets the resource id of the voice search language model string. 670 * 671 * @return A resource id, or {@code 0} if no language model was specified. 672 * @see android.R.styleable#Searchable_voiceLanguageModel 673 */ 674 public int getVoiceLanguageModeId() { 675 return mVoiceLanguageModeId; 676 } 677 678 /** 679 * Gets the resource id of the voice prompt text string. 680 * 681 * @return A resource id, or {@code 0} if no voice prompt text was specified. 682 * @see android.R.styleable#Searchable_voicePromptText 683 */ 684 public int getVoicePromptTextId() { 685 return mVoicePromptTextId; 686 } 687 688 /** 689 * Gets the resource id of the spoken language to recognize in voice search. 690 * 691 * @return A resource id, or {@code 0} if no language was specified. 692 * @see android.R.styleable#Searchable_voiceLanguage 693 */ 694 public int getVoiceLanguageId() { 695 return mVoiceLanguageId; 696 } 697 698 /** 699 * The maximum number of voice recognition results to return. 700 * 701 * @return the max results count, if specified in the searchable 702 * activity's metadata, or {@code 0} if not specified. 703 * @see android.R.styleable#Searchable_voiceMaxResults 704 */ 705 public int getVoiceMaxResults() { 706 return mVoiceMaxResults; 707 } 708 709 /** 710 * Gets the resource id of replacement text for the "Search" button. 711 * 712 * @return A resource id, or {@code 0} if no replacement text was specified. 713 * @see android.R.styleable#Searchable_searchButtonText 714 * @hide This feature is deprecated, no need to add it to the API. 715 */ 716 public int getSearchButtonText() { 717 return mSearchButtonText; 718 } 719 720 /** 721 * Gets the input type as specified in the searchable attributes. This will default to 722 * {@link InputType#TYPE_CLASS_TEXT} if not specified (which is appropriate 723 * for free text input). 724 * 725 * @return the input type 726 * @see android.R.styleable#Searchable_inputType 727 */ 728 public int getInputType() { 729 return mSearchInputType; 730 } 731 732 /** 733 * Gets the input method options specified in the searchable attributes. 734 * This will default to {@link EditorInfo#IME_ACTION_GO} if not specified (which is 735 * appropriate for a search box). 736 * 737 * @return the input type 738 * @see android.R.styleable#Searchable_imeOptions 739 */ 740 public int getImeOptions() { 741 return mSearchImeOptions; 742 } 743 744 /** 745 * Checks whether the searchable should be included in global search. 746 * 747 * @return The value of the {@link android.R.styleable#Searchable_includeInGlobalSearch} 748 * attribute, or {@code false} if the attribute is not set. 749 * @see android.R.styleable#Searchable_includeInGlobalSearch 750 */ 751 public boolean shouldIncludeInGlobalSearch() { 752 return mIncludeInGlobalSearch; 753 } 754 755 /** 756 * Checks whether this searchable activity should be queried for suggestions if a prefix 757 * of the query has returned no results. 758 * 759 * @see android.R.styleable#Searchable_queryAfterZeroResults 760 */ 761 public boolean queryAfterZeroResults() { 762 return mQueryAfterZeroResults; 763 } 764 765 /** 766 * Checks whether this searchable activity has auto URL detection turned on. 767 * 768 * @see android.R.styleable#Searchable_autoUrlDetect 769 */ 770 public boolean autoUrlDetect() { 771 return mAutoUrlDetect; 772 } 773 774 /** 775 * Support for parcelable and aidl operations. 776 */ 777 public static final Parcelable.Creator<SearchableInfo> CREATOR 778 = new Parcelable.Creator<SearchableInfo>() { 779 public SearchableInfo createFromParcel(Parcel in) { 780 return new SearchableInfo(in); 781 } 782 783 public SearchableInfo[] newArray(int size) { 784 return new SearchableInfo[size]; 785 } 786 }; 787 788 /** 789 * Instantiates a new SearchableInfo from the data in a Parcel that was 790 * previously written with {@link #writeToParcel(Parcel, int)}. 791 * 792 * @param in The Parcel containing the previously written SearchableInfo, 793 * positioned at the location in the buffer where it was written. 794 */ 795 SearchableInfo(Parcel in) { 796 mLabelId = in.readInt(); 797 mSearchActivity = ComponentName.readFromParcel(in); 798 mHintId = in.readInt(); 799 mSearchMode = in.readInt(); 800 mIconId = in.readInt(); 801 mSearchButtonText = in.readInt(); 802 mSearchInputType = in.readInt(); 803 mSearchImeOptions = in.readInt(); 804 mIncludeInGlobalSearch = in.readInt() != 0; 805 mQueryAfterZeroResults = in.readInt() != 0; 806 mAutoUrlDetect = in.readInt() != 0; 807 808 mSettingsDescriptionId = in.readInt(); 809 mSuggestAuthority = in.readString(); 810 mSuggestPath = in.readString(); 811 mSuggestSelection = in.readString(); 812 mSuggestIntentAction = in.readString(); 813 mSuggestIntentData = in.readString(); 814 mSuggestThreshold = in.readInt(); 815 816 for (int count = in.readInt(); count > 0; count--) { 817 addActionKey(new ActionKeyInfo(in)); 818 } 819 820 mSuggestProviderPackage = in.readString(); 821 822 mVoiceSearchMode = in.readInt(); 823 mVoiceLanguageModeId = in.readInt(); 824 mVoicePromptTextId = in.readInt(); 825 mVoiceLanguageId = in.readInt(); 826 mVoiceMaxResults = in.readInt(); 827 } 828 829 public int describeContents() { 830 return 0; 831 } 832 833 public void writeToParcel(Parcel dest, int flags) { 834 dest.writeInt(mLabelId); 835 mSearchActivity.writeToParcel(dest, flags); 836 dest.writeInt(mHintId); 837 dest.writeInt(mSearchMode); 838 dest.writeInt(mIconId); 839 dest.writeInt(mSearchButtonText); 840 dest.writeInt(mSearchInputType); 841 dest.writeInt(mSearchImeOptions); 842 dest.writeInt(mIncludeInGlobalSearch ? 1 : 0); 843 dest.writeInt(mQueryAfterZeroResults ? 1 : 0); 844 dest.writeInt(mAutoUrlDetect ? 1 : 0); 845 846 dest.writeInt(mSettingsDescriptionId); 847 dest.writeString(mSuggestAuthority); 848 dest.writeString(mSuggestPath); 849 dest.writeString(mSuggestSelection); 850 dest.writeString(mSuggestIntentAction); 851 dest.writeString(mSuggestIntentData); 852 dest.writeInt(mSuggestThreshold); 853 854 if (mActionKeys == null) { 855 dest.writeInt(0); 856 } else { 857 dest.writeInt(mActionKeys.size()); 858 for (ActionKeyInfo actionKey : mActionKeys.values()) { 859 actionKey.writeToParcel(dest, flags); 860 } 861 } 862 863 dest.writeString(mSuggestProviderPackage); 864 865 dest.writeInt(mVoiceSearchMode); 866 dest.writeInt(mVoiceLanguageModeId); 867 dest.writeInt(mVoicePromptTextId); 868 dest.writeInt(mVoiceLanguageId); 869 dest.writeInt(mVoiceMaxResults); 870 } 871 } 872