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