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