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