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