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