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