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 */ getSuggestAuthority()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 */ getSuggestPackage()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 */ getSearchActivity()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 */ useBadgeLabel()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 */ useBadgeIcon()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 */ shouldRewriteQueryFromData()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 */ shouldRewriteQueryFromText()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 */ getSettingsDescriptionId()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 */ getSuggestPath()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 */ getSuggestSelection()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 */ getSuggestIntentAction()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 */ getSuggestIntentData()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 */ getSuggestThreshold()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 */ getActivityContext(Context context)252 public Context getActivityContext(Context context) { 253 return createActivityContext(context, mSearchActivity); 254 } 255 256 /** 257 * Creates a context for another activity. 258 */ createActivityContext(Context context, ComponentName activity)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 */ getProviderContext(Context context, Context activityContext)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 */ SearchableInfo(Context activityContext, AttributeSet attr, final ComponentName cName)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, 0); 367 if (pi != null) { 368 suggestProviderPackage = pi.packageName; 369 } 370 } 371 mSuggestProviderPackage = suggestProviderPackage; 372 373 // for now, implement some form of rules - minimal data 374 if (mLabelId == 0) { 375 throw new IllegalArgumentException("Search label must be a resource reference."); 376 } 377 } 378 379 /** 380 * Information about an action key in searchability meta-data. 381 * 382 * @see SearchableInfo#findActionKey(int) 383 * 384 * @hide This feature is used very little, and on many devices there are no reasonable 385 * keys to use for actions. 386 */ 387 public static class ActionKeyInfo implements Parcelable { 388 389 private final int mKeyCode; 390 private final String mQueryActionMsg; 391 private final String mSuggestActionMsg; 392 private final String mSuggestActionMsgColumn; 393 394 /** 395 * Create one object using attributeset as input data. 396 * @param activityContext runtime context of the activity that the action key information 397 * is about. 398 * @param attr The attribute set we found in the XML file, contains the values that are used to 399 * construct the object. 400 * @throws IllegalArgumentException if the action key configuration is invalid 401 */ ActionKeyInfo(Context activityContext, AttributeSet attr)402 ActionKeyInfo(Context activityContext, AttributeSet attr) { 403 TypedArray a = activityContext.obtainStyledAttributes(attr, 404 com.android.internal.R.styleable.SearchableActionKey); 405 406 mKeyCode = a.getInt( 407 com.android.internal.R.styleable.SearchableActionKey_keycode, 0); 408 mQueryActionMsg = a.getString( 409 com.android.internal.R.styleable.SearchableActionKey_queryActionMsg); 410 mSuggestActionMsg = a.getString( 411 com.android.internal.R.styleable.SearchableActionKey_suggestActionMsg); 412 mSuggestActionMsgColumn = a.getString( 413 com.android.internal.R.styleable.SearchableActionKey_suggestActionMsgColumn); 414 a.recycle(); 415 416 // sanity check. 417 if (mKeyCode == 0) { 418 throw new IllegalArgumentException("No keycode."); 419 } else if ((mQueryActionMsg == null) && 420 (mSuggestActionMsg == null) && 421 (mSuggestActionMsgColumn == null)) { 422 throw new IllegalArgumentException("No message information."); 423 } 424 } 425 426 /** 427 * Instantiate a new ActionKeyInfo from the data in a Parcel that was 428 * previously written with {@link #writeToParcel(Parcel, int)}. 429 * 430 * @param in The Parcel containing the previously written ActionKeyInfo, 431 * positioned at the location in the buffer where it was written. 432 */ ActionKeyInfo(Parcel in)433 private ActionKeyInfo(Parcel in) { 434 mKeyCode = in.readInt(); 435 mQueryActionMsg = in.readString(); 436 mSuggestActionMsg = in.readString(); 437 mSuggestActionMsgColumn = in.readString(); 438 } 439 440 /** 441 * Gets the key code that this action key info is for. 442 * @see android.R.styleable#SearchableActionKey_keycode 443 */ getKeyCode()444 public int getKeyCode() { 445 return mKeyCode; 446 } 447 448 /** 449 * Gets the action message to use for queries. 450 * @see android.R.styleable#SearchableActionKey_queryActionMsg 451 */ getQueryActionMsg()452 public String getQueryActionMsg() { 453 return mQueryActionMsg; 454 } 455 456 /** 457 * Gets the action message to use for suggestions. 458 * @see android.R.styleable#SearchableActionKey_suggestActionMsg 459 */ getSuggestActionMsg()460 public String getSuggestActionMsg() { 461 return mSuggestActionMsg; 462 } 463 464 /** 465 * Gets the name of the column to get the suggestion action message from. 466 * @see android.R.styleable#SearchableActionKey_suggestActionMsgColumn 467 */ getSuggestActionMsgColumn()468 public String getSuggestActionMsgColumn() { 469 return mSuggestActionMsgColumn; 470 } 471 describeContents()472 public int describeContents() { 473 return 0; 474 } 475 writeToParcel(Parcel dest, int flags)476 public void writeToParcel(Parcel dest, int flags) { 477 dest.writeInt(mKeyCode); 478 dest.writeString(mQueryActionMsg); 479 dest.writeString(mSuggestActionMsg); 480 dest.writeString(mSuggestActionMsgColumn); 481 } 482 } 483 484 /** 485 * If any action keys were defined for this searchable activity, look up and return. 486 * 487 * @param keyCode The key that was pressed 488 * @return Returns the action key info, or {@code null} if none defined. 489 * 490 * @hide ActionKeyInfo is hidden 491 */ findActionKey(int keyCode)492 public ActionKeyInfo findActionKey(int keyCode) { 493 if (mActionKeys == null) { 494 return null; 495 } 496 return mActionKeys.get(keyCode); 497 } 498 addActionKey(ActionKeyInfo keyInfo)499 private void addActionKey(ActionKeyInfo keyInfo) { 500 if (mActionKeys == null) { 501 mActionKeys = new HashMap<Integer,ActionKeyInfo>(); 502 } 503 mActionKeys.put(keyInfo.getKeyCode(), keyInfo); 504 } 505 506 /** 507 * Gets search information for the given activity. 508 * 509 * @param context Context to use for reading activity resources. 510 * @param activityInfo Activity to get search information from. 511 * @return Search information about the given activity, or {@code null} if 512 * the activity has no or invalid searchability meta-data. 513 * 514 * @hide For use by SearchManagerService. 515 */ getActivityMetaData(Context context, ActivityInfo activityInfo, int userId)516 public static SearchableInfo getActivityMetaData(Context context, ActivityInfo activityInfo, 517 int userId) { 518 Context userContext = null; 519 try { 520 userContext = context.createPackageContextAsUser("system", 0, 521 new UserHandle(userId)); 522 } catch (NameNotFoundException nnfe) { 523 Log.e(LOG_TAG, "Couldn't create package context for user " + userId); 524 return null; 525 } 526 // for each component, try to find metadata 527 XmlResourceParser xml = 528 activityInfo.loadXmlMetaData(userContext.getPackageManager(), MD_LABEL_SEARCHABLE); 529 if (xml == null) { 530 return null; 531 } 532 ComponentName cName = new ComponentName(activityInfo.packageName, activityInfo.name); 533 534 SearchableInfo searchable = getActivityMetaData(userContext, xml, cName); 535 xml.close(); 536 537 if (DBG) { 538 if (searchable != null) { 539 Log.d(LOG_TAG, "Checked " + activityInfo.name 540 + ",label=" + searchable.getLabelId() 541 + ",icon=" + searchable.getIconId() 542 + ",suggestAuthority=" + searchable.getSuggestAuthority() 543 + ",target=" + searchable.getSearchActivity().getClassName() 544 + ",global=" + searchable.shouldIncludeInGlobalSearch() 545 + ",settingsDescription=" + searchable.getSettingsDescriptionId() 546 + ",threshold=" + searchable.getSuggestThreshold()); 547 } else { 548 Log.d(LOG_TAG, "Checked " + activityInfo.name + ", no searchable meta-data"); 549 } 550 } 551 return searchable; 552 } 553 554 /** 555 * Get the metadata for a given activity 556 * 557 * @param context runtime context 558 * @param xml XML parser for reading attributes 559 * @param cName The component name of the searchable activity 560 * 561 * @result A completely constructed SearchableInfo, or null if insufficient XML data for it 562 */ getActivityMetaData(Context context, XmlPullParser xml, final ComponentName cName)563 private static SearchableInfo getActivityMetaData(Context context, XmlPullParser xml, 564 final ComponentName cName) { 565 SearchableInfo result = null; 566 Context activityContext = createActivityContext(context, cName); 567 if (activityContext == null) return null; 568 569 // in order to use the attributes mechanism, we have to walk the parser 570 // forward through the file until it's reading the tag of interest. 571 try { 572 int tagType = xml.next(); 573 while (tagType != XmlPullParser.END_DOCUMENT) { 574 if (tagType == XmlPullParser.START_TAG) { 575 if (xml.getName().equals(MD_XML_ELEMENT_SEARCHABLE)) { 576 AttributeSet attr = Xml.asAttributeSet(xml); 577 if (attr != null) { 578 try { 579 result = new SearchableInfo(activityContext, attr, cName); 580 } catch (IllegalArgumentException ex) { 581 Log.w(LOG_TAG, "Invalid searchable metadata for " + 582 cName.flattenToShortString() + ": " + ex.getMessage()); 583 return null; 584 } 585 } 586 } else if (xml.getName().equals(MD_XML_ELEMENT_SEARCHABLE_ACTION_KEY)) { 587 if (result == null) { 588 // Can't process an embedded element if we haven't seen the enclosing 589 return null; 590 } 591 AttributeSet attr = Xml.asAttributeSet(xml); 592 if (attr != null) { 593 try { 594 result.addActionKey(new ActionKeyInfo(activityContext, attr)); 595 } catch (IllegalArgumentException ex) { 596 Log.w(LOG_TAG, "Invalid action key for " + 597 cName.flattenToShortString() + ": " + ex.getMessage()); 598 return null; 599 } 600 } 601 } 602 } 603 tagType = xml.next(); 604 } 605 } catch (XmlPullParserException e) { 606 Log.w(LOG_TAG, "Reading searchable metadata for " + cName.flattenToShortString(), e); 607 return null; 608 } catch (IOException e) { 609 Log.w(LOG_TAG, "Reading searchable metadata for " + cName.flattenToShortString(), e); 610 return null; 611 } 612 613 return result; 614 } 615 616 /** 617 * Gets the "label" (user-visible name) of this searchable context. This must be 618 * read using the searchable Activity's resources. 619 * 620 * @return A resource id, or {@code 0} if no label was specified. 621 * @see android.R.styleable#Searchable_label 622 * 623 * @hide deprecated functionality 624 */ getLabelId()625 public int getLabelId() { 626 return mLabelId; 627 } 628 629 /** 630 * Gets the resource id of the hint text. This must be 631 * read using the searchable Activity's resources. 632 * 633 * @return A resource id, or {@code 0} if no hint was specified. 634 * @see android.R.styleable#Searchable_hint 635 */ getHintId()636 public int getHintId() { 637 return mHintId; 638 } 639 640 /** 641 * Gets the icon id specified by the Searchable_icon meta-data entry. This must be 642 * read using the searchable Activity's resources. 643 * 644 * @return A resource id, or {@code 0} if no icon was specified. 645 * @see android.R.styleable#Searchable_icon 646 * 647 * @hide deprecated functionality 648 */ getIconId()649 public int getIconId() { 650 return mIconId; 651 } 652 653 /** 654 * Checks if the searchable activity wants the voice search button to be shown. 655 * 656 * @see android.R.styleable#Searchable_voiceSearchMode 657 */ getVoiceSearchEnabled()658 public boolean getVoiceSearchEnabled() { 659 return 0 != (mVoiceSearchMode & VOICE_SEARCH_SHOW_BUTTON); 660 } 661 662 /** 663 * Checks if voice search should start web search. 664 * 665 * @see android.R.styleable#Searchable_voiceSearchMode 666 */ getVoiceSearchLaunchWebSearch()667 public boolean getVoiceSearchLaunchWebSearch() { 668 return 0 != (mVoiceSearchMode & VOICE_SEARCH_LAUNCH_WEB_SEARCH); 669 } 670 671 /** 672 * Checks if voice search should start in-app search. 673 * 674 * @see android.R.styleable#Searchable_voiceSearchMode 675 */ getVoiceSearchLaunchRecognizer()676 public boolean getVoiceSearchLaunchRecognizer() { 677 return 0 != (mVoiceSearchMode & VOICE_SEARCH_LAUNCH_RECOGNIZER); 678 } 679 680 /** 681 * Gets the resource id of the voice search language model string. 682 * 683 * @return A resource id, or {@code 0} if no language model was specified. 684 * @see android.R.styleable#Searchable_voiceLanguageModel 685 */ 686 @StringRes getVoiceLanguageModeId()687 public int getVoiceLanguageModeId() { 688 return mVoiceLanguageModeId; 689 } 690 691 /** 692 * Gets the resource id of the voice prompt text string. 693 * 694 * @return A resource id, or {@code 0} if no voice prompt text was specified. 695 * @see android.R.styleable#Searchable_voicePromptText 696 */ 697 @StringRes getVoicePromptTextId()698 public int getVoicePromptTextId() { 699 return mVoicePromptTextId; 700 } 701 702 /** 703 * Gets the resource id of the spoken language to recognize in voice search. 704 * 705 * @return A resource id, or {@code 0} if no language was specified. 706 * @see android.R.styleable#Searchable_voiceLanguage 707 */ 708 @StringRes getVoiceLanguageId()709 public int getVoiceLanguageId() { 710 return mVoiceLanguageId; 711 } 712 713 /** 714 * The maximum number of voice recognition results to return. 715 * 716 * @return the max results count, if specified in the searchable 717 * activity's metadata, or {@code 0} if not specified. 718 * @see android.R.styleable#Searchable_voiceMaxResults 719 */ getVoiceMaxResults()720 public int getVoiceMaxResults() { 721 return mVoiceMaxResults; 722 } 723 724 /** 725 * Gets the resource id of replacement text for the "Search" button. 726 * 727 * @return A resource id, or {@code 0} if no replacement text was specified. 728 * @see android.R.styleable#Searchable_searchButtonText 729 * @hide This feature is deprecated, no need to add it to the API. 730 */ getSearchButtonText()731 public int getSearchButtonText() { 732 return mSearchButtonText; 733 } 734 735 /** 736 * Gets the input type as specified in the searchable attributes. This will default to 737 * {@link InputType#TYPE_CLASS_TEXT} if not specified (which is appropriate 738 * for free text input). 739 * 740 * @return the input type 741 * @see android.R.styleable#Searchable_inputType 742 */ getInputType()743 public int getInputType() { 744 return mSearchInputType; 745 } 746 747 /** 748 * Gets the input method options specified in the searchable attributes. 749 * This will default to {@link EditorInfo#IME_ACTION_GO} if not specified (which is 750 * appropriate for a search box). 751 * 752 * @return the input type 753 * @see android.R.styleable#Searchable_imeOptions 754 */ getImeOptions()755 public int getImeOptions() { 756 return mSearchImeOptions; 757 } 758 759 /** 760 * Checks whether the searchable should be included in global search. 761 * 762 * @return The value of the {@link android.R.styleable#Searchable_includeInGlobalSearch} 763 * attribute, or {@code false} if the attribute is not set. 764 * @see android.R.styleable#Searchable_includeInGlobalSearch 765 */ shouldIncludeInGlobalSearch()766 public boolean shouldIncludeInGlobalSearch() { 767 return mIncludeInGlobalSearch; 768 } 769 770 /** 771 * Checks whether this searchable activity should be queried for suggestions if a prefix 772 * of the query has returned no results. 773 * 774 * @see android.R.styleable#Searchable_queryAfterZeroResults 775 */ queryAfterZeroResults()776 public boolean queryAfterZeroResults() { 777 return mQueryAfterZeroResults; 778 } 779 780 /** 781 * Checks whether this searchable activity has auto URL detection turned on. 782 * 783 * @see android.R.styleable#Searchable_autoUrlDetect 784 */ autoUrlDetect()785 public boolean autoUrlDetect() { 786 return mAutoUrlDetect; 787 } 788 789 /** 790 * Support for parcelable and aidl operations. 791 */ 792 public static final Parcelable.Creator<SearchableInfo> CREATOR 793 = new Parcelable.Creator<SearchableInfo>() { 794 public SearchableInfo createFromParcel(Parcel in) { 795 return new SearchableInfo(in); 796 } 797 798 public SearchableInfo[] newArray(int size) { 799 return new SearchableInfo[size]; 800 } 801 }; 802 803 /** 804 * Instantiates a new SearchableInfo from the data in a Parcel that was 805 * previously written with {@link #writeToParcel(Parcel, int)}. 806 * 807 * @param in The Parcel containing the previously written SearchableInfo, 808 * positioned at the location in the buffer where it was written. 809 */ SearchableInfo(Parcel in)810 SearchableInfo(Parcel in) { 811 mLabelId = in.readInt(); 812 mSearchActivity = ComponentName.readFromParcel(in); 813 mHintId = in.readInt(); 814 mSearchMode = in.readInt(); 815 mIconId = in.readInt(); 816 mSearchButtonText = in.readInt(); 817 mSearchInputType = in.readInt(); 818 mSearchImeOptions = in.readInt(); 819 mIncludeInGlobalSearch = in.readInt() != 0; 820 mQueryAfterZeroResults = in.readInt() != 0; 821 mAutoUrlDetect = in.readInt() != 0; 822 823 mSettingsDescriptionId = in.readInt(); 824 mSuggestAuthority = in.readString(); 825 mSuggestPath = in.readString(); 826 mSuggestSelection = in.readString(); 827 mSuggestIntentAction = in.readString(); 828 mSuggestIntentData = in.readString(); 829 mSuggestThreshold = in.readInt(); 830 831 for (int count = in.readInt(); count > 0; count--) { 832 addActionKey(new ActionKeyInfo(in)); 833 } 834 835 mSuggestProviderPackage = in.readString(); 836 837 mVoiceSearchMode = in.readInt(); 838 mVoiceLanguageModeId = in.readInt(); 839 mVoicePromptTextId = in.readInt(); 840 mVoiceLanguageId = in.readInt(); 841 mVoiceMaxResults = in.readInt(); 842 } 843 describeContents()844 public int describeContents() { 845 return 0; 846 } 847 writeToParcel(Parcel dest, int flags)848 public void writeToParcel(Parcel dest, int flags) { 849 dest.writeInt(mLabelId); 850 mSearchActivity.writeToParcel(dest, flags); 851 dest.writeInt(mHintId); 852 dest.writeInt(mSearchMode); 853 dest.writeInt(mIconId); 854 dest.writeInt(mSearchButtonText); 855 dest.writeInt(mSearchInputType); 856 dest.writeInt(mSearchImeOptions); 857 dest.writeInt(mIncludeInGlobalSearch ? 1 : 0); 858 dest.writeInt(mQueryAfterZeroResults ? 1 : 0); 859 dest.writeInt(mAutoUrlDetect ? 1 : 0); 860 861 dest.writeInt(mSettingsDescriptionId); 862 dest.writeString(mSuggestAuthority); 863 dest.writeString(mSuggestPath); 864 dest.writeString(mSuggestSelection); 865 dest.writeString(mSuggestIntentAction); 866 dest.writeString(mSuggestIntentData); 867 dest.writeInt(mSuggestThreshold); 868 869 if (mActionKeys == null) { 870 dest.writeInt(0); 871 } else { 872 dest.writeInt(mActionKeys.size()); 873 for (ActionKeyInfo actionKey : mActionKeys.values()) { 874 actionKey.writeToParcel(dest, flags); 875 } 876 } 877 878 dest.writeString(mSuggestProviderPackage); 879 880 dest.writeInt(mVoiceSearchMode); 881 dest.writeInt(mVoiceLanguageModeId); 882 dest.writeInt(mVoicePromptTextId); 883 dest.writeInt(mVoiceLanguageId); 884 dest.writeInt(mVoiceMaxResults); 885 } 886 } 887