• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 android.content.ComponentName;
20 import android.content.ContentResolver;
21 import android.content.Context;
22 import android.content.DialogInterface;
23 import android.database.Cursor;
24 import android.net.Uri;
25 import android.os.Bundle;
26 import android.os.Handler;
27 import android.os.RemoteException;
28 import android.os.ServiceManager;
29 import android.server.search.SearchableInfo;
30 import android.text.TextUtils;
31 import android.util.Log;
32 import android.view.KeyEvent;
33 
34 import java.util.List;
35 
36 /**
37  * This class provides access to the system search services.
38  *
39  * <p>In practice, you won't interact with this class directly, as search
40  * services are provided through methods in {@link android.app.Activity Activity}
41  * methods and the the {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH}
42  * {@link android.content.Intent Intent}.  This class does provide a basic
43  * overview of search services and how to integrate them with your activities.
44  * If you do require direct access to the SearchManager, do not instantiate
45  * this class directly; instead, retrieve it through
46  * {@link android.content.Context#getSystemService
47  * context.getSystemService(Context.SEARCH_SERVICE)}.
48  *
49  * <p>Topics covered here:
50  * <ol>
51  * <li><a href="#DeveloperGuide">Developer Guide</a>
52  * <li><a href="#HowSearchIsInvoked">How Search Is Invoked</a>
53  * <li><a href="#ImplementingSearchForYourApp">Implementing Search for Your App</a>
54  * <li><a href="#Suggestions">Search Suggestions</a>
55  * <li><a href="#ExposingSearchSuggestionsToQuickSearchBox">Exposing Search Suggestions to
56  * Quick Search Box</a></li>
57  * <li><a href="#ActionKeys">Action Keys</a>
58  * <li><a href="#SearchabilityMetadata">Searchability Metadata</a>
59  * <li><a href="#PassingSearchContext">Passing Search Context</a>
60  * <li><a href="#ProtectingUserPrivacy">Protecting User Privacy</a>
61  * </ol>
62  *
63  * <a name="DeveloperGuide"></a>
64  * <h3>Developer Guide</h3>
65  *
66  * <p>The ability to search for user, system, or network based data is considered to be
67  * a core user-level feature of the Android platform.  At any time, the user should be
68  * able to use a familiar command, button, or keystroke to invoke search, and the user
69  * should be able to search any data which is available to them.
70  *
71  * <p>To make search appear to the user as a seamless system-wide feature, the application
72  * framework centrally controls it, offering APIs to individual applications to control how they
73  * are searched. Applications can customize how search is invoked, how the search dialog looks,
74  * and what type of search results are available, including suggestions that are available as the
75  * user types.
76  *
77  * <p>Even applications which are not searchable will by default support the invocation of
78  * search to trigger Quick Search Box, the system's 'global search'.
79  *
80  * <a name="HowSearchIsInvoked"></a>
81  * <h3>How Search Is Invoked</h3>
82  *
83  * <p>Unless impossible or inapplicable, all applications should support
84  * invoking the search UI.  This means that when the user invokes the search command,
85  * a search UI will be presented to them.  The search command is currently defined as a menu
86  * item called "Search" (with an alphabetic shortcut key of "S"), or on many devices, a dedicated
87  * search button key.
88  * <p>If your application is not inherently searchable, the default implementation will cause
89  * the search UI to be invoked in a "global search" mode known as Quick Search Box.  As the user
90  * types, search suggestions from across the device and the web will be surfaced, and if they
91  * click the "Search" button, this will bring the browser to the front and will launch a web-based
92  * search.  The user will be able to click the "Back" button and return to your application.
93  * <p>In general this is implemented by your activity, or the {@link android.app.Activity Activity}
94  * base class, which captures the search command and invokes the SearchManager to
95  * display and operate the search UI.  You can also cause the search UI to be presented in response
96  * to user keystrokes in your activity (for example, to instantly start filter searching while
97  * viewing a list and typing any key).
98  * <p>The search UI is presented as a floating
99  * window and does not cause any change in the activity stack.  If the user
100  * cancels search, the previous activity re-emerges.  If the user launches a
101  * search, this will be done by sending a search {@link android.content.Intent Intent} (see below),
102  * and the normal intent-handling sequence will take place (your activity will pause,
103  * etc.)
104  * <p><b>What you need to do:</b> First, you should consider the way in which you want to
105  * handle invoking search.  There are four broad (and partially overlapping) categories for
106  * you to choose from.
107  * <ul><li>You can capture the search command yourself, by including a <i>search</i>
108  * button or menu item - and invoking the search UI directly.</li>
109  * <li>You can provide a <i>type-to-search</i> feature, in which search is invoked automatically
110  * when the user enters any characters.</li>
111  * <li>Even if your application is not inherently searchable, you can allow global search,
112  * via the search key (or even via a search menu item).
113  * <li>You can disable search entirely.  This should only be used in very rare circumstances,
114  * as search is a system-wide feature and users will expect it to be available in all contexts.</li>
115  * </ul>
116  *
117  * <p><b>How to define a search menu.</b>  The system provides the following resources which may
118  * be useful when adding a search item to your menu:
119  * <ul><li>android.R.drawable.ic_search_category_default is an icon you can use in your menu.</li>
120  * <li>{@link #MENU_KEY SearchManager.MENU_KEY} is the recommended alphabetic shortcut.</li>
121  * </ul>
122  *
123  * <p><b>How to invoke search directly.</b>  In order to invoke search directly, from a button
124  * or menu item, you can launch a generic search by calling
125  * {@link android.app.Activity#onSearchRequested onSearchRequested} as shown:
126  * <pre class="prettyprint">
127  * onSearchRequested();</pre>
128  *
129  * <p><b>How to implement type-to-search.</b>  While setting up your activity, call
130  * {@link android.app.Activity#setDefaultKeyMode setDefaultKeyMode}:
131  * <pre class="prettyprint">
132  * setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);   // search within your activity
133  * setDefaultKeyMode(DEFAULT_KEYS_SEARCH_GLOBAL);  // search using platform global search</pre>
134  *
135  * <p><b>How to enable global search with Quick Search Box.</b>  In addition to searching within
136  * your activity or application, you can also use the Search Manager to invoke a platform-global
137  * search, which uses Quick Search Box to search across the device and the web. There are two ways
138  * to do this:
139  * <ul><li>You can simply define "search" within your application or activity to mean global search.
140  * This is described in more detail in the
141  * <a href="#SearchabilityMetadata">Searchability Metadata</a> section.  Briefly, you will
142  * add a single meta-data entry to your manifest, declaring that the default search
143  * for your application is "*".  This indicates to the system that no application-specific
144  * search activity is provided, and that it should launch web-based search instead.</li>
145  * <li>Simply do nothing and the default implementation of
146  * {@link android.app.Activity#onSearchRequested} will cause global search to be triggered.
147  * (You can also always trigger search via a direct call to {@link android.app.Activity#startSearch}.
148  * This is most useful if you wish to provide local searchability <i>and</i> access to global
149  * search.)</li></ul>
150  *
151  * <p><b>How to disable search from your activity.</b> Search is a system-wide feature and users
152  * will expect it to be available in all contexts.  If your UI design absolutely precludes
153  * launching search, override {@link android.app.Activity#onSearchRequested onSearchRequested}
154  * as shown:
155  * <pre class="prettyprint">
156  * &#64;Override
157  * public boolean onSearchRequested() {
158  *    return false;
159  * }</pre>
160  *
161  * <p><b>Managing focus and knowing if search is active.</b>  The search UI is not a separate
162  * activity, and when the UI is invoked or dismissed, your activity will not typically be paused,
163  * resumed, or otherwise notified by the methods defined in
164  * <a href="{@docRoot}guide/topics/fundamentals.html#actlife">Application Fundamentals:
165  * Activity Lifecycle</a>.  The search UI is
166  * handled in the same way as other system UI elements which may appear from time to time, such as
167  * notifications, screen locks, or other system alerts:
168  * <p>When the search UI appears, your activity will lose input focus.
169  * <p>When the search activity is dismissed, there are three possible outcomes:
170  * <ul><li>If the user simply canceled the search UI, your activity will regain input focus and
171  * proceed as before.  See {@link #setOnDismissListener} and {@link #setOnCancelListener} if you
172  * required direct notification of search dialog dismissals.</li>
173  * <li>If the user launched a search, and this required switching to another activity to receive
174  * and process the search {@link android.content.Intent Intent}, your activity will receive the
175  * normal sequence of activity pause or stop notifications.</li>
176  * <li>If the user launched a search, and the current activity is the recipient of the search
177  * {@link android.content.Intent Intent}, you will receive notification via the
178  * {@link android.app.Activity#onNewIntent onNewIntent()} method.</li></ul>
179  * <p>This list is provided in order to clarify the ways in which your activities will interact with
180  * the search UI.  More details on searchable activities and search intents are provided in the
181  * sections below.
182  *
183  * <a name="ImplementingSearchForYourApp"></a>
184  * <h3>Implementing Search for Your App</h3>
185  *
186  * <p>The following steps are necessary in order to implement search.
187  * <ul>
188  * <li>Implement search invocation as described above.  (Strictly speaking,
189  * these are decoupled, but it would make little sense to be "searchable" but not
190  * "search-invoking".)</li>
191  * <li>Your application should have an activity that takes a search string and
192  * converts it to a list of results.  This could be your primary display activity
193  * or it could be a dedicated search results activity.  This is your <i>searchable</i>
194  * activity and every query-search application must have one.</li>
195  * <li>In the searchable activity, in onCreate(), you must receive and handle the
196  * {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH}
197  * {@link android.content.Intent Intent}.  The text to search (query string) for is provided by
198  * calling
199  * {@link #QUERY getStringExtra(SearchManager.QUERY)}.</li>
200  * <li>To identify and support your searchable activity, you'll need to
201  * provide an XML file providing searchability configuration parameters, a reference to that
202  * in your searchable activity's
203  * <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">manifest</a> entry, and an
204  * intent-filter declaring that you can receive ACTION_SEARCH intents. This is described in more
205  * detail in the <a href="#SearchabilityMetadata">Searchability Metadata</a> section.</li>
206  * <li>Your <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">manifest</a> also needs a
207  * metadata entry providing a global reference to the searchable activity. This is the "glue"
208  * directing the search UI, when invoked from any of your <i>other</i> activities, to use your
209  * application as the default search context.  This is also described in more detail in the
210  * <a href="#SearchabilityMetadata">Searchability Metadata</a> section.</li>
211  * <li>Finally, you may want to define your search results activity as single-top with the
212  * {@link android.R.attr#launchMode singleTop} launchMode flag.  This allows the system
213  * to launch searches from/to the same activity without creating a pile of them on the
214  * activity stack.  If you do this, be sure to also override
215  * {@link android.app.Activity#onNewIntent onNewIntent} to handle the
216  * updated intents (with new queries) as they arrive.</li>
217  * </ul>
218  *
219  * <p>Code snippet showing handling of intents in your search activity:
220  * <pre class="prettyprint">
221  * &#64;Override
222  * protected void onCreate(Bundle icicle) {
223  *     super.onCreate(icicle);
224  *
225  *     final Intent queryIntent = getIntent();
226  *     final String queryAction = queryIntent.getAction();
227  *     if (Intent.ACTION_SEARCH.equals(queryAction)) {
228  *         doSearchWithIntent(queryIntent);
229  *     }
230  * }
231  *
232  * private void doSearchWithIntent(final Intent queryIntent) {
233  *     final String queryString = queryIntent.getStringExtra(SearchManager.QUERY);
234  *     doSearchWithQuery(queryString);
235  * }</pre>
236  *
237  * <a name="Suggestions"></a>
238  * <h3>Search Suggestions</h3>
239  *
240  * <p>A powerful feature of the search system is the ability of any application to easily provide
241  * live "suggestions" in order to prompt the user.  Each application implements suggestions in a
242  * different, unique, and appropriate way.  Suggestions be drawn from many sources, including but
243  * not limited to:
244  * <ul>
245  * <li>Actual searchable results (e.g. names in the address book)</li>
246  * <li>Recently entered queries</li>
247  * <li>Recently viewed data or results</li>
248  * <li>Contextually appropriate queries or results</li>
249  * <li>Summaries of possible results</li>
250  * </ul>
251  *
252  * <p>Once an application is configured to provide search suggestions, those same suggestions can
253  * easily be made available to the system-wide Quick Search Box, providing faster access to its
254  * content from one central prominent place. See
255  * <a href="#ExposingSearchSuggestionsToQuickSearchBox">Exposing Search Suggestions to Quick Search
256  * Box</a> for more details.
257  *
258  * <p>The primary form of suggestions is known as <i>queried suggestions</i> and is based on query
259  * text that the user has already typed.  This would generally be based on partial matches in
260  * the available data.  In certain situations - for example, when no query text has been typed yet -
261  * an application may also opt to provide <i>zero-query suggestions</i>.
262  * These would typically be drawn from the same data source, but because no partial query text is
263  * available, they should be weighted based on other factors - for example, most recent queries
264  * or most recent results.
265  *
266  * <p><b>Overview of how suggestions are provided.</b>  Suggestions are accessed via a
267  * {@link android.content.ContentProvider Content Provider}. When the search manager identifies a
268  * particular activity as searchable, it will check for certain metadata which indicates that
269  * there is also a source of suggestions.  If suggestions are provided, the following steps are
270  * taken.
271  * <ul><li>Using formatting information found in the metadata, the user's query text (whatever
272  * has been typed so far) will be formatted into a query and sent to the suggestions
273  * {@link android.content.ContentProvider Content Provider}.</li>
274  * <li>The suggestions {@link android.content.ContentProvider Content Provider} will create a
275  * {@link android.database.Cursor Cursor} which can iterate over the possible suggestions.</li>
276  * <li>The search manager will populate a list using display data found in each row of the cursor,
277  * and display these suggestions to the user.</li>
278  * <li>If the user types another key, or changes the query in any way, the above steps are repeated
279  * and the suggestions list is updated or repopulated.</li>
280  * <li>If the user clicks or touches the "GO" button, the suggestions are ignored and the search is
281  * launched using the normal {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} type of
282  * {@link android.content.Intent Intent}.</li>
283  * <li>If the user uses the directional controls to navigate the focus into the suggestions list,
284  * the query text will be updated while the user navigates from suggestion to suggestion.  The user
285  * can then click or touch the updated query and edit it further.  If the user navigates back to
286  * the edit field, the original typed query is restored.</li>
287  * <li>If the user clicks or touches a particular suggestion, then a combination of data from the
288  * cursor and
289  * values found in the metadata are used to synthesize an Intent and send it to the application.
290  * Depending on the design of the activity and the way it implements search, this might be a
291  * {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} (in order to launch a query), or it
292  * might be a {@link android.content.Intent#ACTION_VIEW ACTION_VIEW}, in order to proceed directly
293  * to display of specific data.</li>
294  * </ul>
295  *
296  * <p><b>Simple Recent-Query-Based Suggestions.</b>  The Android framework provides a simple Search
297  * Suggestions provider, which simply records and replays recent queries.  For many applications,
298  * this will be sufficient.  The basic steps you will need to
299  * do, in order to use the built-in recent queries suggestions provider, are as follows:
300  * <ul>
301  * <li>Implement and test query search, as described in the previous sections.</li>
302  * <li>Create a Provider within your application by extending
303  * {@link android.content.SearchRecentSuggestionsProvider}.</li>
304  * <li>Create a manifest entry describing your provider.</li>
305  * <li>Update your searchable activity's XML configuration file with information about your
306  * provider.</li>
307  * <li>In your searchable activities, capture any user-generated queries and record them
308  * for future searches by calling {@link android.provider.SearchRecentSuggestions#saveRecentQuery}.
309  * </li>
310  * </ul>
311  * <p>For complete implementation details, please refer to
312  * {@link android.content.SearchRecentSuggestionsProvider}.  The rest of the information in this
313  * section should not be necessary, as it refers to custom suggestions providers.
314  *
315  * <p><b>Creating a Customized Suggestions Provider:</b>  In order to create more sophisticated
316  * suggestion providers, you'll need to take the following steps:
317  * <ul>
318  * <li>Implement and test query search, as described in the previous sections.</li>
319  * <li>Decide how you wish to <i>receive</i> suggestions.  Just like queries that the user enters,
320  * suggestions will be delivered to your searchable activity as
321  * {@link android.content.Intent Intent} messages;  Unlike simple queries, you have quite a bit of
322  * flexibility in forming those intents.  A query search application will probably
323  * wish to continue receiving the {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH}
324  * {@link android.content.Intent Intent}, which will launch a query search using query text as
325  * provided by the suggestion.  A filter search application will probably wish to
326  * receive the {@link android.content.Intent#ACTION_VIEW ACTION_VIEW}
327  * {@link android.content.Intent Intent}, which will take the user directly to a selected entry.
328  * Other interesting suggestions, including hybrids, are possible, and the suggestion provider
329  * can easily mix-and-match results to provide a richer set of suggestions for the user.  Finally,
330  * you'll need to update your searchable activity (or other activities) to receive the intents
331  * as you've defined them.</li>
332  * <li>Implement a Content Provider that provides suggestions.  If you already have one, and it
333  * has access to your suggestions data, you can use that provider. If not, you'll have to create
334  * one. You'll also provide information about your Content Provider in your
335  * package's <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">manifest</a>.</li>
336  * <li>Update your searchable activity's XML configuration file.  There are two categories of
337  * information used for suggestions:
338  * <ul><li>The first is (required) data that the search manager will
339  * use to format the queries which are sent to the Content Provider.</li>
340  * <li>The second is (optional) parameters to configure structure
341  * if intents generated by suggestions.</li></li>
342  * </ul>
343  * </ul>
344  *
345  * <p><b>Configuring your Content Provider to Receive Suggestion Queries.</b>  The basic job of
346  * a search suggestions {@link android.content.ContentProvider Content Provider} is to provide
347  * "live" (while-you-type) conversion of the user's query text into a set of zero or more
348  * suggestions.  Each application is free to define the conversion, and as described above there are
349  * many possible solutions.  This section simply defines how to communicate with the suggestion
350  * provider.
351  *
352  * <p>The Search Manager must first determine if your package provides suggestions.  This is done
353  * by examination of your searchable meta-data XML file.  The android:searchSuggestAuthority
354  * attribute, if provided, is the signal to obtain & display suggestions.
355  *
356  * <p>Every query includes a Uri, and the Search Manager will format the Uri as shown:
357  * <p><pre class="prettyprint">
358  * content:// your.suggest.authority / your.suggest.path / SearchManager.SUGGEST_URI_PATH_QUERY</pre>
359  *
360  * <p>Your Content Provider can receive the query text in one of two ways.
361  * <ul>
362  * <li><b>Query provided as a selection argument.</b>  If you define the attribute value
363  * android:searchSuggestSelection and include a string, this string will be passed as the
364  * <i>selection</i> parameter to your Content Provider's query function.  You must define a single
365  * selection argument, using the '?' character.  The user's query text will be passed to you
366  * as the first element of the selection arguments array.</li>
367  * <li><b>Query provided with Data Uri.</b>  If you <i>do not</i> define the attribute value
368  * android:searchSuggestSelection, then the Search Manager will append another "/" followed by
369  * the user's query to the query Uri.  The query will be encoding using Uri encoding rules - don't
370  * forget to decode it.  (See {@link android.net.Uri#getPathSegments} and
371  * {@link android.net.Uri#getLastPathSegment} for helpful utilities you can use here.)</li>
372  * </ul>
373  *
374  * <p><b>Providing access to Content Providers that require permissions.</b>  If your content
375  * provider declares an android:readPermission in your application's manifest, you must provide
376  * access to the search infrastructure to the search suggestion path by including a path-permission
377  * that grants android:readPermission access to "android.permission.GLOBAL_SEARCH".  Granting access
378  * explicitly to the search infrastructure ensures it will be able to access the search suggestions
379  * without needing to know ahead of time any other details of the permissions protecting your
380  * provider.  Content providers that require no permissions are already available to the search
381  * infrastructure.  Here is an example of a provider that protects access to it with permissions,
382  * and provides read access to the search infrastructure to the path that it expects to receive the
383  * suggestion query on:
384  * <pre class="prettyprint">
385  * &lt;provider android:name="MyProvider" android:authorities="myprovider"
386  *        android:readPermission="android.permission.READ_MY_DATA"
387  *        android:writePermission="android.permission.WRITE_MY_DATA"&gt;
388  *    &lt;path-permission android:path="/search_suggest_query"
389  *            android:readPermission="android.permission.GLOBAL_SEARCH" /&gt;
390  * &lt;/provider&gt;
391  * </pre>
392  *
393  * <p><b>Handling empty queries.</b>  Your application should handle the "empty query"
394  * (no user text entered) case properly, and generate useful suggestions in this case.  There are a
395  * number of ways to do this;  Two are outlined here:
396  * <ul><li>For a simple filter search of local data, you could simply present the entire dataset,
397  * unfiltered.  (example: People)</li>
398  * <li>For a query search, you could simply present the most recent queries.  This allows the user
399  * to quickly repeat a recent search.</li></ul>
400  *
401  * <p><b>The Format of Individual Suggestions.</b>  Your suggestions are communicated back to the
402  * Search Manager by way of a {@link android.database.Cursor Cursor}.  The Search Manager will
403  * usually pass a null Projection, which means that your provider can simply return all appropriate
404  * columns for each suggestion.  The columns currently defined are:
405  *
406  * <table border="2" width="85%" align="center" frame="hsides" rules="rows">
407  *
408  *     <thead>
409  *     <tr><th>Column Name</th> <th>Description</th> <th>Required?</th></tr>
410  *     </thead>
411  *
412  *     <tbody>
413  *     <tr><th>{@link #SUGGEST_COLUMN_FORMAT}</th>
414  *         <td><i>Unused - can be null.</i></td>
415  *         <td align="center">No</td>
416  *     </tr>
417  *
418  *     <tr><th>{@link #SUGGEST_COLUMN_TEXT_1}</th>
419  *         <td>This is the line of text that will be presented to the user as the suggestion.</td>
420  *         <td align="center">Yes</td>
421  *     </tr>
422  *
423  *     <tr><th>{@link #SUGGEST_COLUMN_TEXT_2}</th>
424  *         <td>If your cursor includes this column, then all suggestions will be provided in a
425  *             two-line format.  The data in this column will be displayed as a second, smaller
426  *             line of text below the primary suggestion, or it can be null or empty to indicate no
427  *             text in this row's suggestion.</td>
428  *         <td align="center">No</td>
429  *     </tr>
430  *
431  *     <tr><th>{@link #SUGGEST_COLUMN_ICON_1}</th>
432  *         <td>If your cursor includes this column, then all suggestions will be provided in an
433  *             icons+text format.  This value should be a reference to the icon to
434  *             draw on the left side, or it can be null or zero to indicate no icon in this row.
435  *             </td>
436  *         <td align="center">No.</td>
437  *     </tr>
438  *
439  *     <tr><th>{@link #SUGGEST_COLUMN_ICON_2}</th>
440  *         <td>If your cursor includes this column, then all suggestions will be provided in an
441  *             icons+text format.  This value should be a reference to the icon to
442  *             draw on the right side, or it can be null or zero to indicate no icon in this row.
443  *             </td>
444  *         <td align="center">No.</td>
445  *     </tr>
446  *
447  *     <tr><th>{@link #SUGGEST_COLUMN_INTENT_ACTION}</th>
448  *         <td>If this column exists <i>and</i> this element exists at the given row, this is the
449  *             action that will be used when forming the suggestion's intent.  If the element is
450  *             not provided, the action will be taken from the android:searchSuggestIntentAction
451  *             field in your XML metadata.  <i>At least one of these must be present for the
452  *             suggestion to generate an intent.</i>  Note:  If your action is the same for all
453  *             suggestions, it is more efficient to specify it using XML metadata and omit it from
454  *             the cursor.</td>
455  *         <td align="center">No</td>
456  *     </tr>
457  *
458  *     <tr><th>{@link #SUGGEST_COLUMN_INTENT_DATA}</th>
459  *         <td>If this column exists <i>and</i> this element exists at the given row, this is the
460  *             data that will be used when forming the suggestion's intent.  If the element is not
461  *             provided, the data will be taken from the android:searchSuggestIntentData field in
462  *             your XML metadata.  If neither source is provided, the Intent's data field will be
463  *             null.  Note:  If your data is the same for all suggestions, or can be described
464  *             using a constant part and a specific ID, it is more efficient to specify it using
465  *             XML metadata and omit it from the cursor.</td>
466  *         <td align="center">No</td>
467  *     </tr>
468  *
469  *     <tr><th>{@link #SUGGEST_COLUMN_INTENT_DATA_ID}</th>
470  *         <td>If this column exists <i>and</i> this element exists at the given row, then "/" and
471  *             this value will be appended to the data field in the Intent.  This should only be
472  *             used if the data field has already been set to an appropriate base string.</td>
473  *         <td align="center">No</td>
474  *     </tr>
475  *
476  *     <tr><th>{@link #SUGGEST_COLUMN_INTENT_EXTRA_DATA}</th>
477  *         <td>If this column exists <i>and</i> this element exists at a given row, this is the
478  *             data that will be used when forming the suggestion's intent.  If not provided,
479  *             the Intent's extra data field will be null.  This column allows suggestions to
480  *             provide additional arbitrary data which will be included as an extra under the
481  *             key {@link #EXTRA_DATA_KEY}.</td>
482  *         <td align="center">No.</td>
483  *     </tr>
484  *
485  *     <tr><th>{@link #SUGGEST_COLUMN_QUERY}</th>
486  *         <td>If this column exists <i>and</i> this element exists at the given row, this is the
487  *             data that will be used when forming the suggestion's query.</td>
488  *         <td align="center">Required if suggestion's action is
489  *             {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH}, optional otherwise.</td>
490  *     </tr>
491  *
492  *     <tr><th>{@link #SUGGEST_COLUMN_SHORTCUT_ID}</th>
493  *         <td>This column is used to indicate whether a search suggestion should be stored as a
494  *             shortcut, and whether it should be validated.  Shortcuts are usually formed when the
495  *             user clicks a suggestion from Quick Search Box.  If missing, the result will be
496  *             stored as a shortcut and never refreshed.  If set to
497  *             {@link #SUGGEST_NEVER_MAKE_SHORTCUT}, the result will not be stored as a shortcut.
498  *             Otherwise, the shortcut id will be used to check back for for an up to date
499  *             suggestion using {@link #SUGGEST_URI_PATH_SHORTCUT}. Read more about shortcut
500  *             refreshing in the section about
501  *             <a href="#ExposingSearchSuggestionsToQuickSearchBox">exposing search suggestions to
502  *             Quick Search Box</a>.</td>
503  *         <td align="center">No.  Only applicable to sources included in Quick Search Box.</td>
504  *     </tr>
505  *
506  *     <tr><th>{@link #SUGGEST_COLUMN_SPINNER_WHILE_REFRESHING}</th>
507  *         <td>This column is used to specify that a spinner should be shown in lieu of an icon2
508  *             while the shortcut of this suggestion is being refreshed in Quick Search Box.</td>
509  *         <td align="center">No.  Only applicable to sources included in Quick Search Box.</td>
510  *     </tr>
511  *
512  *     <tr><th><i>Other Columns</i></th>
513  *         <td>Finally, if you have defined any <a href="#ActionKeys">Action Keys</a> and you wish
514  *             for them to have suggestion-specific definitions, you'll need to define one
515  *             additional column per action key.  The action key will only trigger if the
516  *             currently-selection suggestion has a non-empty string in the corresponding column.
517  *             See the section on <a href="#ActionKeys">Action Keys</a> for additional details and
518  *             implementation steps.</td>
519  *         <td align="center">No</td>
520  *     </tr>
521  *
522  *     </tbody>
523  * </table>
524  *
525  * <p>Clearly there are quite a few permutations of your suggestion data, but in the next section
526  * we'll look at a few simple combinations that you'll select from.
527  *
528  * <p><b>The Format Of Intents Sent By Search Suggestions.</b>  Although there are many ways to
529  * configure these intents, this document will provide specific information on just a few of them.
530  * <ul><li><b>Launch a query.</b>  In this model, each suggestion represents a query that your
531  * searchable activity can perform, and the {@link android.content.Intent Intent} will be formatted
532  * exactly like those sent when the user enters query text and clicks the "GO" button:
533  *   <ul>
534  *   <li><b>Action:</b> {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} provided
535  *   using your XML metadata (android:searchSuggestIntentAction).</li>
536  *   <li><b>Data:</b> empty (not used).</li>
537  *   <li><b>Query:</b> query text supplied by the cursor.</li>
538  *   </ul>
539  * </li>
540  * <li><b>Go directly to a result, using a complete Data Uri.</b>  In this model, the user will be
541  * taken directly to a specific result.
542  *   <ul>
543  *   <li><b>Action:</b> {@link android.content.Intent#ACTION_VIEW ACTION_VIEW}</li>
544  *   <li><b>Data:</b> a complete Uri, supplied by the cursor, that identifies the desired data.</li>
545  *   <li><b>Query:</b> query text supplied with the suggestion (probably ignored)</li>
546  *   </ul>
547  * </li>
548  * <li><b>Go directly to a result, using a synthesized Data Uri.</b>  This has the same result
549  * as the previous suggestion, but provides the Data Uri in a different way.
550  *   <ul>
551  *   <li><b>Action:</b> {@link android.content.Intent#ACTION_VIEW ACTION_VIEW}</li>
552  *   <li><b>Data:</b> The search manager will assemble a Data Uri using the following elements:
553  *   a Uri fragment provided in your XML metadata (android:searchSuggestIntentData), followed by
554  *   a single "/", followed by the value found in the {@link #SUGGEST_COLUMN_INTENT_DATA_ID}
555  *   entry in your cursor.</li>
556  *   <li><b>Query:</b> query text supplied with the suggestion (probably ignored)</li>
557  *   </ul>
558  * </li>
559  * </ul>
560  * <p>This list is not meant to be exhaustive.  Applications should feel free to define other types
561  * of suggestions.  For example, you could reduce long lists of results to summaries, and use one
562  * of the above intents (or one of your own) with specially formatted Data Uri's to display more
563  * detailed results.  Or you could display textual shortcuts as suggestions, but launch a display
564  * in a more data-appropriate format such as media artwork.
565  *
566  * <p><b>Suggestion Rewriting.</b>  If the user navigates through the suggestions list, the UI
567  * may temporarily rewrite the user's query with a query that matches the currently selected
568  * suggestion.  This enables the user to see what query is being suggested, and also allows the user
569  * to click or touch in the entry EditText element and make further edits to the query before
570  * dispatching it.  In order to perform this correctly, the Search UI needs to know exactly what
571  * text to rewrite the query with.
572  *
573  * <p>For each suggestion, the following logic is used to select a new query string:
574  * <ul><li>If the suggestion provides an explicit value in the {@link #SUGGEST_COLUMN_QUERY}
575  * column, this value will be used.</li>
576  * <li>If the metadata includes the queryRewriteFromData flag, and the suggestion provides an
577  * explicit value for the intent Data field, this Uri will be used.  Note that this should only be
578  * used with Uri's that are intended to be user-visible, such as HTTP.  Internal Uri schemes should
579  * not be used in this way.</li>
580  * <li>If the metadata includes the queryRewriteFromText flag, the text in
581  * {@link #SUGGEST_COLUMN_TEXT_1} will be used.  This should be used for suggestions in which no
582  * query text is provided and the SUGGEST_COLUMN_INTENT_DATA values are not suitable for user
583  * inspection and editing.</li></ul>
584  *
585  * <a name="ExposingSearchSuggestionsToQuickSearchBox"></a>
586  * <h3>Exposing Search Suggestions to Quick Search Box</h3>
587  *
588  * <p>Once your application is set up to provide search suggestions, making them available to the
589  * globally accessable Quick Search Box is as easy as setting android:includeInGlobalSearch to
590  * "true" in your searchable metadata file.  Beyond that, here are some more details of how
591  * suggestions interact with Quick Search Box, and optional ways that you may customize suggestions
592  * for your application.
593  *
594  * <p><b>Important Note:</b>  By default, your application will not be enabled as a suggestion
595  * provider (or "searchable item") in Quick Search Box. Once your app is installed, the user must
596  * enable it as a "searchable item" in the Search settings in order to receive your app's
597  * suggestions in Quick Search Box. You should consider how to message this to users of your app -
598  * perhaps with a note to the user the first time they launch the app about how to enable search
599  * suggestions. This gives your app a chance to be queried for suggestions as the user types into
600  * Quick Search Box, though exactly how or if your suggestions will be surfaced is decided by Quick
601  * Search Box.
602  *
603  * <p><b>Source Ranking:</b>  Once your application's search results are made available to Quick
604  * Search Box, how they surface to the user for a particular query will be determined as appropriate
605  * by Quick Search Box ranking. This may depend on how many other apps have results for that query,
606  * and how often the user has clicked on your results compared to the other apps - but there is no
607  * guarantee about how ranking will occur, or whether your app's suggestions will show at all for
608  * a given query.  In general, you can expect that providing quality results will increase the
609  * likelihood that your app's suggestions are provided in a prominent position, and apps that
610  * provide lower quality suggestions will be more likely to be ranked lower and/or not displayed.
611  *
612  * <p><b>Search Settings:</b>  Each app that is available to Quick Search Box has an entry in the
613  * system settings where the user can enable or disable the inclusion of its results.  Below the
614  * name of the application, each application may provide a brief description of what kind of
615  * information will be made available via a search settings description string pointed to by the
616  * android:searchSettingsDescription attribute in the searchable metadata. Note that the
617  * user will need to visit this settings menu to enable search suggestions for your app before your
618  * app will have a chance to provide search suggestions to Quick Search Box - see the section
619  * called "Important Note" above.
620  *
621  * <p><b>Shortcuts:</b>  Suggestions that are clicked on by the user may be automatically made into
622  * shortcuts, which are suggestions that have been copied from your provider in order to be quickly
623  * displayed without the need to re-query the original sources. Shortcutted suggestions may be
624  * displayed for the query that yielded the suggestion and for any prefixes of that query. You can
625  * request how to have your app's suggestions made into shortcuts, and whether they should be
626  * refreshed, using the {@link #SUGGEST_COLUMN_SHORTCUT_ID} column:
627  * <ul><li>Suggestions that do not include a shortcut id column will be made into shortcuts and
628  * never refreshed.  This makes sense for suggestions that refer to data that will never be changed
629  * or removed.</li>
630  * <li>Suggestions that include a shortcut id will be re-queried for a fresh version of the
631  * suggestion each time the shortcut is displayed.  The shortcut will be quickly displayed with
632  * whatever data was most recently available until the refresh query returns, after which the
633  * suggestion will be dynamically refreshed with the up to date information.  The shortcut refresh
634  * query will be sent to your suggestion provider with a uri of {@link #SUGGEST_URI_PATH_SHORTCUT}.
635  * The result should contain one suggestion using the same columns as the suggestion query, or be
636  * empty, indicating that the shortcut is no longer valid.  Shortcut ids make sense when referring
637  * to data that may change over time, such as a contact's presence status.  If a suggestion refers
638  * to data that could take longer to refresh, such as a network based refresh of a stock quote, you
639  * may include {@link #SUGGEST_COLUMN_SPINNER_WHILE_REFRESHING} to show a progress spinner for the
640  * right hand icon until the refresh is complete.</li>
641  * <li>Finally, to prevent a suggestion from being copied into a shortcut, you may provide a
642  * shortcut id with a value of {@link #SUGGEST_NEVER_MAKE_SHORTCUT}.</li></ul>
643  *
644  * Note that Quick Search Box will ultimately decide whether to shortcut your app's suggestions,
645  * considering these values as a strong request from your application.
646  *
647  * <a name="ActionKeys"></a>
648  * <h3>Action Keys</h3>
649  *
650  * <p>Searchable activities may also wish to provide shortcuts based on the various action keys
651  * available on the device.  The most basic example of this is the contacts app, which enables the
652  * green "dial" key for quick access during searching.  Not all action keys are available on
653  * every device, and not all are allowed to be overriden in this way.  (For example, the "Home"
654  * key must always return to the home screen, with no exceptions.)
655  *
656  * <p>In order to define action keys for your searchable application, you must do two things.
657  *
658  * <ul>
659  * <li>You'll add one or more <i>actionkey</i> elements to your searchable metadata configuration
660  * file.  Each element defines one of the keycodes you are interested in,
661  * defines the conditions under which they are sent, and provides details
662  * on how to communicate the action key event back to your searchable activity.</li>
663  * <li>In your broadcast receiver, if you wish, you can check for action keys by checking the
664  * extras field of the {@link android.content.Intent Intent}.</li>
665  * </ul>
666  *
667  * <p><b>Updating metadata.</b>  For each keycode of interest, you must add an &lt;actionkey&gt;
668  * element.  Within this element you must define two or three attributes.  The first attribute,
669  * &lt;android:keycode&gt;, is required;  It is the key code of the action key event, as defined in
670  * {@link android.view.KeyEvent}.  The remaining two attributes define the value of the actionkey's
671  * <i>message</i>, which will be passed to your searchable activity in the
672  * {@link android.content.Intent Intent} (see below for more details).  Although each of these
673  * attributes is optional, you must define one or both for the action key to have any effect.
674  * &lt;android:queryActionMsg&gt; provides the message that will be sent if the action key is
675  * pressed while the user is simply entering query text.  &lt;android:suggestActionMsgColumn&gt;
676  * is used when action keys are tied to specific suggestions.  This attribute provides the name
677  * of a <i>column</i> in your suggestion cursor;  The individual suggestion, in that column,
678  * provides the message.  (If the cell is empty or null, that suggestion will not work with that
679  * action key.)
680  * <p>See the <a href="#SearchabilityMetadata">Searchability Metadata</a> section for more details
681  * and examples.
682  *
683  * <p><b>Receiving Action Keys</b>  Intents launched by action keys will be specially marked
684  * using a combination of values.  This enables your searchable application to examine the intent,
685  * if necessary, and perform special processing.  For example, clicking a suggested contact might
686  * simply display them;  Selecting a suggested contact and clicking the dial button might
687  * immediately call them.
688  *
689  * <p>When a search {@link android.content.Intent Intent} is launched by an action key, two values
690  * will be added to the extras field.
691  * <ul>
692  * <li>To examine the key code, use {@link android.content.Intent#getIntExtra
693  * getIntExtra(SearchManager.ACTION_KEY)}.</li>
694  * <li>To examine the message string, use {@link android.content.Intent#getStringExtra
695  * getStringExtra(SearchManager.ACTION_MSG)}</li>
696  * </ul>
697  *
698  * <a name="SearchabilityMetadata"></a>
699  * <h3>Searchability Metadata</h3>
700  *
701  * <p>Every activity that is searchable must provide a small amount of additional information
702  * in order to properly configure the search system.  This controls the way that your search
703  * is presented to the user, and controls for the various modalities described previously.
704  *
705  * <p>If your application is not searchable,
706  * then you do not need to provide any search metadata, and you can skip the rest of this section.
707  * When this search metadata cannot be found, the search manager will assume that the activity
708  * does not implement search.  (Note: to implement web-based search, you will need to add
709  * the android.app.default_searchable metadata to your manifest, as shown below.)
710  *
711  * <p>Values you supply in metadata apply only to each local searchable activity.  Each
712  * searchable activity can define a completely unique search experience relevant to its own
713  * capabilities and user experience requirements, and a single application can even define multiple
714  * searchable activities.
715  *
716  * <p><b>Metadata for searchable activity.</b>  As with your search implementations described
717  * above, you must first identify which of your activities is searchable.  In the
718  * <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">manifest</a> entry for this activity, you must
719  * provide two elements:
720  * <ul><li>An intent-filter specifying that you can receive and process the
721  * {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} {@link android.content.Intent Intent}.
722  * </li>
723  * <li>A reference to a small XML file (typically called "searchable.xml") which contains the
724  * remaining configuration information for how your application implements search.</li></ul>
725  *
726  * <p>Here is a snippet showing the necessary elements in the
727  * <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">manifest</a> entry for your searchable activity.
728  * <pre class="prettyprint">
729  *        &lt;!-- Search Activity - searchable --&gt;
730  *        &lt;activity android:name="MySearchActivity"
731  *                  android:label="Search"
732  *                  android:launchMode="singleTop"&gt;
733  *            &lt;intent-filter&gt;
734  *                &lt;action android:name="android.intent.action.SEARCH" /&gt;
735  *                &lt;category android:name="android.intent.category.DEFAULT" /&gt;
736  *            &lt;/intent-filter&gt;
737  *            &lt;meta-data android:name="android.app.searchable"
738  *                       android:resource="@xml/searchable" /&gt;
739  *        &lt;/activity&gt;</pre>
740  *
741  * <p>Next, you must provide the rest of the searchability configuration in
742  * the small XML file, stored in the ../xml/ folder in your build.  The XML file is a
743  * simple enumeration of the search configuration parameters for searching within this activity,
744  * application, or package.  Here is a sample XML file (named searchable.xml, for use with
745  * the above manifest) for a query-search activity.
746  *
747  * <pre class="prettyprint">
748  * &lt;searchable xmlns:android="http://schemas.android.com/apk/res/android"
749  *     android:label="@string/search_label"
750  *     android:hint="@string/search_hint" &gt;
751  * &lt;/searchable&gt;</pre>
752  *
753  * <p>Note that all user-visible strings <i>must</i> be provided in the form of "@string"
754  * references.  Hard-coded strings, which cannot be localized, will not work properly in search
755  * metadata.
756  *
757  * <p>Attributes you can set in search metadata:
758  * <table border="2" width="85%" align="center" frame="hsides" rules="rows">
759  *
760  *     <thead>
761  *     <tr><th>Attribute</th> <th>Description</th> <th>Required?</th></tr>
762  *     </thead>
763  *
764  *     <tbody>
765  *     <tr><th>android:label</th>
766  *         <td>This is the name for your application that will be presented to the user in a
767  *             list of search targets, or in the search box as a label.</td>
768  *         <td align="center">Yes</td>
769  *     </tr>
770  *
771  *     <tr><th>android:icon</th>
772  *         <td><strong>This is deprecated.</strong><br/>The default
773  *           application icon is now always used, so this attribute is
774  *           obsolete.</td>
775  *         <td align="center">No</td>
776  *     </tr>
777  *
778  *     <tr><th>android:hint</th>
779  *         <td>This is the text to display in the search text field when no text
780  *             has been entered by the user.</td>
781  *         <td align="center">No</td>
782  *     </tr>
783  *
784  *     <tr><th>android:searchMode</th>
785  *         <td>If provided and non-zero, sets additional modes for control of the search
786  *             presentation.  The following mode bits are defined:
787  *             <table border="2" align="center" frame="hsides" rules="rows">
788  *                 <tbody>
789  *                 <tr><th>showSearchLabelAsBadge</th>
790  *                     <td>If set, this flag enables the display of the search target (label)
791  *                         above the search box. As an alternative, you may
792  *                         want to instead use "hint" text in the search box.
793  *                         See the "android:hint" attribute above.</td>
794  *                 </tr>
795  *                 <tr><th>showSearchIconAsBadge</th>
796  *                     <td><strong>This is deprecated.</strong><br/>The default
797  *                         application icon is now always used, so this
798  *                         option is obsolete.</td>
799  *                 </tr>
800  *                 <tr><th>queryRewriteFromData</th>
801  *                     <td>If set, this flag causes the suggestion column SUGGEST_COLUMN_INTENT_DATA
802  *                         to be considered as the text for suggestion query rewriting.  This should
803  *                         only be used when the values in SUGGEST_COLUMN_INTENT_DATA are suitable
804  *                         for user inspection and editing - typically, HTTP/HTTPS Uri's.</td>
805  *                 </tr>
806  *                 <tr><th>queryRewriteFromText</th>
807  *                     <td>If set, this flag causes the suggestion column SUGGEST_COLUMN_TEXT_1 to
808  *                         be considered as the text for suggestion query rewriting.  This should
809  *                         be used for suggestions in which no query text is provided and the
810  *                         SUGGEST_COLUMN_INTENT_DATA values are not suitable for user inspection
811  *                         and editing.</td>
812  *                 </tr>
813  *                 </tbody>
814  *            </table>
815  *            Note that the icon of your app will likely be shown alongside any badge you specify,
816  *            to differentiate search in your app from Quick Search Box. The display of this icon
817  *            is not under the app's control.
818  *         </td>
819  *
820  *         <td align="center">No</td>
821  *     </tr>
822  *
823  *     <tr><th>android:inputType</th>
824  *         <td>If provided, supplies a hint about the type of search text the user will be
825  *             entering.  For most searches, in which free form text is expected, this attribute
826  *             need not be provided.  Suitable values for this attribute are described in the
827  *             <a href="../R.attr.html#inputType">inputType</a> attribute.</td>
828  *         <td align="center">No</td>
829  *     </tr>
830  *     <tr><th>android:imeOptions</th>
831  *         <td>If provided, supplies additional options for the input method.
832  *             For most searches, in which free form text is expected, this attribute
833  *             need not be provided, and will default to "actionSearch".
834  *             Suitable values for this attribute are described in the
835  *             <a href="../R.attr.html#imeOptions">imeOptions</a> attribute.</td>
836  *         <td align="center">No</td>
837  *     </tr>
838  *
839  *     </tbody>
840  * </table>
841  *
842  * <p><b>Styleable Resources in your Metadata.</b>  It's possible to provide alternate strings
843  * for your searchable application, in order to provide localization and/or to better visual
844  * presentation on different device configurations.  Each searchable activity has a single XML
845  * metadata file, but any resource references can be replaced at runtime based on device
846  * configuration, language setting, and other system inputs.
847  *
848  * <p>A concrete example is the "hint" text you supply using the android:searchHint attribute.
849  * In portrait mode you'll have less screen space and may need to provide a shorter string, but
850  * in landscape mode you can provide a longer, more descriptive hint.  To do this, you'll need to
851  * define two or more strings.xml files, in the following directories:
852  * <ul><li>.../res/values-land/strings.xml</li>
853  * <li>.../res/values-port/strings.xml</li>
854  * <li>.../res/values/strings.xml</li></ul>
855  *
856  * <p>For more complete documentation on this capability, see
857  * <a href="{@docRoot}guide/topics/resources/resources-i18n.html#AlternateResources">Resources and
858  * Internationalization: Alternate Resources</a>.
859  *
860  * <p><b>Metadata for non-searchable activities.</b>  Activities which are part of a searchable
861  * application, but don't implement search itself, require a bit of "glue" in order to cause
862  * them to invoke search using your searchable activity as their primary context.  If this is not
863  * provided, then searches from these activities will use the system default search context.
864  *
865  * <p>The simplest way to specify this is to add a <i>search reference</i> element to the
866  * application entry in the <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">manifest</a> file.
867  * The value of this reference can be either of:
868  * <ul><li>The name of your searchable activity.
869  * It is typically prefixed by '.' to indicate that it's in the same package.</li>
870  * <li>A "*" indicates that the system may select a default searchable activity, in which
871  * case it will typically select web-based search.</li>
872  * </ul>
873  *
874  * <p>Here is a snippet showing the necessary addition to the manifest entry for your
875  * non-searchable activities.
876  * <pre class="prettyprint">
877  *        &lt;application&gt;
878  *            &lt;meta-data android:name="android.app.default_searchable"
879  *                       android:value=".MySearchActivity" /&gt;
880  *
881  *            &lt;!-- followed by activities, providers, etc... --&gt;
882  *        &lt;/application&gt;</pre>
883  *
884  * <p>You can also specify android.app.default_searchable on a per-activity basis, by including
885  * the meta-data element (as shown above) in one or more activity sections.  If found, these will
886  * override the reference in the application section.  The only reason to configure your application
887  * this way would be if you wish to partition it into separate sections with different search
888  * behaviors;  Otherwise this configuration is not recommended.
889  *
890  * <p><b>Additional metadata for search suggestions.</b>  If you have defined a content provider
891  * to generate search suggestions, you'll need to publish it to the system, and you'll need to
892  * provide a bit of additional XML metadata in order to configure communications with it.
893  *
894  * <p>First, in your <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">manifest</a>, you'll add the
895  * following lines.
896  * <pre class="prettyprint">
897  *        &lt;!-- Content provider for search suggestions --&gt;
898  *        &lt;provider android:name="YourSuggestionProviderClass"
899  *                android:authorities="your.suggestion.authority" /&gt;</pre>
900  *
901  * <p>Next, you'll add a few lines to your XML metadata file, as shown:
902  * <pre class="prettyprint">
903  *     &lt;!-- Required attribute for any suggestions provider --&gt;
904  *     android:searchSuggestAuthority="your.suggestion.authority"
905  *
906  *     &lt;!-- Optional attribute for configuring queries --&gt;
907  *     android:searchSuggestSelection="field =?"
908  *
909  *     &lt;!-- Optional attributes for configuring intent construction --&gt;
910  *     android:searchSuggestIntentAction="intent action string"
911  *     android:searchSuggestIntentData="intent data Uri" /&gt;</pre>
912  *
913  * <p>Elements of search metadata that support suggestions:
914  * <table border="2" width="85%" align="center" frame="hsides" rules="rows">
915  *
916  *     <thead>
917  *     <tr><th>Attribute</th> <th>Description</th> <th>Required?</th></tr>
918  *     </thead>
919  *
920  *     <tbody>
921  *     <tr><th>android:searchSuggestAuthority</th>
922  *         <td>This value must match the authority string provided in the <i>provider</i> section
923  *             of your <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">manifest</a>.</td>
924  *         <td align="center">Yes</td>
925  *     </tr>
926  *
927  *     <tr><th>android:searchSuggestPath</th>
928  *         <td>If provided, this will be inserted in the suggestions query Uri, after the authority
929  *             you have provide but before the standard suggestions path.  This is only required if
930  *             you have a single content provider issuing different types of suggestions (e.g. for
931  *             different data types) and you need a way to disambiguate the suggestions queries
932  *             when they are received.</td>
933  *         <td align="center">No</td>
934  *     </tr>
935  *
936  *     <tr><th>android:searchSuggestSelection</th>
937  *         <td>If provided, this value will be passed into your query function as the
938  *             <i>selection</i> parameter.  Typically this will be a WHERE clause for your database,
939  *             and will contain a single question mark, which represents the actual query string
940  *             that has been typed by the user.  However, you can also use any non-null value
941  *             to simply trigger the delivery of the query text (via selection arguments), and then
942  *             use the query text in any way appropriate for your provider (ignoring the actual
943  *             text of the selection parameter.)</td>
944  *         <td align="center">No</td>
945  *     </tr>
946  *
947  *     <tr><th>android:searchSuggestIntentAction</th>
948  *         <td>If provided, and not overridden by the selected suggestion, this value will be
949  *             placed in the action field of the {@link android.content.Intent Intent} when the
950  *             user clicks a suggestion.</td>
951  *         <td align="center">No</td>
952  *
953  *     <tr><th>android:searchSuggestIntentData</th>
954  *         <td>If provided, and not overridden by the selected suggestion, this value will be
955  *             placed in the data field of the {@link android.content.Intent Intent} when the user
956  *             clicks a suggestion.</td>
957  *         <td align="center">No</td>
958  *     </tr>
959  *
960  *     </tbody>
961  * </table>
962  *
963  * <p>Elements of search metadata that configure search suggestions being available to Quick Search
964  * Box:
965  * <table border="2" width="85%" align="center" frame="hsides" rules="rows">
966  *
967  *     <thead>
968  *     <tr><th>Attribute</th> <th>Description</th> <th>Required?</th></tr>
969  *     </thead>
970  *
971  *     <tr><th>android:includeInGlobalSearch</th>
972  *         <td>If true, indicates the search suggestions provided by your application should be
973  *             included in the globally accessible Quick Search Box.  The attributes below are only
974  *             applicable if this is set to true.</td>
975  *         <td align="center">Yes</td>
976  *     </tr>
977  *
978  *     <tr><th>android:searchSettingsDescription</th>
979  *         <td>If provided, provides a brief description of the search suggestions that are provided
980  *             by your application to Quick Search Box, and will be displayed in the search settings
981  *             entry for your application.</td>
982  *         <td align="center">No</td>
983  *     </tr>
984  *
985  *     <tr><th>android:queryAfterZeroResults</th>
986  *         <td>Indicates whether a source should be invoked for supersets of queries it has
987  *             returned zero results for in the past.  For example, if a source returned zero
988  *             results for "bo", it would be ignored for "bob".  If set to false, this source
989  *             will only be ignored for a single session; the next time the search dialog is
990  *             invoked, all sources will be queried.  The default value is false.</td>
991  *         <td align="center">No</td>
992  *     </tr>
993  *
994  *     <tr><th>android:searchSuggestThreshold</th>
995  *         <td>Indicates the minimum number of characters needed to trigger a source from Quick
996  *             Search Box.  Only guarantees that a source will not be queried for anything shorter
997  *             than the threshold.  The default value is 0.</td>
998  *         <td align="center">No</td>
999  *     </tr>
1000  *
1001  *     </tbody>
1002  * </table>
1003  *
1004  * <p><b>Additional metadata for search action keys.</b>  For each action key that you would like to
1005  * define, you'll need to add an additional element defining that key, and using the attributes
1006  * discussed in <a href="#ActionKeys">Action Keys</a>.  A simple example is shown here:
1007  *
1008  * <pre class="prettyprint">&lt;actionkey
1009  *     android:keycode="KEYCODE_CALL"
1010  *     android:queryActionMsg="call"
1011  *     android:suggestActionMsg="call"
1012  *     android:suggestActionMsgColumn="call_column" /&gt;</pre>
1013  *
1014  * <p>Elements of search metadata that support search action keys.  Note that although each of the
1015  * action message elements are marked as <i>optional</i>, at least one must be present for the
1016  * action key to have any effect.
1017  *
1018  * <table border="2" width="85%" align="center" frame="hsides" rules="rows">
1019  *
1020  *     <thead>
1021  *     <tr><th>Attribute</th> <th>Description</th> <th>Required?</th></tr>
1022  *     </thead>
1023  *
1024  *     <tbody>
1025  *     <tr><th>android:keycode</th>
1026  *         <td>This attribute denotes the action key you wish to respond to.  Note that not
1027  *             all action keys are actually supported using this mechanism, as many of them are
1028  *             used for typing, navigation, or system functions.  This will be added to the
1029  *             {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} intent that is passed to
1030  *             your searchable activity.  To examine the key code, use
1031  *             {@link android.content.Intent#getIntExtra getIntExtra(SearchManager.ACTION_KEY)}.
1032  *             <p>Note, in addition to the keycode, you must also provide one or more of the action
1033  *             specifier attributes.</td>
1034  *         <td align="center">Yes</td>
1035  *     </tr>
1036  *
1037  *     <tr><th>android:queryActionMsg</th>
1038  *         <td>If you wish to handle an action key during normal search query entry, you
1039  *          must define an action string here.  This will be added to the
1040  *          {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} intent that is passed to your
1041  *          searchable activity.  To examine the string, use
1042  *          {@link android.content.Intent#getStringExtra
1043  *          getStringExtra(SearchManager.ACTION_MSG)}.</td>
1044  *         <td align="center">No</td>
1045  *     </tr>
1046  *
1047  *     <tr><th>android:suggestActionMsg</th>
1048  *         <td>If you wish to handle an action key while a suggestion is being displayed <i>and
1049  *             selected</i>, there are two ways to handle this.  If <i>all</i> of your suggestions
1050  *             can handle the action key, you can simply define the action message using this
1051  *             attribute.  This will be added to the
1052  *             {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} intent that is passed to
1053  *             your searchable activity.  To examine the string, use
1054  *             {@link android.content.Intent#getStringExtra
1055  *             getStringExtra(SearchManager.ACTION_MSG)}.</td>
1056  *         <td align="center">No</td>
1057  *     </tr>
1058  *
1059  *     <tr><th>android:suggestActionMsgColumn</th>
1060  *         <td>If you wish to handle an action key while a suggestion is being displayed <i>and
1061  *             selected</i>, but you do not wish to enable this action key for every suggestion,
1062  *             then you can use this attribute to control it on a suggestion-by-suggestion basis.
1063  *             First, you must define a column (and name it here) where your suggestions will
1064  *             include the action string.  Then, in your content provider, you must provide this
1065  *             column, and when desired, provide data in this column.
1066  *             The search manager will look at your suggestion cursor, using the string
1067  *             provided here in order to select a column, and will use that to select a string from
1068  *             the cursor.  That string will be added to the
1069  *             {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} intent that is passed to
1070  *             your searchable activity.  To examine the string, use
1071  *             {@link android.content.Intent#getStringExtra
1072  *             getStringExtra(SearchManager.ACTION_MSG)}.  <i>If the data does not exist for the
1073  *             selection suggestion, the action key will be ignored.</i></td>
1074  *         <td align="center">No</td>
1075  *     </tr>
1076  *
1077  *     </tbody>
1078  * </table>
1079  *
1080  * <p><b>Additional metadata for enabling voice search.</b>  To enable voice search for your
1081  * activity, you can add fields to the metadata that enable and configure voice search.  When
1082  * enabled (and available on the device), a voice search button will be displayed in the
1083  * Search UI.  Clicking this button will launch a voice search activity.  When the user has
1084  * finished speaking, the voice search phrase will be transcribed into text and presented to the
1085  * searchable activity as if it were a typed query.
1086  *
1087  * <p>Elements of search metadata that support voice search:
1088  * <table border="2" width="85%" align="center" frame="hsides" rules="rows">
1089  *
1090  *     <thead>
1091  *     <tr><th>Attribute</th> <th>Description</th> <th>Required?</th></tr>
1092  *     </thead>
1093  *
1094  *     <tr><th>android:voiceSearchMode</th>
1095  *         <td>If provided and non-zero, enables voice search.  (Voice search may not be
1096  *             provided by the device, in which case these flags will have no effect.)  The
1097  *             following mode bits are defined:
1098  *             <table border="2" align="center" frame="hsides" rules="rows">
1099  *                 <tbody>
1100  *                 <tr><th>showVoiceSearchButton</th>
1101  *                     <td>If set, display a voice search button.  This only takes effect if voice
1102  *                         search is available on the device.  If set, then launchWebSearch or
1103  *                         launchRecognizer must also be set.</td>
1104  *                 </tr>
1105  *                 <tr><th>launchWebSearch</th>
1106  *                     <td>If set, the voice search button will take the user directly to a
1107  *                         built-in voice web search activity.  Most applications will not use this
1108  *                         flag, as it will take the user away from the activity in which search
1109  *                         was invoked.</td>
1110  *                 </tr>
1111  *                 <tr><th>launchRecognizer</th>
1112  *                     <td>If set, the voice search button will take the user directly to a
1113  *                         built-in voice recording activity.  This activity will prompt the user
1114  *                         to speak, transcribe the spoken text, and forward the resulting query
1115  *                         text to the searchable activity, just as if the user had typed it into
1116  *                         the search UI and clicked the search button.</td>
1117  *                 </tr>
1118  *                 </tbody>
1119  *            </table></td>
1120  *         <td align="center">No</td>
1121  *     </tr>
1122  *
1123  *     <tr><th>android:voiceLanguageModel</th>
1124  *         <td>If provided, this specifies the language model that should be used by the voice
1125  *             recognition system.
1126  *             See {@link android.speech.RecognizerIntent#EXTRA_LANGUAGE_MODEL}
1127  *             for more information.  If not provided, the default value
1128  *             {@link android.speech.RecognizerIntent#LANGUAGE_MODEL_FREE_FORM} will be used.</td>
1129  *         <td align="center">No</td>
1130  *     </tr>
1131  *
1132  *     <tr><th>android:voicePromptText</th>
1133  *         <td>If provided, this specifies a prompt that will be displayed during voice input.
1134  *             (If not provided, a default prompt will be displayed.)</td>
1135  *         <td align="center">No</td>
1136  *     </tr>
1137  *
1138  *     <tr><th>android:voiceLanguage</th>
1139  *         <td>If provided, this specifies the spoken language to be expected.  This is only
1140  *             needed if it is different from the current value of
1141  *             {@link java.util.Locale#getDefault()}.
1142  *             </td>
1143  *         <td align="center">No</td>
1144  *     </tr>
1145  *
1146  *     <tr><th>android:voiceMaxResults</th>
1147  *         <td>If provided, enforces the maximum number of results to return, including the "best"
1148  *             result which will always be provided as the SEARCH intent's primary query.  Must be
1149  *             one or greater.  Use {@link android.speech.RecognizerIntent#EXTRA_RESULTS}
1150  *             to get the results from the intent.  If not provided, the recognizer will choose
1151  *             how many results to return.</td>
1152  *         <td align="center">No</td>
1153  *     </tr>
1154  *
1155  *     </tbody>
1156  * </table>
1157  *
1158  * <a name="PassingSearchContext"></a>
1159  * <h3>Passing Search Context</h3>
1160  *
1161  * <p>In order to improve search experience, an application may wish to specify
1162  * additional data along with the search, such as local history or context.  For
1163  * example, a maps search would be improved by including the current location.
1164  * In order to simplify the structure of your activities, this can be done using
1165  * the search manager.
1166  *
1167  * <p>Any data can be provided at the time the search is launched, as long as it
1168  * can be stored in a {@link android.os.Bundle Bundle} object.
1169  *
1170  * <p>To pass application data into the Search Manager, you'll need to override
1171  * {@link android.app.Activity#onSearchRequested onSearchRequested} as follows:
1172  *
1173  * <pre class="prettyprint">
1174  * &#64;Override
1175  * public boolean onSearchRequested() {
1176  *     Bundle appData = new Bundle();
1177  *     appData.put...();
1178  *     appData.put...();
1179  *     startSearch(null, false, appData, false);
1180  *     return true;
1181  * }</pre>
1182  *
1183  * <p>To receive application data from the Search Manager, you'll extract it from
1184  * the {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH}
1185  * {@link android.content.Intent Intent} as follows:
1186  *
1187  * <pre class="prettyprint">
1188  * final Bundle appData = queryIntent.getBundleExtra(SearchManager.APP_DATA);
1189  * if (appData != null) {
1190  *     appData.get...();
1191  *     appData.get...();
1192  * }</pre>
1193  *
1194  * <a name="ProtectingUserPrivacy"></a>
1195  * <h3>Protecting User Privacy</h3>
1196  *
1197  * <p>Many users consider their activities on the phone, including searches, to be private
1198  * information.  Applications that implement search should take steps to protect users' privacy
1199  * wherever possible.  This section covers two areas of concern, but you should consider your search
1200  * design carefully and take any additional steps necessary.
1201  *
1202  * <p><b>Don't send personal information to servers, and if you do, don't log it.</b>
1203  * "Personal information" is information that can personally identify your users, such as name,
1204  * email address or billing information, or other data which can be reasonably linked to such
1205  * information.  If your application implements search with the assistance of a server, try to
1206  * avoid sending personal information with your searches.  For example, if you are searching for
1207  * businesses near a zip code, you don't need to send the user ID as well - just send the zip code
1208  * to the server.  If you do need to send personal information, you should take steps to avoid
1209  * logging it.  If you must log it, you should protect that data very carefully, and erase it as
1210  * soon as possible.
1211  *
1212  * <p><b>Provide the user with a way to clear their search history.</b>  The Search Manager helps
1213  * your application provide context-specific suggestions.  Sometimes these suggestions are based
1214  * on previous searches, or other actions taken by the user in an earlier session.  A user may not
1215  * wish for previous searches to be revealed to other users, for instance if they share their phone
1216  * with a friend.  If your application provides suggestions that can reveal previous activities,
1217  * you should implement a "Clear History" menu, preference, or button.  If you are using
1218  * {@link android.provider.SearchRecentSuggestions}, you can simply call its
1219  * {@link android.provider.SearchRecentSuggestions#clearHistory() clearHistory()} method from
1220  * your "Clear History" UI.  If you are implementing your own form of recent suggestions, you'll
1221  * need to provide a similar a "clear history" API in your provider, and call it from your
1222  * "Clear History" UI.
1223  */
1224 public class SearchManager
1225         implements DialogInterface.OnDismissListener, DialogInterface.OnCancelListener
1226 {
1227 
1228     private static final boolean DBG = false;
1229     private static final String TAG = "SearchManager";
1230 
1231     /**
1232      * This is a shortcut definition for the default menu key to use for invoking search.
1233      *
1234      * See Menu.Item.setAlphabeticShortcut() for more information.
1235      */
1236     public final static char MENU_KEY = 's';
1237 
1238     /**
1239      * This is a shortcut definition for the default menu key to use for invoking search.
1240      *
1241      * See Menu.Item.setAlphabeticShortcut() for more information.
1242      */
1243     public final static int MENU_KEYCODE = KeyEvent.KEYCODE_S;
1244 
1245     /**
1246      * Intent extra data key: Use this key with
1247      * {@link android.content.Intent#getStringExtra
1248      *  content.Intent.getStringExtra()}
1249      * to obtain the query string from Intent.ACTION_SEARCH.
1250      */
1251     public final static String QUERY = "query";
1252 
1253     /**
1254      * Intent extra data key: Use this key with
1255      * {@link android.content.Intent#getStringExtra
1256      *  content.Intent.getStringExtra()}
1257      * to obtain the query string typed in by the user.
1258      * This may be different from the value of {@link #QUERY}
1259      * if the intent is the result of selecting a suggestion.
1260      * In that case, {@link #QUERY} will contain the value of
1261      * {@link #SUGGEST_COLUMN_QUERY} for the suggestion, and
1262      * {@link #USER_QUERY} will contain the string typed by the
1263      * user.
1264      */
1265     public final static String USER_QUERY = "user_query";
1266 
1267     /**
1268      * Intent extra data key: Use this key with Intent.ACTION_SEARCH and
1269      * {@link android.content.Intent#getBundleExtra
1270      *  content.Intent.getBundleExtra()}
1271      * to obtain any additional app-specific data that was inserted by the
1272      * activity that launched the search.
1273      */
1274     public final static String APP_DATA = "app_data";
1275 
1276     /**
1277      * Intent app_data bundle key: Use this key with the bundle from
1278      * {@link android.content.Intent#getBundleExtra
1279      * content.Intent.getBundleExtra(APP_DATA)} to obtain the source identifier
1280      * set by the activity that launched the search.
1281      *
1282      * @hide
1283      */
1284     public final static String SOURCE = "source";
1285 
1286     /**
1287      * Intent extra data key: Use {@link android.content.Intent#getBundleExtra
1288      * content.Intent.getBundleExtra(SEARCH_MODE)} to get the search mode used
1289      * to launch the intent.
1290      * The only current value for this is {@link #MODE_GLOBAL_SEARCH_SUGGESTION}.
1291      *
1292      * @hide
1293      */
1294     public final static String SEARCH_MODE = "search_mode";
1295 
1296     /**
1297      * Value for the {@link #SEARCH_MODE} key.
1298      * This is used if the intent was launched by clicking a suggestion in global search
1299      * mode (Quick Search Box).
1300      *
1301      * @hide
1302      */
1303     public static final String MODE_GLOBAL_SEARCH_SUGGESTION = "global_search_suggestion";
1304 
1305     /**
1306      * Intent extra data key: Use this key with Intent.ACTION_SEARCH and
1307      * {@link android.content.Intent#getIntExtra content.Intent.getIntExtra()}
1308      * to obtain the keycode that the user used to trigger this query.  It will be zero if the
1309      * user simply pressed the "GO" button on the search UI.  This is primarily used in conjunction
1310      * with the keycode attribute in the actionkey element of your searchable.xml configuration
1311      * file.
1312      */
1313     public final static String ACTION_KEY = "action_key";
1314 
1315     /**
1316      * Intent component name key: This key will be used for the extra populated by the
1317      * {@link #SUGGEST_COLUMN_INTENT_COMPONENT_NAME} column.
1318      *
1319      * {@hide}
1320      */
1321     public final static String COMPONENT_NAME_KEY = "intent_component_name_key";
1322 
1323     /**
1324      * Intent extra data key: This key will be used for the extra populated by the
1325      * {@link #SUGGEST_COLUMN_INTENT_EXTRA_DATA} column.
1326      */
1327     public final static String EXTRA_DATA_KEY = "intent_extra_data_key";
1328 
1329     /**
1330      * Defines the constants used in the communication between {@link android.app.SearchDialog} and
1331      * the global search provider via {@link Cursor#respond(android.os.Bundle)}.
1332      *
1333      * @hide
1334      */
1335     public static class DialogCursorProtocol {
1336 
1337         /**
1338          * The sent bundle will contain this integer key, with a value set to one of the events
1339          * below.
1340          */
1341         public final static String METHOD = "DialogCursorProtocol.method";
1342 
1343         /**
1344          * After data has been refreshed.
1345          */
1346         public final static int POST_REFRESH = 0;
1347         public final static String POST_REFRESH_RECEIVE_ISPENDING
1348                 = "DialogCursorProtocol.POST_REFRESH.isPending";
1349         public final static String POST_REFRESH_RECEIVE_DISPLAY_NOTIFY
1350                 = "DialogCursorProtocol.POST_REFRESH.displayNotify";
1351 
1352         /**
1353          * When a position has been clicked.
1354          */
1355         public final static int CLICK = 2;
1356         public final static String CLICK_SEND_POSITION
1357                 = "DialogCursorProtocol.CLICK.sendPosition";
1358         public final static String CLICK_SEND_MAX_DISPLAY_POS
1359                 = "DialogCursorProtocol.CLICK.sendDisplayPosition";
1360         public final static String CLICK_SEND_ACTION_KEY
1361                 = "DialogCursorProtocol.CLICK.sendActionKey";
1362         public final static String CLICK_SEND_ACTION_MSG
1363                 = "DialogCursorProtocol.CLICK.sendActionMsg";
1364         public final static String CLICK_RECEIVE_SELECTED_POS
1365                 = "DialogCursorProtocol.CLICK.receiveSelectedPosition";
1366 
1367         /**
1368          * When the threshold received in {@link #POST_REFRESH_RECEIVE_DISPLAY_NOTIFY} is displayed.
1369          */
1370         public final static int THRESH_HIT = 3;
1371 
1372         /**
1373          * When a search is started without using a suggestion.
1374          */
1375         public final static int SEARCH = 4;
1376         public final static String SEARCH_SEND_MAX_DISPLAY_POS
1377                 = "DialogCursorProtocol.SEARCH.sendDisplayPosition";
1378         public final static String SEARCH_SEND_QUERY = "DialogCursorProtocol.SEARCH.query";
1379     }
1380 
1381     /**
1382      * Intent extra data key: Use this key with Intent.ACTION_SEARCH and
1383      * {@link android.content.Intent#getStringExtra content.Intent.getStringExtra()}
1384      * to obtain the action message that was defined for a particular search action key and/or
1385      * suggestion.  It will be null if the search was launched by typing "enter", touched the the
1386      * "GO" button, or other means not involving any action key.
1387      */
1388     public final static String ACTION_MSG = "action_msg";
1389 
1390     /**
1391      * Uri path for queried suggestions data.  This is the path that the search manager
1392      * will use when querying your content provider for suggestions data based on user input
1393      * (e.g. looking for partial matches).
1394      * Typically you'll use this with a URI matcher.
1395      */
1396     public final static String SUGGEST_URI_PATH_QUERY = "search_suggest_query";
1397 
1398     /**
1399      * MIME type for suggestions data.  You'll use this in your suggestions content provider
1400      * in the getType() function.
1401      */
1402     public final static String SUGGEST_MIME_TYPE =
1403             "vnd.android.cursor.dir/vnd.android.search.suggest";
1404 
1405     /**
1406      * Uri path for shortcut validation.  This is the path that the search manager will use when
1407      * querying your content provider to refresh a shortcutted suggestion result and to check if it
1408      * is still valid.  When asked, a source may return an up to date result, or no result.  No
1409      * result indicates the shortcut refers to a no longer valid sugggestion.
1410      *
1411      * @see #SUGGEST_COLUMN_SHORTCUT_ID
1412      */
1413     public final static String SUGGEST_URI_PATH_SHORTCUT = "search_suggest_shortcut";
1414 
1415     /**
1416      * MIME type for shortcut validation.  You'll use this in your suggestions content provider
1417      * in the getType() function.
1418      */
1419     public final static String SHORTCUT_MIME_TYPE =
1420             "vnd.android.cursor.item/vnd.android.search.suggest";
1421 
1422 
1423     /**
1424      * The authority of the provider to report clicks to when a click is detected after pivoting
1425      * into a specific app's search from global search.
1426      *
1427      * In addition to the columns below, the suggestion columns are used to pass along the full
1428      * suggestion so it can be shortcutted.
1429      *
1430      * @hide
1431      */
1432     public final static String SEARCH_CLICK_REPORT_AUTHORITY =
1433             "com.android.globalsearch.stats";
1434 
1435     /**
1436      * The path the write goes to.
1437      *
1438      * @hide
1439      */
1440     public final static String SEARCH_CLICK_REPORT_URI_PATH = "click";
1441 
1442     /**
1443      * The column storing the query for the click.
1444      *
1445      * @hide
1446      */
1447     public final static String SEARCH_CLICK_REPORT_COLUMN_QUERY = "query";
1448 
1449     /**
1450      * The column storing the component name of the application that was pivoted into.
1451      *
1452      * @hide
1453      */
1454     public final static String SEARCH_CLICK_REPORT_COLUMN_COMPONENT = "component";
1455 
1456     /**
1457      * Column name for suggestions cursor.  <i>Unused - can be null or column can be omitted.</i>
1458      */
1459     public final static String SUGGEST_COLUMN_FORMAT = "suggest_format";
1460     /**
1461      * Column name for suggestions cursor.  <i>Required.</i>  This is the primary line of text that
1462      * will be presented to the user as the suggestion.
1463      */
1464     public final static String SUGGEST_COLUMN_TEXT_1 = "suggest_text_1";
1465     /**
1466      * Column name for suggestions cursor.  <i>Optional.</i>  If your cursor includes this column,
1467      *  then all suggestions will be provided in a two-line format.  The second line of text is in
1468      *  a much smaller appearance.
1469      */
1470     public final static String SUGGEST_COLUMN_TEXT_2 = "suggest_text_2";
1471     /**
1472      * Column name for suggestions cursor.  <i>Optional.</i>  If your cursor includes this column,
1473      *  then all suggestions will be provided in a format that includes space for two small icons,
1474      *  one at the left and one at the right of each suggestion.  The data in the column must
1475      *  be a resource ID of a drawable, or a URI in one of the following formats:
1476      *
1477      * <ul>
1478      * <li>content ({@link android.content.ContentResolver#SCHEME_CONTENT})</li>
1479      * <li>android.resource ({@link android.content.ContentResolver#SCHEME_ANDROID_RESOURCE})</li>
1480      * <li>file ({@link android.content.ContentResolver#SCHEME_FILE})</li>
1481      * </ul>
1482      *
1483      * See {@link android.content.ContentResolver#openAssetFileDescriptor(Uri, String)}
1484      * for more information on these schemes.
1485      */
1486     public final static String SUGGEST_COLUMN_ICON_1 = "suggest_icon_1";
1487     /**
1488      * Column name for suggestions cursor.  <i>Optional.</i>  If your cursor includes this column,
1489      *  then all suggestions will be provided in a format that includes space for two small icons,
1490      *  one at the left and one at the right of each suggestion.  The data in the column must
1491      *  be a resource ID of a drawable, or a URI in one of the following formats:
1492      *
1493      * <ul>
1494      * <li>content ({@link android.content.ContentResolver#SCHEME_CONTENT})</li>
1495      * <li>android.resource ({@link android.content.ContentResolver#SCHEME_ANDROID_RESOURCE})</li>
1496      * <li>file ({@link android.content.ContentResolver#SCHEME_FILE})</li>
1497      * </ul>
1498      *
1499      * See {@link android.content.ContentResolver#openAssetFileDescriptor(Uri, String)}
1500      * for more information on these schemes.
1501      */
1502     public final static String SUGGEST_COLUMN_ICON_2 = "suggest_icon_2";
1503     /**
1504      * Column name for suggestions cursor.  <i>Optional.</i>  If this column exists <i>and</i>
1505      * this element exists at the given row, this is the action that will be used when
1506      * forming the suggestion's intent.  If the element is not provided, the action will be taken
1507      * from the android:searchSuggestIntentAction field in your XML metadata.  <i>At least one of
1508      * these must be present for the suggestion to generate an intent.</i>  Note:  If your action is
1509      * the same for all suggestions, it is more efficient to specify it using XML metadata and omit
1510      * it from the cursor.
1511      */
1512     public final static String SUGGEST_COLUMN_INTENT_ACTION = "suggest_intent_action";
1513     /**
1514      * Column name for suggestions cursor.  <i>Optional.</i>  If this column exists <i>and</i>
1515      * this element exists at the given row, this is the data that will be used when
1516      * forming the suggestion's intent.  If the element is not provided, the data will be taken
1517      * from the android:searchSuggestIntentData field in your XML metadata.  If neither source
1518      * is provided, the Intent's data field will be null.  Note:  If your data is
1519      * the same for all suggestions, or can be described using a constant part and a specific ID,
1520      * it is more efficient to specify it using XML metadata and omit it from the cursor.
1521      */
1522     public final static String SUGGEST_COLUMN_INTENT_DATA = "suggest_intent_data";
1523     /**
1524      * Column name for suggestions cursor.  <i>Optional.</i>  If this column exists <i>and</i>
1525      * this element exists at the given row, this is the data that will be used when
1526      * forming the suggestion's intent. If not provided, the Intent's extra data field will be null.
1527      * This column allows suggestions to provide additional arbitrary data which will be included as
1528      * an extra under the key {@link #EXTRA_DATA_KEY}.
1529      */
1530     public final static String SUGGEST_COLUMN_INTENT_EXTRA_DATA = "suggest_intent_extra_data";
1531     /**
1532      * Column name for suggestions cursor.  <i>Optional.</i>  This column allows suggestions
1533      *  to provide additional arbitrary data which will be included as an extra under the key
1534      *  {@link #COMPONENT_NAME_KEY}. For use by the global search system only - if other providers
1535      *  attempt to use this column, the value will be overwritten by global search.
1536      *
1537      * @hide
1538      */
1539     public final static String SUGGEST_COLUMN_INTENT_COMPONENT_NAME = "suggest_intent_component";
1540     /**
1541      * Column name for suggestions cursor.  <i>Optional.</i>  If this column exists <i>and</i>
1542      * this element exists at the given row, then "/" and this value will be appended to the data
1543      * field in the Intent.  This should only be used if the data field has already been set to an
1544      * appropriate base string.
1545      */
1546     public final static String SUGGEST_COLUMN_INTENT_DATA_ID = "suggest_intent_data_id";
1547     /**
1548      * Column name for suggestions cursor.  <i>Required if action is
1549      * {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH}, optional otherwise.</i>  If this
1550      * column exists <i>and</i> this element exists at the given row, this is the data that will be
1551      * used when forming the suggestion's query.
1552      */
1553     public final static String SUGGEST_COLUMN_QUERY = "suggest_intent_query";
1554 
1555     /**
1556      * Column name for suggestions cursor. <i>Optional.</i>  This column is used to indicate whether
1557      * a search suggestion should be stored as a shortcut, and whether it should be refreshed.  If
1558      * missing, the result will be stored as a shortcut and never validated.  If set to
1559      * {@link #SUGGEST_NEVER_MAKE_SHORTCUT}, the result will not be stored as a shortcut.
1560      * Otherwise, the shortcut id will be used to check back for an up to date suggestion using
1561      * {@link #SUGGEST_URI_PATH_SHORTCUT}.
1562      */
1563     public final static String SUGGEST_COLUMN_SHORTCUT_ID = "suggest_shortcut_id";
1564 
1565     /**
1566      * Column name for suggestions cursor. <i>Optional.</i>  This column is used to specify the
1567      * cursor item's background color if it needs a non-default background color. A non-zero value
1568      * indicates a valid background color to override the default.
1569      *
1570      * @hide For internal use, not part of the public API.
1571      */
1572     public final static String SUGGEST_COLUMN_BACKGROUND_COLOR = "suggest_background_color";
1573 
1574     /**
1575      * Column name for suggestions cursor. <i>Optional.</i> This column is used to specify
1576      * that a spinner should be shown in lieu of an icon2 while the shortcut of this suggestion
1577      * is being refreshed.
1578      */
1579     public final static String SUGGEST_COLUMN_SPINNER_WHILE_REFRESHING =
1580             "suggest_spinner_while_refreshing";
1581 
1582     /**
1583      * Column value for suggestion column {@link #SUGGEST_COLUMN_SHORTCUT_ID} when a suggestion
1584      * should not be stored as a shortcut in global search.
1585      */
1586     public final static String SUGGEST_NEVER_MAKE_SHORTCUT = "_-1";
1587 
1588     /**
1589      * Query parameter added to suggestion queries to limit the number of suggestions returned.
1590      * This limit is only advisory and suggestion providers may chose to ignore it.
1591      */
1592     public final static String SUGGEST_PARAMETER_LIMIT = "limit";
1593 
1594     /**
1595      * If a suggestion has this value in {@link #SUGGEST_COLUMN_INTENT_ACTION},
1596      * the search dialog will switch to a different suggestion source when the
1597      * suggestion is clicked.
1598      *
1599      * {@link #SUGGEST_COLUMN_INTENT_DATA} must contain
1600      * the flattened {@link ComponentName} of the activity which is to be searched.
1601      *
1602      * TODO: Should {@link #SUGGEST_COLUMN_INTENT_DATA} instead contain a URI in the format
1603      * used by {@link android.provider.Applications}?
1604      *
1605      * TODO: This intent should be protected by the same permission that we use
1606      * for replacing the global search provider.
1607      *
1608      * The query text field will be set to the value of {@link #SUGGEST_COLUMN_QUERY}.
1609      *
1610      * @hide Pending API council approval.
1611      */
1612     public final static String INTENT_ACTION_CHANGE_SEARCH_SOURCE
1613             = "android.search.action.CHANGE_SEARCH_SOURCE";
1614 
1615     /**
1616      * Intent action for finding the global search activity.
1617      * The global search provider should handle this intent.
1618      *
1619      * @hide Pending API council approval.
1620      */
1621     public final static String INTENT_ACTION_GLOBAL_SEARCH
1622             = "android.search.action.GLOBAL_SEARCH";
1623 
1624     /**
1625      * Intent action for starting the global search settings activity.
1626      * The global search provider should handle this intent.
1627      *
1628      * @hide Pending API council approval.
1629      */
1630     public final static String INTENT_ACTION_SEARCH_SETTINGS
1631             = "android.search.action.SEARCH_SETTINGS";
1632 
1633     /**
1634      * Intent action for starting a web search provider's settings activity.
1635      * Web search providers should handle this intent if they have provider-specific
1636      * settings to implement.
1637      */
1638     public final static String INTENT_ACTION_WEB_SEARCH_SETTINGS
1639             = "android.search.action.WEB_SEARCH_SETTINGS";
1640 
1641     /**
1642      * Intent action broadcasted to inform that the searchables list or default have changed.
1643      * Components should handle this intent if they cache any searchable data and wish to stay
1644      * up to date on changes.
1645      */
1646     public final static String INTENT_ACTION_SEARCHABLES_CHANGED
1647             = "android.search.action.SEARCHABLES_CHANGED";
1648 
1649     /**
1650      * Intent action broadcasted to inform that the search settings have changed in some way.
1651      * Either searchables have been enabled or disabled, or a different web search provider
1652      * has been chosen.
1653      */
1654     public final static String INTENT_ACTION_SEARCH_SETTINGS_CHANGED
1655             = "android.search.action.SETTINGS_CHANGED";
1656 
1657     /**
1658      * If a suggestion has this value in {@link #SUGGEST_COLUMN_INTENT_ACTION},
1659      * the search dialog will take no action.
1660      *
1661      * @hide
1662      */
1663     public final static String INTENT_ACTION_NONE = "android.search.action.ZILCH";
1664 
1665     /**
1666      * Reference to the shared system search service.
1667      */
1668     private static ISearchManager mService;
1669 
1670     private final Context mContext;
1671 
1672     /**
1673      * compact representation of the activity associated with this search manager so
1674      * we can say who we are when starting search.  the search managerservice, in turn,
1675      * uses this to properly handle the back stack.
1676      */
1677     private int mIdent;
1678 
1679     /**
1680      * The package associated with this seach manager.
1681      */
1682     private String mAssociatedPackage;
1683 
1684     // package private since they are used by the inner class SearchManagerCallback
1685     /* package */ final Handler mHandler;
1686     /* package */ OnDismissListener mDismissListener = null;
1687     /* package */ OnCancelListener mCancelListener = null;
1688 
1689     private final SearchManagerCallback mSearchManagerCallback = new SearchManagerCallback();
1690 
SearchManager(Context context, Handler handler)1691     /*package*/ SearchManager(Context context, Handler handler)  {
1692         mContext = context;
1693         mHandler = handler;
1694         mService = ISearchManager.Stub.asInterface(
1695                 ServiceManager.getService(Context.SEARCH_SERVICE));
1696     }
1697 
hasIdent()1698     /*package*/ boolean hasIdent() {
1699         return mIdent != 0;
1700     }
1701 
setIdent(int ident, ComponentName component)1702     /*package*/ void setIdent(int ident, ComponentName component) {
1703         if (mIdent != 0) {
1704             throw new IllegalStateException("mIdent already set");
1705         }
1706         if (component == null) {
1707             throw new IllegalArgumentException("component must be non-null");
1708         }
1709         mIdent = ident;
1710         mAssociatedPackage = component.getPackageName();
1711     }
1712 
1713     /**
1714      * Launch search UI.
1715      *
1716      * <p>The search manager will open a search widget in an overlapping
1717      * window, and the underlying activity may be obscured.  The search
1718      * entry state will remain in effect until one of the following events:
1719      * <ul>
1720      * <li>The user completes the search.  In most cases this will launch
1721      * a search intent.</li>
1722      * <li>The user uses the back, home, or other keys to exit the search.</li>
1723      * <li>The application calls the {@link #stopSearch}
1724      * method, which will hide the search window and return focus to the
1725      * activity from which it was launched.</li>
1726      *
1727      * <p>Most applications will <i>not</i> use this interface to invoke search.
1728      * The primary method for invoking search is to call
1729      * {@link android.app.Activity#onSearchRequested Activity.onSearchRequested()} or
1730      * {@link android.app.Activity#startSearch Activity.startSearch()}.
1731      *
1732      * @param initialQuery A search string can be pre-entered here, but this
1733      * is typically null or empty.
1734      * @param selectInitialQuery If true, the intial query will be preselected, which means that
1735      * any further typing will replace it.  This is useful for cases where an entire pre-formed
1736      * query is being inserted.  If false, the selection point will be placed at the end of the
1737      * inserted query.  This is useful when the inserted query is text that the user entered,
1738      * and the user would expect to be able to keep typing.  <i>This parameter is only meaningful
1739      * if initialQuery is a non-empty string.</i>
1740      * @param launchActivity The ComponentName of the activity that has launched this search.
1741      * @param appSearchData An application can insert application-specific
1742      * context here, in order to improve quality or specificity of its own
1743      * searches.  This data will be returned with SEARCH intent(s).  Null if
1744      * no extra data is required.
1745      * @param globalSearch If false, this will only launch the search that has been specifically
1746      * defined by the application (which is usually defined as a local search).  If no default
1747      * search is defined in the current application or activity, global search will be launched.
1748      * If true, this will always launch a platform-global (e.g. web-based) search instead.
1749      *
1750      * @see android.app.Activity#onSearchRequested
1751      * @see #stopSearch
1752      */
startSearch(String initialQuery, boolean selectInitialQuery, ComponentName launchActivity, Bundle appSearchData, boolean globalSearch)1753     public void startSearch(String initialQuery,
1754                             boolean selectInitialQuery,
1755                             ComponentName launchActivity,
1756                             Bundle appSearchData,
1757                             boolean globalSearch) {
1758         if (mIdent == 0) throw new IllegalArgumentException(
1759                 "Called from outside of an Activity context");
1760         if (!globalSearch && !mAssociatedPackage.equals(launchActivity.getPackageName())) {
1761             Log.w(TAG, "invoking app search on a different package " +
1762                     "not associated with this search manager");
1763         }
1764         try {
1765             // activate the search manager and start it up!
1766             mService.startSearch(initialQuery, selectInitialQuery, launchActivity, appSearchData,
1767                     globalSearch, mSearchManagerCallback, mIdent);
1768         } catch (RemoteException ex) {
1769             Log.e(TAG, "startSearch() failed.", ex);
1770         }
1771     }
1772 
1773     /**
1774      * Similar to {@link #startSearch} but actually fires off the search query after invoking
1775      * the search dialog.  Made available for testing purposes.
1776      *
1777      * @param query The query to trigger.  If empty, request will be ignored.
1778      * @param launchActivity The ComponentName of the activity that has launched this search.
1779      * @param appSearchData An application can insert application-specific
1780      * context here, in order to improve quality or specificity of its own
1781      * searches.  This data will be returned with SEARCH intent(s).  Null if
1782      * no extra data is required.
1783      *
1784      * @see #startSearch
1785      */
triggerSearch(String query, ComponentName launchActivity, Bundle appSearchData)1786     public void triggerSearch(String query,
1787                               ComponentName launchActivity,
1788                               Bundle appSearchData) {
1789         if (mIdent == 0) throw new IllegalArgumentException(
1790                 "Called from outside of an Activity context");
1791         if (!mAssociatedPackage.equals(launchActivity.getPackageName())) {
1792             throw new IllegalArgumentException("invoking app search on a different package " +
1793                     "not associated with this search manager");
1794         }
1795         if (query == null || TextUtils.getTrimmedLength(query) == 0) {
1796             Log.w(TAG, "triggerSearch called with empty query, ignoring.");
1797             return;
1798         }
1799         try {
1800             mService.triggerSearch(query, launchActivity, appSearchData, mSearchManagerCallback,
1801                     mIdent);
1802         } catch (RemoteException ex) {
1803             Log.e(TAG, "triggerSearch() failed.", ex);
1804         }
1805     }
1806 
1807     /**
1808      * Terminate search UI.
1809      *
1810      * <p>Typically the user will terminate the search UI by launching a
1811      * search or by canceling.  This function allows the underlying application
1812      * or activity to cancel the search prematurely (for any reason).
1813      *
1814      * <p>This function can be safely called at any time (even if no search is active.)
1815      *
1816      * @see #startSearch
1817      */
stopSearch()1818     public void stopSearch() {
1819         if (DBG) debug("stopSearch()");
1820         try {
1821             mService.stopSearch();
1822         } catch (RemoteException ex) {
1823         }
1824     }
1825 
1826     /**
1827      * Determine if the Search UI is currently displayed.
1828      *
1829      * This is provided primarily for application test purposes.
1830      *
1831      * @return Returns true if the search UI is currently displayed.
1832      *
1833      * @hide
1834      */
isVisible()1835     public boolean isVisible() {
1836         if (DBG) debug("isVisible()");
1837         try {
1838             return mService.isVisible();
1839         } catch (RemoteException e) {
1840             Log.e(TAG, "isVisible() failed: " + e);
1841             return false;
1842         }
1843     }
1844 
1845     /**
1846      * See {@link SearchManager#setOnDismissListener} for configuring your activity to monitor
1847      * search UI state.
1848      */
1849     public interface OnDismissListener {
1850         /**
1851          * This method will be called when the search UI is dismissed. To make use of it, you must
1852          * implement this method in your activity, and call
1853          * {@link SearchManager#setOnDismissListener} to register it.
1854          */
onDismiss()1855         public void onDismiss();
1856     }
1857 
1858     /**
1859      * See {@link SearchManager#setOnCancelListener} for configuring your activity to monitor
1860      * search UI state.
1861      */
1862     public interface OnCancelListener {
1863         /**
1864          * This method will be called when the search UI is canceled. To make use if it, you must
1865          * implement this method in your activity, and call
1866          * {@link SearchManager#setOnCancelListener} to register it.
1867          */
onCancel()1868         public void onCancel();
1869     }
1870 
1871     /**
1872      * Set or clear the callback that will be invoked whenever the search UI is dismissed.
1873      *
1874      * @param listener The {@link OnDismissListener} to use, or null.
1875      */
setOnDismissListener(final OnDismissListener listener)1876     public void setOnDismissListener(final OnDismissListener listener) {
1877         mDismissListener = listener;
1878     }
1879 
1880     /**
1881      * Set or clear the callback that will be invoked whenever the search UI is canceled.
1882      *
1883      * @param listener The {@link OnCancelListener} to use, or null.
1884      */
setOnCancelListener(OnCancelListener listener)1885     public void setOnCancelListener(OnCancelListener listener) {
1886         mCancelListener = listener;
1887     }
1888 
1889     private class SearchManagerCallback extends ISearchManagerCallback.Stub {
1890 
1891         private final Runnable mFireOnDismiss = new Runnable() {
1892             public void run() {
1893                 if (DBG) debug("mFireOnDismiss");
1894                 if (mDismissListener != null) {
1895                     mDismissListener.onDismiss();
1896                 }
1897             }
1898         };
1899 
1900         private final Runnable mFireOnCancel = new Runnable() {
1901             public void run() {
1902                 if (DBG) debug("mFireOnCancel");
1903                 if (mCancelListener != null) {
1904                     mCancelListener.onCancel();
1905                 }
1906             }
1907         };
1908 
onDismiss()1909         public void onDismiss() {
1910             if (DBG) debug("onDismiss()");
1911             mHandler.post(mFireOnDismiss);
1912         }
1913 
onCancel()1914         public void onCancel() {
1915             if (DBG) debug("onCancel()");
1916             mHandler.post(mFireOnCancel);
1917         }
1918 
1919     }
1920 
1921     /**
1922      * @deprecated This method is an obsolete internal implementation detail. Do not use.
1923      */
1924     @Deprecated
onCancel(DialogInterface dialog)1925     public void onCancel(DialogInterface dialog) {
1926         throw new UnsupportedOperationException();
1927     }
1928 
1929     /**
1930      * @deprecated This method is an obsolete internal implementation detail. Do not use.
1931      */
1932     @Deprecated
onDismiss(DialogInterface dialog)1933     public void onDismiss(DialogInterface dialog) {
1934         throw new UnsupportedOperationException();
1935     }
1936 
1937     /**
1938      * Gets information about a searchable activity. This method is static so that it can
1939      * be used from non-Activity contexts.
1940      *
1941      * @param componentName The activity to get searchable information for.
1942      * @param globalSearch If <code>false</code>, return information about the given activity.
1943      *        If <code>true</code>, return information about the global search activity.
1944      * @return Searchable information, or <code>null</code> if the activity is not searchable.
1945      *
1946      * @hide because SearchableInfo is not part of the API.
1947      */
getSearchableInfo(ComponentName componentName, boolean globalSearch)1948     public SearchableInfo getSearchableInfo(ComponentName componentName,
1949             boolean globalSearch) {
1950         try {
1951             return mService.getSearchableInfo(componentName, globalSearch);
1952         } catch (RemoteException ex) {
1953             Log.e(TAG, "getSearchableInfo() failed: " + ex);
1954             return null;
1955         }
1956     }
1957 
1958     /**
1959      * Checks whether the given searchable is the default searchable.
1960      *
1961      * @hide because SearchableInfo is not part of the API.
1962      */
isDefaultSearchable(SearchableInfo searchable)1963     public boolean isDefaultSearchable(SearchableInfo searchable) {
1964         SearchableInfo defaultSearchable = getSearchableInfo(null, true);
1965         return defaultSearchable != null
1966                 && defaultSearchable.getSearchActivity().equals(searchable.getSearchActivity());
1967     }
1968 
1969     /**
1970      * Gets a cursor with search suggestions.
1971      *
1972      * @param searchable Information about how to get the suggestions.
1973      * @param query The search text entered (so far).
1974      * @return a cursor with suggestions, or <code>null</null> the suggestion query failed.
1975      *
1976      * @hide because SearchableInfo is not part of the API.
1977      */
getSuggestions(SearchableInfo searchable, String query)1978     public Cursor getSuggestions(SearchableInfo searchable, String query) {
1979         return getSuggestions(searchable, query, -1);
1980     }
1981 
1982     /**
1983      * Gets a cursor with search suggestions.
1984      *
1985      * @param searchable Information about how to get the suggestions.
1986      * @param query The search text entered (so far).
1987      * @param limit The query limit to pass to the suggestion provider. This is advisory,
1988      *        the returned cursor may contain more rows. Pass {@code -1} for no limit.
1989      * @return a cursor with suggestions, or <code>null</null> the suggestion query failed.
1990      *
1991      * @hide because SearchableInfo is not part of the API.
1992      */
getSuggestions(SearchableInfo searchable, String query, int limit)1993     public Cursor getSuggestions(SearchableInfo searchable, String query, int limit) {
1994         if (searchable == null) {
1995             return null;
1996         }
1997 
1998         String authority = searchable.getSuggestAuthority();
1999         if (authority == null) {
2000             return null;
2001         }
2002 
2003         Uri.Builder uriBuilder = new Uri.Builder()
2004                 .scheme(ContentResolver.SCHEME_CONTENT)
2005                 .authority(authority)
2006                 .query("")  // TODO: Remove, workaround for a bug in Uri.writeToParcel()
2007                 .fragment("");  // TODO: Remove, workaround for a bug in Uri.writeToParcel()
2008 
2009         // if content path provided, insert it now
2010         final String contentPath = searchable.getSuggestPath();
2011         if (contentPath != null) {
2012             uriBuilder.appendEncodedPath(contentPath);
2013         }
2014 
2015         // append standard suggestion query path
2016         uriBuilder.appendPath(SearchManager.SUGGEST_URI_PATH_QUERY);
2017 
2018         // get the query selection, may be null
2019         String selection = searchable.getSuggestSelection();
2020         // inject query, either as selection args or inline
2021         String[] selArgs = null;
2022         if (selection != null) {    // use selection if provided
2023             selArgs = new String[] { query };
2024         } else {                    // no selection, use REST pattern
2025             uriBuilder.appendPath(query);
2026         }
2027 
2028         if (limit > 0) {
2029             uriBuilder.appendQueryParameter(SUGGEST_PARAMETER_LIMIT, String.valueOf(limit));
2030         }
2031 
2032         Uri uri = uriBuilder.build();
2033 
2034         // finally, make the query
2035         return mContext.getContentResolver().query(uri, null, selection, selArgs, null);
2036     }
2037 
2038     /**
2039      * Returns a list of the searchable activities that can be included in global search.
2040      *
2041      * @return a list containing searchable information for all searchable activities
2042      *         that have the <code>exported</code> attribute set in their searchable
2043      *         meta-data.
2044      *
2045      * @hide because SearchableInfo is not part of the API.
2046      */
getSearchablesInGlobalSearch()2047     public List<SearchableInfo> getSearchablesInGlobalSearch() {
2048         try {
2049             return mService.getSearchablesInGlobalSearch();
2050         } catch (RemoteException e) {
2051             Log.e(TAG, "getSearchablesInGlobalSearch() failed: " + e);
2052             return null;
2053         }
2054     }
2055 
2056     /**
2057      * Returns a list of the searchable activities that handle web searches.
2058      *
2059      * @return a list of all searchable activities that handle
2060      *         {@link android.content.Intent#ACTION_WEB_SEARCH}.
2061      *
2062      * @hide because SearchableInfo is not part of the API.
2063      */
getSearchablesForWebSearch()2064     public List<SearchableInfo> getSearchablesForWebSearch() {
2065         try {
2066             return mService.getSearchablesForWebSearch();
2067         } catch (RemoteException e) {
2068             Log.e(TAG, "getSearchablesForWebSearch() failed: " + e);
2069             return null;
2070         }
2071     }
2072 
2073     /**
2074      * Returns the default searchable activity for web searches.
2075      *
2076      * @return searchable information for the activity handling web searches by default.
2077      *
2078      * @hide because SearchableInfo is not part of the API.
2079      */
getDefaultSearchableForWebSearch()2080     public SearchableInfo getDefaultSearchableForWebSearch() {
2081         try {
2082             return mService.getDefaultSearchableForWebSearch();
2083         } catch (RemoteException e) {
2084             Log.e(TAG, "getDefaultSearchableForWebSearch() failed: " + e);
2085             return null;
2086         }
2087     }
2088 
2089     /**
2090      * Sets the default searchable activity for web searches.
2091      *
2092      * @param component Name of the component to set as default activity for web searches.
2093      *
2094      * @hide
2095      */
setDefaultWebSearch(ComponentName component)2096     public void setDefaultWebSearch(ComponentName component) {
2097         try {
2098             mService.setDefaultWebSearch(component);
2099         } catch (RemoteException e) {
2100             Log.e(TAG, "setDefaultWebSearch() failed: " + e);
2101         }
2102     }
2103 
debug(String msg)2104     private static void debug(String msg) {
2105         Thread thread = Thread.currentThread();
2106         Log.d(TAG, msg + " (" + thread.getName() + "-" + thread.getId() + ")");
2107     }
2108 }