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