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