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