SearchManager.java revision 54b6cfa9a9e5b861a9930af873580d6dc20f773c
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.Context; 21import android.content.DialogInterface; 22import android.content.res.Configuration; 23import android.os.Bundle; 24import android.os.Handler; 25import android.os.ServiceManager; 26import android.view.KeyEvent; 27 28/** 29 * This class provides access to the system search services. 30 * 31 * <p>In practice, you won't interact with this class directly, as search 32 * services are provided through methods in {@link android.app.Activity Activity} 33 * methods and the the {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} 34 * {@link android.content.Intent Intent}. This class does provide a basic 35 * overview of search services and how to integrate them with your activities. 36 * If you do require direct access to the Search Manager, do not instantiate 37 * this class directly; instead, retrieve it through 38 * {@link android.content.Context#getSystemService 39 * context.getSystemService(Context.SEARCH_SERVICE)}. 40 * 41 * <p>Topics covered here: 42 * <ol> 43 * <li><a href="#DeveloperGuide">Developer Guide</a> 44 * <li><a href="#HowSearchIsInvoked">How Search Is Invoked</a> 45 * <li><a href="#QuerySearchApplications">Query-Search Applications</a> 46 * <li><a href="#FilterSearchApplications">Filter-Search Applications</a> 47 * <li><a href="#Suggestions">Search Suggestions</a> 48 * <li><a href="#ActionKeys">Action Keys</a> 49 * <li><a href="#SearchabilityMetadata">Searchability Metadata</a> 50 * <li><a href="#PassingSearchContext">Passing Search Context</a> 51 * </ol> 52 * 53 * <a name="DeveloperGuide"></a> 54 * <h3>Developer Guide</h3> 55 * 56 * <p>The ability to search for user, system, or network based data is considered to be 57 * a core user-level feature of the android platform. At any time, the user should be 58 * able to use a familiar command, button, or keystroke to invoke search, and the user 59 * should be able to search any data which is available to them. The goal is to make search 60 * appear to the user as a seamless, system-wide feature. 61 * 62 * <p>In terms of implementation, there are three broad classes of Applications: 63 * <ol> 64 * <li>Applications that are not inherently searchable</li> 65 * <li>Query-Search Applications</li> 66 * <li>Filter-Search Applications</li> 67 * </ol> 68 * <p>These categories, as well as related topics, are discussed in 69 * the sections below. 70 * 71 * <p>Even if your application is not <i>searchable</i>, it can still support the invocation of 72 * search. Please review the section <a href="#HowSearchIsInvoked">How Search Is Invoked</a> 73 * for more information on how to support this. 74 * 75 * <p>Many applications are <i>searchable</i>. These are 76 * the applications which can convert a query string into a list of results. 77 * Within this subset, applications can be grouped loosely into two families: 78 * <ul><li><i>Query Search</i> applications perform batch-mode searches - each query string is 79 * converted to a list of results.</li> 80 * <li><i>Filter Search</i> applications provide live filter-as-you-type searches.</li></ul> 81 * <p>Generally speaking, you would use query search for network-based data, and filter 82 * search for local data, but this is not a hard requirement and applications 83 * are free to use the model that fits them best (or invent a new model). 84 * <p>It should be clear that the search implementation decouples "search 85 * invocation" from "searchable". This satisfies the goal of making search appear 86 * to be "universal". The user should be able to launch any search from 87 * almost any context. 88 * 89 * <a name="HowSearchIsInvoked"></a> 90 * <h3>How Search Is Invoked</h3> 91 * 92 * <p>Unless impossible or inapplicable, all applications should support 93 * invoking the search UI. This means that when the user invokes the search command, 94 * a search UI will be presented to them. The search command is currently defined as a menu 95 * item called "Search" (with an alphabetic shortcut key of "S"), or on some devices, a dedicated 96 * search button key. 97 * <p>If your application is not inherently searchable, you can also allow the search UI 98 * to be invoked in a "web search" mode. If the user enters a search term and clicks the 99 * "Search" button, this will bring the browser to the front and will launch a web-based 100 * search. The user will be able to click the "Back" button and return to your application. 101 * <p>In general this is implemented by your activity, or the {@link android.app.Activity Activity} 102 * base class, which captures the search command and invokes the Search Manager to 103 * display and operate the search UI. You can also cause the search UI to be presented in response 104 * to user keystrokes in your activity (for example, to instantly start filter searching while 105 * viewing a list and typing any key). 106 * <p>The search UI is presented as a floating 107 * window and does not cause any change in the activity stack. If the user 108 * cancels search, the previous activity re-emerges. If the user launches a 109 * search, this will be done by sending a search {@link android.content.Intent Intent} (see below), 110 * and the normal intent-handling sequence will take place (your activity will pause, 111 * etc.) 112 * <p><b>What you need to do:</b> First, you should consider the way in which you want to 113 * handle invoking search. There are four broad (and partially overlapping) categories for 114 * you to choose from. 115 * <ul><li>You can capture the search command yourself, by including a <i>search</i> 116 * button or menu item - and invoking the search UI directly.</li> 117 * <li>You can provide a <i>type-to-search</i> feature, in which search is invoked automatically 118 * when the user enters any characters.</li> 119 * <li>Even if your application is not inherently searchable, you can allow web search, 120 * via the search key (or even via a search menu item). 121 * <li>You can disable search entirely. This should only be used in very rare circumstances, 122 * as search is a system-wide feature and users will expect it to be available in all contexts.</li> 123 * </ul> 124 * 125 * <p><b>How to define a search menu.</b> The system provides the following resources which may 126 * be useful when adding a search item to your menu: 127 * <ul><li>android.R.drawable.ic_search_category_default is an icon you can use in your menu.</li> 128 * <li>{@link #MENU_KEY SearchManager.MENU_KEY} is the recommended alphabetic shortcut.</li> 129 * </ul> 130 * 131 * <p><b>How to invoke search directly.</b> In order to invoke search directly, from a button 132 * or menu item, you can launch a generic search by calling 133 * {@link android.app.Activity#onSearchRequested onSearchRequested} as shown: 134 * <pre class="prettyprint"> 135 * onSearchRequested();</pre> 136 * 137 * <p><b>How to implement type-to-search.</b> While setting up your activity, call 138 * {@link android.app.Activity#setDefaultKeyMode setDefaultKeyMode}: 139 * <pre class="prettyprint"> 140 * setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL); // search within your activity 141 * setDefaultKeyMode(DEFAULT_KEYS_SEARCH_GLOBAL); // search using platform global search</pre> 142 * 143 * <p><b>How to enable web-based search.</b> In addition to searching within your activity or 144 * application, you can also use the Search Manager to invoke a platform-global search, typically 145 * a web search. There are two ways to do this: 146 * <ul><li>You can simply define "search" within your application or activity to mean global search. 147 * This is described in more detail in the 148 * <a href="#SearchabilityMetadata">Searchability Metadata</a> section. Briefly, you will 149 * add a single meta-data entry to your manifest, declaring that the default search 150 * for your application is "*". This indicates to the system that no application-specific 151 * search activity is provided, and that it should launch web-based search instead.</li> 152 * <li>You can specify this at invocation time via default keys (see above), overriding 153 * {@link android.app.Activity#onSearchRequested}, or via a direct call to 154 * {@link android.app.Activity#startSearch}. This is most useful if you wish to provide local 155 * searchability <i>and</i> access to global search.</li></ul> 156 * 157 * <p><b>How to disable search from your activity.</b> search is a system-wide feature and users 158 * will expect it to be available in all contexts. If your UI design absolutely precludes 159 * launching search, override {@link android.app.Activity#onSearchRequested onSearchRequested} 160 * as shown: 161 * <pre class="prettyprint"> 162 * @Override 163 * public boolean onSearchRequested() { 164 * return false; 165 * }</pre> 166 * 167 * <p><b>Managing focus and knowing if Search is active.</b> The search UI is not a separate 168 * activity, and when the UI is invoked or dismissed, your activity will not typically be paused, 169 * resumed, or otherwise notified by the methods defined in 170 * <a href="android.app.Activity#ActivityLifecycle">Activity Lifecycle</a>. The search UI is 171 * handled in the same way as other system UI elements which may appear from time to time, such as 172 * notifications, screen locks, or other system alerts: 173 * <p>When the search UI appears, your activity will lose input focus. 174 * <p>When the search activity is dismissed, there are three possible outcomes: 175 * <ul><li>If the user simply canceled the search UI, your activity will regain input focus and 176 * proceed as before. See {@link #setOnDismissListener} and {@link #setOnCancelListener} if you 177 * required direct notification of search dialog dismissals.</li> 178 * <li>If the user launched a search, and this required switching to another activity to receive 179 * and process the search {@link android.content.Intent Intent}, your activity will receive the 180 * normal sequence of activity pause or stop notifications.</li> 181 * <li>If the user launched a search, and the current activity is the recipient of the search 182 * {@link android.content.Intent Intent}, you will receive notification via the 183 * {@link android.app.Activity#onNewIntent onNewIntent()} method.</li></ul> 184 * <p>This list is provided in order to clarify the ways in which your activities will interact with 185 * the search UI. More details on searchable activities and search intents are provided in the 186 * sections below. 187 * 188 * <a name="QuerySearchApplications"></a> 189 * <h3>Query-Search Applications</h3> 190 * 191 * <p>Query-search applications are those that take a single query (e.g. a search 192 * string) and present a set of results that may fit. Primary examples include 193 * web queries, map lookups, or email searches (with the common thread being 194 * network query dispatch). It may also be the case that certain local searches 195 * are treated this way. It's up to the application to decide. 196 * 197 * <p><b>What you need to do:</b> The following steps are necessary in order to 198 * implement query search. 199 * <ul> 200 * <li>Implement search invocation as described above. (Strictly speaking, 201 * these are decoupled, but it would make little sense to be "searchable" but not 202 * "search-invoking".)</li> 203 * <li>Your application should have an activity that takes a search string and 204 * converts it to a list of results. This could be your primary display activity 205 * or it could be a dedicated search results activity. This is your <i>searchable</i> 206 * activity and every query-search application must have one.</li> 207 * <li>In the searchable activity, in onCreate(), you must receive and handle the 208 * {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} 209 * {@link android.content.Intent Intent}. The text to search (query string) for is provided by 210 * calling 211 * {@link #QUERY getStringExtra(SearchManager.QUERY)}.</li> 212 * <li>To identify and support your searchable activity, you'll need to 213 * provide an XML file providing searchability configuration parameters, a reference to that 214 * in your searchable activity's <a href="../../../devel/bblocks-manifest.html">manifest</a> 215 * entry, and an intent-filter declaring that you can 216 * receive ACTION_SEARCH intents. This is described in more detail in the 217 * <a href="#SearchabilityMetadata">Searchability Metadata</a> section.</li> 218 * <li>Your <a href="../../../devel/bblocks-manifest.html">manifest</a> also needs a metadata entry 219 * providing a global reference to the searchable activity. This is the "glue" directing the search 220 * UI, when invoked from any of your <i>other</i> activities, to use your application as the 221 * default search context. This is also described in more detail in the 222 * <a href="#SearchabilityMetadata">Searchability Metadata</a> section.</li> 223 * <li>Finally, you may want to define your search results activity as with the 224 * {@link android.R.attr#launchMode singleTop} launchMode flag. This allows the system 225 * to launch searches from/to the same activity without creating a pile of them on the 226 * activity stack. If you do this, be sure to also override 227 * {@link android.app.Activity#onNewIntent onNewIntent} to handle the 228 * updated intents (with new queries) as they arrive.</li> 229 * </ul> 230 * 231 * <p>Code snippet showing handling of intents in your search activity: 232 * <pre class="prettyprint"> 233 * @Override 234 * protected void onCreate(Bundle icicle) { 235 * super.onCreate(icicle); 236 * 237 * final Intent queryIntent = getIntent(); 238 * final String queryAction = queryIntent.getAction(); 239 * if (Intent.ACTION_SEARCH.equals(queryAction)) { 240 * doSearchWithIntent(queryIntent); 241 * } 242 * } 243 * 244 * private void doSearchWithIntent(final Intent queryIntent) { 245 * final String queryString = queryIntent.getStringExtra(SearchManager.QUERY); 246 * doSearchWithQuery(queryString); 247 * }</pre> 248 * 249 * <a name="FilterSearchApplications"></a> 250 * <h3>Filter-Search Applications</h3> 251 * 252 * <p>Filter-search applications are those that use live text entry (e.g. keystrokes)) to 253 * display and continuously update a list of results. Primary examples include applications 254 * that use locally-stored data. 255 * 256 * <p>Filter search is not directly supported by the Search Manager. Most filter search 257 * implementations will use variants of {@link android.widget.Filterable}, such as a 258 * {@link android.widget.ListView} bound to a {@link android.widget.SimpleCursorAdapter}. However, 259 * you may find it useful to mix them together, by declaring your filtered view searchable. With 260 * this configuration, you can still present the standard search dialog in all activities 261 * within your application, but transition to a filtered search when you enter the activity 262 * and display the results. 263 * 264 * <a name="Suggestions"></a> 265 * <h3>Search Suggestions</h3> 266 * 267 * <p>A powerful feature of the Search Manager is the ability of any application to easily provide 268 * live "suggestions" in order to prompt the user. Each application implements suggestions in a 269 * different, unique, and appropriate way. Suggestions be drawn from many sources, including but 270 * not limited to: 271 * <ul> 272 * <li>Actual searchable results (e.g. names in the address book)</li> 273 * <li>Recently entered queries</li> 274 * <li>Recently viewed data or results</li> 275 * <li>Contextually appropriate queries or results</li> 276 * <li>Summaries of possible results</li> 277 * </ul> 278 * 279 * <p>Another feature of suggestions is that they can expose queries or results before the user 280 * ever visits the application. This reduces the amount of context switching required, and helps 281 * the user access their data quickly and with less context shifting. In order to provide this 282 * capability, suggestions are accessed via a 283 * {@link android.content.ContentProvider Content Provider}. 284 * 285 * <p>The primary form of suggestions is known as <i>queried suggestions</i> and is based on query 286 * text that the user has already typed. This would generally be based on partial matches in 287 * the available data. In certain situations - for example, when no query text has been typed yet - 288 * an application may also opt to provide <i>zero-query suggestions</i>. 289 * These would typically be drawn from the same data source, but because no partial query text is 290 * available, they should be weighted based on other factors - for example, most recent queries 291 * or most recent results. 292 * 293 * <p><b>Overview of how suggestions are provided.</b> When the search manager identifies a 294 * particular activity as searchable, it will check for certain metadata which indicates that 295 * there is also a source of suggestions. If suggestions are provided, the following steps are 296 * taken. 297 * <ul><li>Using formatting information found in the metadata, the user's query text (whatever 298 * has been typed so far) will be formatted into a query and sent to the suggestions 299 * {@link android.content.ContentProvider Content Provider}.</li> 300 * <li>The suggestions {@link android.content.ContentProvider Content Provider} will create a 301 * {@link android.database.Cursor Cursor} which can iterate over the possible suggestions.</li> 302 * <li>The search manager will populate a list using display data found in each row of the cursor, 303 * and display these suggestions to the user.</li> 304 * <li>If the user types another key, or changes the query in any way, the above steps are repeated 305 * and the suggestions list is updated or repopulated.</li> 306 * <li>If the user clicks or touches the "GO" button, the suggestions are ignored and the search is 307 * launched using the normal {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} type of 308 * {@link android.content.Intent Intent}.</li> 309 * <li>If the user uses the directional controls to navigate the focus into the suggestions list, 310 * the query text will be updated while the user navigates from suggestion to suggestion. The user 311 * can then click or touch the updated query and edit it further. If the user navigates back to 312 * the edit field, the original typed query is restored.</li> 313 * <li>If the user clicks or touches a particular suggestion, then a combination of data from the 314 * cursor and 315 * values found in the metadata are used to synthesize an Intent and send it to the application. 316 * Depending on the design of the activity and the way it implements search, this might be a 317 * {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} (in order to launch a query), or it 318 * might be a {@link android.content.Intent#ACTION_VIEW ACTION_VIEW}, in order to proceed directly 319 * to display of specific data.</li> 320 * </ul> 321 * 322 * <p><b>Simple Recent-Query-Based Suggestions.</b> The Android framework provides a simple Search 323 * Suggestions provider, which simply records and replays recent queries. For many applications, 324 * this will be sufficient. The basic steps you will need to 325 * do, in order to use the built-in recent queries suggestions provider, are as follows: 326 * <ul> 327 * <li>Implement and test query search, as described in the previous sections.</li> 328 * <li>Create a Provider within your application by extending 329 * {@link android.content.SearchRecentSuggestionsProvider}.</li> 330 * <li>Create a manifest entry describing your provider.</li> 331 * <li>Update your searchable activity's XML configuration file with information about your 332 * provider.</li> 333 * <li>In your searchable activities, capture any user-generated queries and record them 334 * for future searches by calling {@link android.provider.SearchRecentSuggestions#saveRecentQuery}. 335 * </li> 336 * </ul> 337 * <p>For complete implementation details, please refer to 338 * {@link android.content.SearchRecentSuggestionsProvider}. The rest of the information in this 339 * section should not be necessary, as it refers to custom suggestions providers. 340 * 341 * <p><b>Creating a Customized Suggestions Provider:</b> In order to create more sophisticated 342 * suggestion providers, you'll need to take the following steps: 343 * <ul> 344 * <li>Implement and test query search, as described in the previous sections.</li> 345 * <li>Decide how you wish to <i>receive</i> suggestions. Just like queries that the user enters, 346 * suggestions will be delivered to your searchable activity as 347 * {@link android.content.Intent Intent} messages; Unlike simple queries, you have quite a bit of 348 * flexibility in forming those intents. A query search application will probably 349 * wish to continue receiving the {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} 350 * {@link android.content.Intent Intent}, which will launch a query search using query text as 351 * provided by the suggestion. A filter search application will probably wish to 352 * receive the {@link android.content.Intent#ACTION_VIEW ACTION_VIEW} 353 * {@link android.content.Intent Intent}, which will take the user directly to a selected entry. 354 * Other interesting suggestions, including hybrids, are possible, and the suggestion provider 355 * can easily mix-and-match results to provide a richer set of suggestions for the user. Finally, 356 * you'll need to update your searchable activity (or other activities) to receive the intents 357 * as you've defined them.</li> 358 * <li>Implement a Content Provider that provides suggestions. If you already have one, and it 359 * has access to your suggestions data. If not, you'll have to create one. 360 * You'll also provide information about your Content Provider in your 361 * package's <a href="../../../devel/bblocks-manifest.html">manifest</a>.</li> 362 * <li>Update your searchable activity's XML configuration file. There are two categories of 363 * information used for suggestions: 364 * <ul><li>The first is (required) data that the search manager will 365 * use to format the queries which are sent to the Content Provider.</li> 366 * <li>The second is (optional) parameters to configure structure 367 * if intents generated by suggestions.</li></li> 368 * </ul> 369 * </ul> 370 * 371 * <p><b>Configuring your Content Provider to Receive Suggestion Queries.</b> The basic job of 372 * a search suggestions {@link android.content.ContentProvider Content Provider} is to provide 373 * "live" (while-you-type) conversion of the user's query text into a set of zero or more 374 * suggestions. Each application is free to define the conversion, and as described above there are 375 * many possible solutions. This section simply defines how to communicate with the suggestion 376 * provider. 377 * 378 * <p>The Search Manager must first determine if your package provides suggestions. This is done 379 * by examination of your searchable meta-data XML file. The android:searchSuggestAuthority 380 * attribute, if provided, is the signal to obtain & display suggestions. 381 * 382 * <p>Every query includes a Uri, and the Search Manager will format the Uri as shown: 383 * <p><pre class="prettyprint"> 384 * content:// your.suggest.authority / your.suggest.path / SearchManager.SUGGEST_URI_PATH_QUERY</pre> 385 * 386 * <p>Your Content Provider can receive the query text in one of two ways. 387 * <ul> 388 * <li><b>Query provided as a selection argument.</b> If you define the attribute value 389 * android:searchSuggestSelection and include a string, this string will be passed as the 390 * <i>selection</i> parameter to your Content Provider's query function. You must define a single 391 * selection argument, using the '?' character. The user's query text will be passed to you 392 * as the first element of the selection arguments array.</li> 393 * <li><b>Query provided with Data Uri.</b> If you <i>do not</i> define the attribute value 394 * android:searchSuggestSelection, then the Search Manager will append another "/" followed by 395 * the user's query to the query Uri. The query will be encoding using Uri encoding rules - don't 396 * forget to decode it. (See {@link android.net.Uri#getPathSegments} and 397 * {@link android.net.Uri#getLastPathSegment} for helpful utilities you can use here.)</li> 398 * </ul> 399 * 400 * <p><b>Handling empty queries.</b> Your application should handle the "empty query" 401 * (no user text entered) case properly, and generate useful suggestions in this case. There are a 402 * number of ways to do this; Two are outlined here: 403 * <ul><li>For a simple filter search of local data, you could simply present the entire dataset, 404 * unfiltered. (example: People)</li> 405 * <li>For a query search, you could simply present the most recent queries. This allows the user 406 * to quickly repeat a recent search.</li></ul> 407 * 408 * <p><b>The Format of Individual Suggestions.</b> Your suggestions are communicated back to the 409 * Search Manager by way of a {@link android.database.Cursor Cursor}. The Search Manager will 410 * usually pass a null Projection, which means that your provider can simply return all appropriate 411 * columns for each suggestion. The columns currently defined are: 412 * 413 * <table border="2" width="85%" align="center" frame="hsides" rules="rows"> 414 * 415 * <thead> 416 * <tr><th>Column Name</th> <th>Description</th> <th>Required?</th></tr> 417 * </thead> 418 * 419 * <tbody> 420 * <tr><th>{@link #SUGGEST_COLUMN_FORMAT}</th> 421 * <td><i>Unused - can be null.</i></td> 422 * <td align="center">No</td> 423 * </tr> 424 * 425 * <tr><th>{@link #SUGGEST_COLUMN_TEXT_1}</th> 426 * <td>This is the line of text that will be presented to the user as the suggestion.</td> 427 * <td align="center">Yes</td> 428 * </tr> 429 * 430 * <tr><th>{@link #SUGGEST_COLUMN_TEXT_2}</th> 431 * <td>If your cursor includes this column, then all suggestions will be provided in a 432 * two-line format. The data in this column will be displayed as a second, smaller 433 * line of text below the primary suggestion, or it can be null or empty to indicate no 434 * text in this row's suggestion.</td> 435 * <td align="center">No</td> 436 * </tr> 437 * 438 * <tr><th>{@link #SUGGEST_COLUMN_ICON_1}</th> 439 * <td>If your cursor includes this column, then all suggestions will be provided in an 440 * icons+text format. This value should be a reference (resource ID) of the icon to 441 * draw on the left side, or it can be null or zero to indicate no icon in this row. 442 * You must provide both cursor columns, or neither. 443 * </td> 444 * <td align="center">No, but required if you also have {@link #SUGGEST_COLUMN_ICON_2}</td> 445 * </tr> 446 * 447 * <tr><th>{@link #SUGGEST_COLUMN_ICON_2}</th> 448 * <td>If your cursor includes this column, then all suggestions will be provided in an 449 * icons+text format. This value should be a reference (resource ID) of the icon to 450 * draw on the right side, or it can be null or zero to indicate no icon in this row. 451 * You must provide both cursor columns, or neither. 452 * </td> 453 * <td align="center">No, but required if you also have {@link #SUGGEST_COLUMN_ICON_1}</td> 454 * </tr> 455 * 456 * <tr><th>{@link #SUGGEST_COLUMN_INTENT_ACTION}</th> 457 * <td>If this column exists <i>and</i> this element exists at the given row, this is the 458 * action that will be used when forming the suggestion's intent. If the element is 459 * not provided, the action will be taken from the android:searchSuggestIntentAction 460 * field in your XML metadata. <i>At least one of these must be present for the 461 * suggestion to generate an intent.</i> Note: If your action is the same for all 462 * suggestions, it is more efficient to specify it using XML metadata and omit it from 463 * the cursor.</td> 464 * <td align="center">No</td> 465 * </tr> 466 * 467 * <tr><th>{@link #SUGGEST_COLUMN_INTENT_DATA}</th> 468 * <td>If this column exists <i>and</i> this element exists at the given row, this is the 469 * data that will be used when forming the suggestion's intent. If the element is not 470 * provided, the data will be taken from the android:searchSuggestIntentData field in 471 * your XML metadata. If neither source is provided, the Intent's data field will be 472 * null. Note: If your data is the same for all suggestions, or can be described 473 * using a constant part and a specific ID, it is more efficient to specify it using 474 * XML metadata and omit it from the cursor.</td> 475 * <td align="center">No</td> 476 * </tr> 477 * 478 * <tr><th>{@link #SUGGEST_COLUMN_INTENT_DATA_ID}</th> 479 * <td>If this column exists <i>and</i> this element exists at the given row, then "/" and 480 * this value will be appended to the data field in the Intent. This should only be 481 * used if the data field has already been set to an appropriate base string.</td> 482 * <td align="center">No</td> 483 * </tr> 484 * 485 * <tr><th>{@link #SUGGEST_COLUMN_QUERY}</th> 486 * <td>If this column exists <i>and</i> this element exists at the given row, this is the 487 * data that will be used when forming the suggestion's query.</td> 488 * <td align="center">Required if suggestion's action is 489 * {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH}, optional otherwise.</td> 490 * </tr> 491 * 492 * <tr><th><i>Other Columns</i></th> 493 * <td>Finally, if you have defined any <a href="#ActionKeys">Action Keys</a> and you wish 494 * for them to have suggestion-specific definitions, you'll need to define one 495 * additional column per action key. The action key will only trigger if the 496 * currently-selection suggestion has a non-empty string in the corresponding column. 497 * See the section on <a href="#ActionKeys">Action Keys</a> for additional details and 498 * implementation steps.</td> 499 * <td align="center">No</td> 500 * </tr> 501 * 502 * </tbody> 503 * </table> 504 * 505 * <p>Clearly there are quite a few permutations of your suggestion data, but in the next section 506 * we'll look at a few simple combinations that you'll select from. 507 * 508 * <p><b>The Format Of Intents Sent By Search Suggestions.</b> Although there are many ways to 509 * configure these intents, this document will provide specific information on just a few of them. 510 * <ul><li><b>Launch a query.</b> In this model, each suggestion represents a query that your 511 * searchable activity can perform, and the {@link android.content.Intent Intent} will be formatted 512 * exactly like those sent when the user enters query text and clicks the "GO" button: 513 * <ul> 514 * <li><b>Action:</b> {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} provided 515 * using your XML metadata (android:searchSuggestIntentAction).</li> 516 * <li><b>Data:</b> empty (not used).</li> 517 * <li><b>Query:</b> query text supplied by the cursor.</li> 518 * </ul> 519 * </li> 520 * <li><b>Go directly to a result, using a complete Data Uri.</b> In this model, the user will be 521 * taken directly to a specific result. 522 * <ul> 523 * <li><b>Action:</b> {@link android.content.Intent#ACTION_VIEW ACTION_VIEW}</li> 524 * <li><b>Data:</b> a complete Uri, supplied by the cursor, that identifies the desired data.</li> 525 * <li><b>Query:</b> query text supplied with the suggestion (probably ignored)</li> 526 * </ul> 527 * </li> 528 * <li><b>Go directly to a result, using a synthesized Data Uri.</b> This has the same result 529 * as the previous suggestion, but provides the Data Uri in a different way. 530 * <ul> 531 * <li><b>Action:</b> {@link android.content.Intent#ACTION_VIEW ACTION_VIEW}</li> 532 * <li><b>Data:</b> The search manager will assemble a Data Uri using the following elements: 533 * a Uri fragment provided in your XML metadata (android:searchSuggestIntentData), followed by 534 * a single "/", followed by the value found in the {@link #SUGGEST_COLUMN_INTENT_DATA_ID} 535 * entry in your cursor.</li> 536 * <li><b>Query:</b> query text supplied with the suggestion (probably ignored)</li> 537 * </ul> 538 * </li> 539 * </ul> 540 * <p>This list is not meant to be exhaustive. Applications should feel free to define other types 541 * of suggestions. For example, you could reduce long lists of results to summaries, and use one 542 * of the above intents (or one of your own) with specially formatted Data Uri's to display more 543 * detailed results. Or you could display textual shortcuts as suggestions, but launch a display 544 * in a more data-appropriate format such as media artwork. 545 * 546 * <p><b>Suggestion Rewriting.</b> If the user navigates through the suggestions list, the UI 547 * may temporarily rewrite the user's query with a query that matches the currently selected 548 * suggestion. This enables the user to see what query is being suggested, and also allows the user 549 * to click or touch in the entry EditText element and make further edits to the query before 550 * dispatching it. In order to perform this correctly, the Search UI needs to know exactly what 551 * text to rewrite the query with. 552 * 553 * <p>For each suggestion, the following logic is used to select a new query string: 554 * <ul><li>If the suggestion provides an explicit value in the {@link #SUGGEST_COLUMN_QUERY} 555 * column, this value will be used.</li> 556 * <li>If the metadata includes the queryRewriteFromData flag, and the suggestion provides an 557 * explicit value for the intent Data field, this Uri will be used. Note that this should only be 558 * used with Uri's that are intended to be user-visible, such as HTTP. Internal Uri schemes should 559 * not be used in this way.</li> 560 * <li>If the metadata includes the queryRewriteFromText flag, the text in 561 * {@link #SUGGEST_COLUMN_TEXT_1} will be used. This should be used for suggestions in which no 562 * query text is provided and the SUGGEST_COLUMN_INTENT_DATA values are not suitable for user 563 * inspection and editing.</li></ul> 564 * 565 * <a name="ActionKeys"></a> 566 * <h3>Action Keys</h3> 567 * 568 * <p>Searchable activities may also wish to provide shortcuts based on the various action keys 569 * available on the device. The most basic example of this is the contacts app, which enables the 570 * green "dial" key for quick access during searching. Not all action keys are available on 571 * every device, and not all are allowed to be overriden in this way. (For example, the "Home" 572 * key must always return to the home screen, with no exceptions.) 573 * 574 * <p>In order to define action keys for your searchable application, you must do two things. 575 * 576 * <ul> 577 * <li>You'll add one or more <i>actionkey</i> elements to your searchable metadata configuration 578 * file. Each element defines one of the keycodes you are interested in, 579 * defines the conditions under which they are sent, and provides details 580 * on how to communicate the action key event back to your searchable activity.</li> 581 * <li>In your intent receiver, if you wish, you can check for action keys by checking the 582 * extras field of the {@link android.content.Intent Intent}.</li> 583 * </ul> 584 * 585 * <p><b>Updating metadata.</b> For each keycode of interest, you must add an <actionkey> 586 * element. Within this element you must define two or three attributes. The first attribute, 587 * <android:keycode>, is required; It is the key code of the action key event, as defined in 588 * {@link android.view.KeyEvent}. The remaining two attributes define the value of the actionkey's 589 * <i>message</i>, which will be passed to your searchable activity in the 590 * {@link android.content.Intent Intent} (see below for more details). Although each of these 591 * attributes is optional, you must define one or both for the action key to have any effect. 592 * <android:queryActionMsg> provides the message that will be sent if the action key is 593 * pressed while the user is simply entering query text. <android:suggestActionMsgColumn> 594 * is used when action keys are tied to specific suggestions. This attribute provides the name 595 * of a <i>column</i> in your suggestion cursor; The individual suggestion, in that column, 596 * provides the message. (If the cell is empty or null, that suggestion will not work with that 597 * action key.) 598 * <p>See the <a href="#SearchabilityMetadata">Searchability Metadata</a> section for more details 599 * and examples. 600 * 601 * <p><b>Receiving Action Keys</b> Intents launched by action keys will be specially marked 602 * using a combination of values. This enables your searchable application to examine the intent, 603 * if necessary, and perform special processing. For example, clicking a suggested contact might 604 * simply display them; Selecting a suggested contact and clicking the dial button might 605 * immediately call them. 606 * 607 * <p>When a search {@link android.content.Intent Intent} is launched by an action key, two values 608 * will be added to the extras field. 609 * <ul> 610 * <li>To examine the key code, use {@link android.content.Intent#getIntExtra 611 * getIntExtra(SearchManager.ACTION_KEY)}.</li> 612 * <li>To examine the message string, use {@link android.content.Intent#getStringExtra 613 * getStringExtra(SearchManager.ACTION_MSG)}</li> 614 * </ul> 615 * 616 * <a name="SearchabilityMetadata"></a> 617 * <h3>Searchability Metadata</h3> 618 * 619 * <p>Every activity that is searchable must provide a small amount of additional information 620 * in order to properly configure the search system. This controls the way that your search 621 * is presented to the user, and controls for the various modalities described previously. 622 * 623 * <p>If your application is not searchable, 624 * then you do not need to provide any search metadata, and you can skip the rest of this section. 625 * When this search metadata cannot be found, the search manager will assume that the activity 626 * does not implement search. (Note: to implement web-based search, you will need to add 627 * the android.app.default_searchable metadata to your manifest, as shown below.) 628 * 629 * <p>Values you supply in metadata apply only to each local searchable activity. Each 630 * searchable activity can define a completely unique search experience relevant to its own 631 * capabilities and user experience requirements, and a single application can even define multiple 632 * searchable activities. 633 * 634 * <p><b>Metadata for searchable activity.</b> As with your search implementations described 635 * above, you must first identify which of your activities is searchable. In the 636 * <a href="../../../devel/bblocks-manifest.html">manifest</a> entry for this activity, you must 637 * provide two elements: 638 * <ul><li>An intent-filter specifying that you can receive and process the 639 * {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} {@link android.content.Intent Intent}. 640 * </li> 641 * <li>A reference to a small XML file (typically called "searchable.xml") which contains the 642 * remaining configuration information for how your application implements search.</li></ul> 643 * 644 * <p>Here is a snippet showing the necessary elements in the 645 * <a href="../../../devel/bblocks-manifest.html">manifest</a> entry for your searchable activity. 646 * <pre class="prettyprint"> 647 * <!-- Search Activity - searchable --> 648 * <activity android:name="MySearchActivity" 649 * android:label="Search" 650 * android:launchMode="singleTop"> 651 * <intent-filter> 652 * <action android:name="android.intent.action.SEARCH" /> 653 * <category android:name="android.intent.category.DEFAULT" /> 654 * </intent-filter> 655 * <meta-data android:name="android.app.searchable" 656 * android:resource="@xml/searchable" /> 657 * </activity></pre> 658 * 659 * <p>Next, you must provide the rest of the searchability configuration in 660 * the small XML file, stored in the ../xml/ folder in your build. The XML file is a 661 * simple enumeration of the search configuration parameters for searching within this activity, 662 * application, or package. Here is a sample XML file (named searchable.xml, for use with 663 * the above manifest) for a query-search activity. 664 * 665 * <pre class="prettyprint"> 666 * <searchable xmlns:android="http://schemas.android.com/apk/res/android" 667 * android:label="@string/search_label" 668 * android:hint="@string/search_hint" > 669 * </searchable></pre> 670 * 671 * <p>Note that all user-visible strings <i>must</i> be provided in the form of "@string" 672 * references. Hard-coded strings, which cannot be localized, will not work properly in search 673 * metadata. 674 * 675 * <p>Attributes you can set in search metadata: 676 * <table border="2" width="85%" align="center" frame="hsides" rules="rows"> 677 * 678 * <thead> 679 * <tr><th>Attribute</th> <th>Description</th> <th>Required?</th></tr> 680 * </thead> 681 * 682 * <tbody> 683 * <tr><th>android:label</th> 684 * <td>This is the name for your application that will be presented to the user in a 685 * list of search targets, or in the search box as a label.</td> 686 * <td align="center">Yes</td> 687 * </tr> 688 * 689 * <tr><th>android:icon</th> 690 * <td>If provided, this icon will be used <i>in place</i> of the label string. This 691 * is provided in order to present logos or other non-textual banners.</td> 692 * <td align="center">No</td> 693 * </tr> 694 * 695 * <tr><th>android:hint</th> 696 * <td>This is the text to display in the search text field when no user text has been 697 * entered.</td> 698 * <td align="center">No</td> 699 * </tr> 700 * 701 * <tr><th>android:searchButtonText</th> 702 * <td>If provided, this text will replace the default text in the "Search" button.</td> 703 * <td align="center">No</td> 704 * </tr> 705 * 706 * <tr><th>android:searchMode</th> 707 * <td>If provided and non-zero, sets additional modes for control of the search 708 * presentation. The following mode bits are defined: 709 * <table border="2" align="center" frame="hsides" rules="rows"> 710 * <tbody> 711 * <tr><th>showSearchLabelAsBadge</th> 712 * <td>If set, this flag enables the display of the search target (label) 713 * within the search bar. If this flag and showSearchIconAsBadge 714 * (see below) are both not set, no badge will be shown.</td> 715 * </tr> 716 * <tr><th>showSearchIconAsBadge</th> 717 * <td>If set, this flag enables the display of the search target (icon) within 718 * the search bar. If this flag and showSearchLabelAsBadge 719 * (see above) are both not set, no badge will be shown. If both flags 720 * are set, showSearchIconAsBadge has precedence and the icon will be 721 * shown.</td> 722 * </tr> 723 * <tr><th>queryRewriteFromData</th> 724 * <td>If set, this flag causes the suggestion column SUGGEST_COLUMN_INTENT_DATA 725 * to be considered as the text for suggestion query rewriting. This should 726 * only be used when the values in SUGGEST_COLUMN_INTENT_DATA are suitable 727 * for user inspection and editing - typically, HTTP/HTTPS Uri's.</td> 728 * </tr> 729 * <tr><th>queryRewriteFromText</th> 730 * <td>If set, this flag causes the suggestion column SUGGEST_COLUMN_TEXT_1 to 731 * be considered as the text for suggestion query rewriting. This should 732 * be used for suggestions in which no query text is provided and the 733 * SUGGEST_COLUMN_INTENT_DATA values are not suitable for user inspection 734 * and editing.</td> 735 * </tr> 736 * </tbody> 737 * </table></td> 738 * <td align="center">No</td> 739 * </tr> 740 * 741 * </tbody> 742 * </table> 743 * 744 * <p><b>Styleable Resources in your Metadata.</b> It's possible to provide alternate strings 745 * for your searchable application, in order to provide localization and/or to better visual 746 * presentation on different device configurations. Each searchable activity has a single XML 747 * metadata file, but any resource references can be replaced at runtime based on device 748 * configuration, language setting, and other system inputs. 749 * 750 * <p>A concrete example is the "hint" text you supply using the android:searchHint attribute. 751 * In portrait mode you'll have less screen space and may need to provide a shorter string, but 752 * in landscape mode you can provide a longer, more descriptive hint. To do this, you'll need to 753 * define two or more strings.xml files, in the following directories: 754 * <ul><li>.../res/values-land/strings.xml</li> 755 * <li>.../res/values-port/strings.xml</li> 756 * <li>.../res/values/strings.xml</li></ul> 757 * 758 * <p>For more complete documentation on this capability, see 759 * <a href="../../../devel/resources-i18n.html#AlternateResources">Resources and 760 * Internationalization: Supporting Alternate Resources for Alternate Languages and Configurations 761 * </a>. 762 * 763 * <p><b>Metadata for non-searchable activities.</b> Activities which are part of a searchable 764 * application, but don't implement search itself, require a bit of "glue" in order to cause 765 * them to invoke search using your searchable activity as their primary context. If this is not 766 * provided, then searches from these activities will use the system default search context. 767 * 768 * <p>The simplest way to specify this is to add a <i>search reference</i> element to the 769 * application entry in the <a href="../../../devel/bblocks-manifest.html">manifest</a> file. 770 * The value of this reference can be either of: 771 * <ul><li>The name of your searchable activity. 772 * It is typically prefixed by '.' to indicate that it's in the same package.</li> 773 * <li>A "*" indicates that the system may select a default searchable activity, in which 774 * case it will typically select web-based search.</li> 775 * </ul> 776 * 777 * <p>Here is a snippet showing the necessary addition to the manifest entry for your 778 * non-searchable activities. 779 * <pre class="prettyprint"> 780 * <application> 781 * <meta-data android:name="android.app.default_searchable" 782 * android:value=".MySearchActivity" /> 783 * 784 * <!-- followed by activities, providers, etc... --> 785 * </application></pre> 786 * 787 * <p>You can also specify android.app.default_searchable on a per-activity basis, by including 788 * the meta-data element (as shown above) in one or more activity sections. If found, these will 789 * override the reference in the application section. The only reason to configure your application 790 * this way would be if you wish to partition it into separate sections with different search 791 * behaviors; Otherwise this configuration is not recommended. 792 * 793 * <p><b>Additional Metadata for search suggestions.</b> If you have defined a content provider 794 * to generate search suggestions, you'll need to publish it to the system, and you'll need to 795 * provide a bit of additional XML metadata in order to configure communications with it. 796 * 797 * <p>First, in your <a href="../../../devel/bblocks-manifest.html">manifest</a>, you'll add the 798 * following lines. 799 * <pre class="prettyprint"> 800 * <!-- Content provider for search suggestions --> 801 * <provider android:name="YourSuggestionProviderClass" 802 * android:authorities="your.suggestion.authority" /></pre> 803 * 804 * <p>Next, you'll add a few lines to your XML metadata file, as shown: 805 * <pre class="prettyprint"> 806 * <!-- Required attribute for any suggestions provider --> 807 * android:searchSuggestAuthority="your.suggestion.authority" 808 * 809 * <!-- Optional attribute for configuring queries --> 810 * android:searchSuggestSelection="field =?" 811 * 812 * <!-- Optional attributes for configuring intent construction --> 813 * android:searchSuggestIntentAction="intent action string" 814 * android:searchSuggestIntentData="intent data Uri" /></pre> 815 * 816 * <p>Elements of search metadata that support suggestions: 817 * <table border="2" width="85%" align="center" frame="hsides" rules="rows"> 818 * 819 * <thead> 820 * <tr><th>Attribute</th> <th>Description</th> <th>Required?</th></tr> 821 * </thead> 822 * 823 * <tbody> 824 * <tr><th>android:searchSuggestAuthority</th> 825 * <td>This value must match the authority string provided in the <i>provider</i> section 826 * of your <a href="../../../devel/bblocks-manifest.html">manifest</a>.</td> 827 * <td align="center">Yes</td> 828 * </tr> 829 * 830 * <tr><th>android:searchSuggestPath</th> 831 * <td>If provided, this will be inserted in the suggestions query Uri, after the authority 832 * you have provide but before the standard suggestions path. This is only required if 833 * you have a single content provider issuing different types of suggestions (e.g. for 834 * different data types) and you need a way to disambiguate the suggestions queries 835 * when they are received.</td> 836 * <td align="center">No</td> 837 * </tr> 838 * 839 * <tr><th>android:searchSuggestSelection</th> 840 * <td>If provided, this value will be passed into your query function as the 841 * <i>selection</i> parameter. Typically this will be a WHERE clause for your database, 842 * and will contain a single question mark, which represents the actual query string 843 * that has been typed by the user. However, you can also use any non-null value 844 * to simply trigger the delivery of the query text (via selection arguments), and then 845 * use the query text in any way appropriate for your provider (ignoring the actual 846 * text of the selection parameter.)</td> 847 * <td align="center">No</td> 848 * </tr> 849 * 850 * <tr><th>android:searchSuggestIntentAction</th> 851 * <td>If provided, and not overridden by the selected suggestion, this value will be 852 * placed in the action field of the {@link android.content.Intent Intent} when the 853 * user clicks a suggestion.</td> 854 * <td align="center">No</td> 855 * 856 * <tr><th>android:searchSuggestIntentData</th> 857 * <td>If provided, and not overridden by the selected suggestion, this value will be 858 * placed in the data field of the {@link android.content.Intent Intent} when the user 859 * clicks a suggestion.</td> 860 * <td align="center">No</td> 861 * </tr> 862 * 863 * </tbody> 864 * </table> 865 * 866 * <p><b>Additional Metadata for search action keys.</b> For each action key that you would like to 867 * define, you'll need to add an additional element defining that key, and using the attributes 868 * discussed in <a href="#ActionKeys">Action Keys</a>. A simple example is shown here: 869 * 870 * <pre class="prettyprint"><actionkey 871 * android:keycode="KEYCODE_CALL" 872 * android:queryActionMsg="call" 873 * android:suggestActionMsg="call" 874 * android:suggestActionMsgColumn="call_column" /></pre> 875 * 876 * <p>Elements of search metadata that support search action keys. Note that although each of the 877 * action message elements are marked as <i>optional</i>, at least one must be present for the 878 * action key to have any effect. 879 * 880 * <table border="2" width="85%" align="center" frame="hsides" rules="rows"> 881 * 882 * <thead> 883 * <tr><th>Attribute</th> <th>Description</th> <th>Required?</th></tr> 884 * </thead> 885 * 886 * <tbody> 887 * <tr><th>android:keycode</th> 888 * <td>This attribute denotes the action key you wish to respond to. Note that not 889 * all action keys are actually supported using this mechanism, as many of them are 890 * used for typing, navigation, or system functions. This will be added to the 891 * {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} intent that is passed to 892 * your searchable activity. To examine the key code, use 893 * {@link android.content.Intent#getIntExtra getIntExtra(SearchManager.ACTION_KEY)}. 894 * <p>Note, in addition to the keycode, you must also provide one or more of the action 895 * specifier attributes.</td> 896 * <td align="center">Yes</td> 897 * </tr> 898 * 899 * <tr><th>android:queryActionMsg</th> 900 * <td>If you wish to handle an action key during normal search query entry, you 901 * must define an action string here. This will be added to the 902 * {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} intent that is passed to your 903 * searchable activity. To examine the string, use 904 * {@link android.content.Intent#getStringExtra 905 * getStringExtra(SearchManager.ACTION_MSG)}.</td> 906 * <td align="center">No</td> 907 * </tr> 908 * 909 * <tr><th>android:suggestActionMsg</th> 910 * <td>If you wish to handle an action key while a suggestion is being displayed <i>and 911 * selected</i>, there are two ways to handle this. If <i>all</i> of your suggestions 912 * can handle the action key, you can simply define the action message using this 913 * attribute. This will be added to the 914 * {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} intent that is passed to 915 * your searchable activity. To examine the string, use 916 * {@link android.content.Intent#getStringExtra 917 * getStringExtra(SearchManager.ACTION_MSG)}.</td> 918 * <td align="center">No</td> 919 * </tr> 920 * 921 * <tr><th>android:suggestActionMsgColumn</th> 922 * <td>If you wish to handle an action key while a suggestion is being displayed <i>and 923 * selected</i>, but you do not wish to enable this action key for every suggestion, 924 * then you can use this attribute to control it on a suggestion-by-suggestion basis. 925 * First, you must define a column (and name it here) where your suggestions will 926 * include the action string. Then, in your content provider, you must provide this 927 * column, and when desired, provide data in this column. 928 * The search manager will look at your suggestion cursor, using the string 929 * provided here in order to select a column, and will use that to select a string from 930 * the cursor. That string will be added to the 931 * {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} intent that is passed to 932 * your searchable activity. To examine the string, use 933 * {@link android.content.Intent#getStringExtra 934 * getStringExtra(SearchManager.ACTION_MSG)}. <i>If the data does not exist for the 935 * selection suggestion, the action key will be ignored.</i></td> 936 * <td align="center">No</td> 937 * </tr> 938 * 939 * </tbody> 940 * </table> 941 * 942 * <a name="PassingSearchContext"></a> 943 * <h3>Passing Search Context</h3> 944 * 945 * <p>In order to improve search experience, an application may wish to specify 946 * additional data along with the search, such as local history or context. For 947 * example, a maps search would be improved by including the current location. 948 * In order to simplify the structure of your activities, this can be done using 949 * the search manager. 950 * 951 * <p>Any data can be provided at the time the search is launched, as long as it 952 * can be stored in a {@link android.os.Bundle Bundle} object. 953 * 954 * <p>To pass application data into the Search Manager, you'll need to override 955 * {@link android.app.Activity#onSearchRequested onSearchRequested} as follows: 956 * 957 * <pre class="prettyprint"> 958 * @Override 959 * public boolean onSearchRequested() { 960 * Bundle appData = new Bundle(); 961 * appData.put...(); 962 * appData.put...(); 963 * startSearch(null, false, appData); 964 * return true; 965 * }</pre> 966 * 967 * <p>To receive application data from the Search Manager, you'll extract it from 968 * the {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} 969 * {@link android.content.Intent Intent} as follows: 970 * 971 * <pre class="prettyprint"> 972 * final Bundle appData = queryIntent.getBundleExtra(SearchManager.APP_DATA); 973 * if (appData != null) { 974 * appData.get...(); 975 * appData.get...(); 976 * }</pre> 977 */ 978public class SearchManager 979 implements DialogInterface.OnDismissListener, DialogInterface.OnCancelListener 980{ 981 /** 982 * This is a shortcut definition for the default menu key to use for invoking search. 983 * 984 * See Menu.Item.setAlphabeticShortcut() for more information. 985 */ 986 public final static char MENU_KEY = 's'; 987 988 /** 989 * This is a shortcut definition for the default menu key to use for invoking search. 990 * 991 * See Menu.Item.setAlphabeticShortcut() for more information. 992 */ 993 public final static int MENU_KEYCODE = KeyEvent.KEYCODE_S; 994 995 /** 996 * Intent extra data key: Use this key with 997 * {@link android.content.Intent#getStringExtra 998 * content.Intent.getStringExtra()} 999 * to obtain the query string from Intent.ACTION_SEARCH. 1000 */ 1001 public final static String QUERY = "query"; 1002 1003 /** 1004 * Intent extra data key: Use this key with Intent.ACTION_SEARCH and 1005 * {@link android.content.Intent#getBundleExtra 1006 * content.Intent.getBundleExtra()} 1007 * to obtain any additional app-specific data that was inserted by the 1008 * activity that launched the search. 1009 */ 1010 public final static String APP_DATA = "app_data"; 1011 1012 /** 1013 * Intent extra data key: Use this key with Intent.ACTION_SEARCH and 1014 * {@link android.content.Intent#getIntExtra content.Intent.getIntExtra()} 1015 * to obtain the keycode that the user used to trigger this query. It will be zero if the 1016 * user simply pressed the "GO" button on the search UI. This is primarily used in conjunction 1017 * with the keycode attribute in the actionkey element of your searchable.xml configuration 1018 * file. 1019 */ 1020 public final static String ACTION_KEY = "action_key"; 1021 1022 /** 1023 * Intent extra data key: Use this key with Intent.ACTION_SEARCH and 1024 * {@link android.content.Intent#getStringExtra content.Intent.getStringExtra()} 1025 * to obtain the action message that was defined for a particular search action key and/or 1026 * suggestion. It will be null if the search was launched by typing "enter", touched the the 1027 * "GO" button, or other means not involving any action key. 1028 */ 1029 public final static String ACTION_MSG = "action_msg"; 1030 1031 /** 1032 * Uri path for queried suggestions data. This is the path that the search manager 1033 * will use when querying your content provider for suggestions data based on user input 1034 * (e.g. looking for partial matches). 1035 * Typically you'll use this with a URI matcher. 1036 */ 1037 public final static String SUGGEST_URI_PATH_QUERY = "search_suggest_query"; 1038 1039 /** 1040 * MIME type for suggestions data. You'll use this in your suggestions content provider 1041 * in the getType() function. 1042 */ 1043 public final static String SUGGEST_MIME_TYPE = 1044 "vnd.android.cursor.dir/vnd.android.search.suggest"; 1045 1046 /** 1047 * Column name for suggestions cursor. <i>Unused - can be null or column can be omitted.</i> 1048 */ 1049 public final static String SUGGEST_COLUMN_FORMAT = "suggest_format"; 1050 /** 1051 * Column name for suggestions cursor. <i>Required.</i> This is the primary line of text that 1052 * will be presented to the user as the suggestion. 1053 */ 1054 public final static String SUGGEST_COLUMN_TEXT_1 = "suggest_text_1"; 1055 /** 1056 * Column name for suggestions cursor. <i>Optional.</i> If your cursor includes this column, 1057 * then all suggestions will be provided in a two-line format. The second line of text is in 1058 * a much smaller appearance. 1059 */ 1060 public final static String SUGGEST_COLUMN_TEXT_2 = "suggest_text_2"; 1061 /** 1062 * Column name for suggestions cursor. <i>Optional.</i> If your cursor includes this column, 1063 * then all suggestions will be provided in format that includes space for two small icons, 1064 * one at the left and one at the right of each suggestion. The data in the column must 1065 * be a a resource ID for the icon you wish to have displayed. If you include this column, 1066 * you must also include {@link #SUGGEST_COLUMN_ICON_2}. 1067 */ 1068 public final static String SUGGEST_COLUMN_ICON_1 = "suggest_icon_1"; 1069 /** 1070 * Column name for suggestions cursor. <i>Optional.</i> If your cursor includes this column, 1071 * then all suggestions will be provided in format that includes space for two small icons, 1072 * one at the left and one at the right of each suggestion. The data in the column must 1073 * be a a resource ID for the icon you wish to have displayed. If you include this column, 1074 * you must also include {@link #SUGGEST_COLUMN_ICON_1}. 1075 */ 1076 public final static String SUGGEST_COLUMN_ICON_2 = "suggest_icon_2"; 1077 /** 1078 * Column name for suggestions cursor. <i>Optional.</i> If this column exists <i>and</i> 1079 * this element exists at the given row, this is the action that will be used when 1080 * forming the suggestion's intent. If the element is not provided, the action will be taken 1081 * from the android:searchSuggestIntentAction field in your XML metadata. <i>At least one of 1082 * these must be present for the suggestion to generate an intent.</i> Note: If your action is 1083 * the same for all suggestions, it is more efficient to specify it using XML metadata and omit 1084 * it from the cursor. 1085 */ 1086 public final static String SUGGEST_COLUMN_INTENT_ACTION = "suggest_intent_action"; 1087 /** 1088 * Column name for suggestions cursor. <i>Optional.</i> If this column exists <i>and</i> 1089 * this element exists at the given row, this is the data that will be used when 1090 * forming the suggestion's intent. If the element is not provided, the data will be taken 1091 * from the android:searchSuggestIntentData field in your XML metadata. If neither source 1092 * is provided, the Intent's data field will be null. Note: If your data is 1093 * the same for all suggestions, or can be described using a constant part and a specific ID, 1094 * it is more efficient to specify it using XML metadata and omit it from the cursor. 1095 */ 1096 public final static String SUGGEST_COLUMN_INTENT_DATA = "suggest_intent_data"; 1097 /** 1098 * Column name for suggestions cursor. <i>Optional.</i> If this column exists <i>and</i> 1099 * this element exists at the given row, then "/" and this value will be appended to the data 1100 * field in the Intent. This should only be used if the data field has already been set to an 1101 * appropriate base string. 1102 */ 1103 public final static String SUGGEST_COLUMN_INTENT_DATA_ID = "suggest_intent_data_id"; 1104 /** 1105 * Column name for suggestions cursor. <i>Required if action is 1106 * {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH}, optional otherwise.</i> If this 1107 * column exists <i>and</i> this element exists at the given row, this is the data that will be 1108 * used when forming the suggestion's query. 1109 */ 1110 public final static String SUGGEST_COLUMN_QUERY = "suggest_intent_query"; 1111 1112 1113 private final Context mContext; 1114 private final Handler mHandler; 1115 1116 private SearchDialog mSearchDialog; 1117 1118 private OnDismissListener mDismissListener = null; 1119 private OnCancelListener mCancelListener = null; 1120 1121 /*package*/ SearchManager(Context context, Handler handler) { 1122 mContext = context; 1123 mHandler = handler; 1124 } 1125 private static ISearchManager mService; 1126 1127 static { 1128 mService = ISearchManager.Stub.asInterface( 1129 ServiceManager.getService(Context.SEARCH_SERVICE)); 1130 } 1131 1132 /** 1133 * Launch search UI. 1134 * 1135 * <p>The search manager will open a search widget in an overlapping 1136 * window, and the underlying activity may be obscured. The search 1137 * entry state will remain in effect until one of the following events: 1138 * <ul> 1139 * <li>The user completes the search. In most cases this will launch 1140 * a search intent.</li> 1141 * <li>The user uses the back, home, or other keys to exit the search.</li> 1142 * <li>The application calls the {@link #stopSearch} 1143 * method, which will hide the search window and return focus to the 1144 * activity from which it was launched.</li> 1145 * 1146 * <p>Most applications will <i>not</i> use this interface to invoke search. 1147 * The primary method for invoking search is to call 1148 * {@link android.app.Activity#onSearchRequested Activity.onSearchRequested()} or 1149 * {@link android.app.Activity#startSearch Activity.startSearch()}. 1150 * 1151 * @param initialQuery A search string can be pre-entered here, but this 1152 * is typically null or empty. 1153 * @param selectInitialQuery If true, the intial query will be preselected, which means that 1154 * any further typing will replace it. This is useful for cases where an entire pre-formed 1155 * query is being inserted. If false, the selection point will be placed at the end of the 1156 * inserted query. This is useful when the inserted query is text that the user entered, 1157 * and the user would expect to be able to keep typing. <i>This parameter is only meaningful 1158 * if initialQuery is a non-empty string.</i> 1159 * @param launchActivity The ComponentName of the activity that has launched this search. 1160 * @param appSearchData An application can insert application-specific 1161 * context here, in order to improve quality or specificity of its own 1162 * searches. This data will be returned with SEARCH intent(s). Null if 1163 * no extra data is required. 1164 * @param globalSearch If false, this will only launch the search that has been specifically 1165 * defined by the application (which is usually defined as a local search). If no default 1166 * search is defined in the current application or activity, no search will be launched. 1167 * If true, this will always launch a platform-global (e.g. web-based) search instead. 1168 * 1169 * @see android.app.Activity#onSearchRequested 1170 * @see #stopSearch 1171 */ 1172 public void startSearch(String initialQuery, 1173 boolean selectInitialQuery, 1174 ComponentName launchActivity, 1175 Bundle appSearchData, 1176 boolean globalSearch) { 1177 1178 if (mSearchDialog == null) { 1179 mSearchDialog = new SearchDialog(mContext); 1180 } 1181 1182 // activate the search manager and start it up! 1183 mSearchDialog.show(initialQuery, selectInitialQuery, launchActivity, appSearchData, 1184 globalSearch); 1185 1186 mSearchDialog.setOnCancelListener(this); 1187 mSearchDialog.setOnDismissListener(this); 1188 } 1189 1190 /** 1191 * Terminate search UI. 1192 * 1193 * <p>Typically the user will terminate the search UI by launching a 1194 * search or by canceling. This function allows the underlying application 1195 * or activity to cancel the search prematurely (for any reason). 1196 * 1197 * <p>This function can be safely called at any time (even if no search is active.) 1198 * 1199 * @see #startSearch 1200 */ 1201 public void stopSearch() { 1202 if (mSearchDialog != null) { 1203 mSearchDialog.cancel(); 1204 } 1205 } 1206 1207 /** 1208 * Determine if the Search UI is currently displayed. 1209 * 1210 * This is provided primarily for application test purposes. 1211 * 1212 * @return Returns true if the search UI is currently displayed. 1213 * 1214 * @hide 1215 */ 1216 public boolean isVisible() { 1217 if (mSearchDialog != null) { 1218 return mSearchDialog.isShowing(); 1219 } 1220 return false; 1221 } 1222 1223 /** 1224 * See {@link #setOnDismissListener} for configuring your activity to monitor search UI state. 1225 */ 1226 public interface OnDismissListener { 1227 /** 1228 * This method will be called when the search UI is dismissed. To make use if it, you must 1229 * implement this method in your activity, and call {@link #setOnDismissListener} to 1230 * register it. 1231 */ 1232 public void onDismiss(); 1233 } 1234 1235 /** 1236 * See {@link #setOnCancelListener} for configuring your activity to monitor search UI state. 1237 */ 1238 public interface OnCancelListener { 1239 /** 1240 * This method will be called when the search UI is canceled. To make use if it, you must 1241 * implement this method in your activity, and call {@link #setOnCancelListener} to 1242 * register it. 1243 */ 1244 public void onCancel(); 1245 } 1246 1247 /** 1248 * Set or clear the callback that will be invoked whenever the search UI is dismissed. 1249 * 1250 * @param listener The {@link OnDismissListener} to use, or null. 1251 */ 1252 public void setOnDismissListener(final OnDismissListener listener) { 1253 mDismissListener = listener; 1254 } 1255 1256 /** 1257 * The callback from the search dialog when dismissed 1258 * @hide 1259 */ 1260 public void onDismiss(DialogInterface dialog) { 1261 if (dialog == mSearchDialog) { 1262 if (mDismissListener != null) { 1263 mDismissListener.onDismiss(); 1264 } 1265 } 1266 } 1267 1268 /** 1269 * Set or clear the callback that will be invoked whenever the search UI is canceled. 1270 * 1271 * @param listener The {@link OnCancelListener} to use, or null. 1272 */ 1273 public void setOnCancelListener(final OnCancelListener listener) { 1274 mCancelListener = listener; 1275 } 1276 1277 1278 /** 1279 * The callback from the search dialog when canceled 1280 * @hide 1281 */ 1282 public void onCancel(DialogInterface dialog) { 1283 if (dialog == mSearchDialog) { 1284 if (mCancelListener != null) { 1285 mCancelListener.onCancel(); 1286 } 1287 } 1288 } 1289 1290 /** 1291 * Save instance state so we can recreate after a rotation. 1292 * 1293 * @hide 1294 */ 1295 void saveSearchDialog(Bundle outState, String key) { 1296 if (mSearchDialog != null && mSearchDialog.isShowing()) { 1297 Bundle searchDialogState = mSearchDialog.onSaveInstanceState(); 1298 outState.putBundle(key, searchDialogState); 1299 } 1300 } 1301 1302 /** 1303 * Restore instance state after a rotation. 1304 * 1305 * @hide 1306 */ 1307 void restoreSearchDialog(Bundle inState, String key) { 1308 Bundle searchDialogState = inState.getBundle(key); 1309 if (searchDialogState != null) { 1310 if (mSearchDialog == null) { 1311 mSearchDialog = new SearchDialog(mContext); 1312 } 1313 mSearchDialog.onRestoreInstanceState(searchDialogState); 1314 } 1315 } 1316 1317 /** 1318 * Hook for updating layout on a rotation 1319 * 1320 * @hide 1321 */ 1322 void onConfigurationChanged(Configuration newConfig) { 1323 if (mSearchDialog != null && mSearchDialog.isShowing()) { 1324 mSearchDialog.onConfigurationChanged(newConfig); 1325 } 1326 } 1327 1328} 1329