adding-custom-suggestions.jd revision b3b2b4f2def1719852cdffd8a190ee066b81b598
1page.title=Adding Custom Suggestions 2parent.title=Search 3parent.link=index.html 4@jd:body 5 6<div id="qv-wrapper"> 7<div id="qv"> 8<h2>Key classes</h2> 9<ol> 10<li>{@link android.app.SearchManager}</li> 11<li>{@link android.content.SearchRecentSuggestionsProvider}</li> 12<li>{@link android.content.ContentProvider}</li> 13</ol> 14<h2>In this document</h2> 15<ol> 16<li><a href="#TheBasics">The Basics</a></li> 17<li><a href="#CustomSearchableConfiguration">Modifying the searchable configuration</a></li> 18<li><a href="#CustomContentProvider">Creating a Content Provider</a> 19 <ol> 20 <li><a href="#HandlingSuggestionQuery">Handling a suggestion query</a></li> 21 <li><a href="#SuggestionTable">Building a suggestion table</a></li> 22 </ol> 23</li> 24<li><a href="#IntentForSuggestions">Declaring an Intent for suggestions</a> 25 <ol> 26 <li><a href="#IntentAction">Declaring the Intent action</a></li> 27 <li><a href="#IntentData">Declaring the Intent data</a></li> 28 </ol> 29</li> 30<li><a href="#HandlingIntent">Handling the Intent</a></li> 31<li><a href="#RewritingQueryText">Rewriting the query text</a></li> 32<li><a href="#QSB">Exposing search suggestions to Quick Search Box</a></li> 33</ol> 34<h2>See also</h2> 35<ol> 36<li><a href="searchable-config.html">Searchable Configuration</a></li> 37<li><a href="{@docRoot}guide/topics/providers/content-providers.html">Content Providers</a></li> 38<li><a href="{@docRoot}resources/samples/SearchableDictionary/index.html">Searchable 39Dictionary sample app</a></li> 40</ol> 41</div> 42</div> 43 44<p>The Android search framework provides the ability for your application to 45provide suggestions while the user types into the Android search dialog. In this guide, you'll learn 46how to create custom suggestions. These are suggestions based on custom data provided by your 47application. For example, if your application is a word dictionary, you can suggest words from the 48dictionary that match the text entered so far. These are the most valuable suggestions because you 49can effectively predict what the user wants and provide instant access to it. Once you provide 50custom suggestions, you then make them available to the system-wide Quick Search Box, providing 51access to your content from outside your application.</p> 52 53<p>Before you begin, you need to have implemented the Android search dialog for searches in your 54application. If you haven't done this, see <a href="search-dialog.html">Using the Android Search 55Dialog</a>.</p> 56 57 58<h2 id="TheBasics">The Basics</h2> 59 60<img src="{@docRoot}images/search/search-suggest-custom.png" alt="" height="417" 61style="float:right;clear:right;" /> 62 63<p>When the user selects a custom suggestions, the Search Manager will send a customized Intent to 64your searchable Activity. Whereas a normal search query will send an Intent with the {@link 65android.content.Intent#ACTION_SEARCH} action, you can instead define your custom suggestions to use 66{@link android.content.Intent#ACTION_VIEW} (or any other action), and also include additional data 67that's relevant to the selected suggestion. Continuing 68the dictionary example, when the user selects a suggestion, your application can immediately 69open the definition for that word, instead of searching the dictionary for matches.</p> 70 71<p>To provide custom suggestions, you need to do the following:</p> 72 73<ul> 74 <li>Implement a basic searchable Activity, as described in <a 75href="search-dialog.html">Using the Android Search Dialog</a>.</li> 76 <li>Build a table (such as in an {@link android.database.sqlite.SQLiteDatabase}) for your 77suggestions and format the table with required columns.</li> 78 <li>Create a <a href="{@docRoot}guide/topics/providers/content-providers.html">Content 79Provider</a> that has access to your suggestions table and declare the provider 80in your manifest.</li> 81 <li>Declare the type of {@link android.content.Intent} to be sent when the user selects a 82suggestion (including a custom action and custom data). </li> 83 <li>Modify the searchable configuration with information about the content provider.</li> 84</ul> 85 86<p>Just like the Search Manager handles the rendering of the search dialog, it will also do the work 87to display all search suggestions below the search dialog. All you need to do is provide a source 88from which the suggestions can be retrieved.</p> 89 90<p class="note"><strong>Note:</strong> If you're not familiar with creating Content 91Providers, please read the <a href="{@docRoot}guide/topics/providers/content-providers.html">Content 92Providers</a> developer guide before you continue.</p> 93 94<p>When the Search Manager identifies that your Activity is searchable and also provides search 95suggestions, the following procedure will take place as soon as the user types into the Android 96search box:</p> 97 98<ul> 99 <li>The Search Manager takes the search query text (whatever has been typed so far) and performs a 100query to the content provider that manages your suggestions.</li> 101 <li>Your content provider then returns a {@link android.database.Cursor} that points to all 102suggestions that are relevant to the search query text.</li> 103 <li>The Search Manager then displays the list of suggestions provided by the Cursor (as 104demonstrated in the screenshot to the right).</li> 105</ul> 106 107<p>At this point, the following may happen:</p> 108 109<ul> 110 <li>If the user types another key, or changes the query in any way, the above steps are repeated 111and the suggestion list is updated as appropriate. </li> 112 <li>If the user executes the search, the suggestions are ignored and the search is delivered 113to your searchable Activity using the normal {@link android.content.Intent#ACTION_SEARCH} 114Intent.</li> 115 <li>If the user selects a suggestion, an Intent is sent to your searchable Activity, carrying a 116custom action and custom data so that your application can open the suggested content.</li> 117</ul> 118 119 120 121<h2 id="CustomSearchableConfiguration">Modifying the searchable configuration</h2> 122 123<p>To add support for custom suggestions, add the {@code android:searchSuggestAuthority} attribute 124to the {@code <searchable>} element in your searchable configuration file. For example:</p> 125 126<pre> 127<?xml version="1.0" encoding="utf-8"?> 128<searchable xmlns:android="http://schemas.android.com/apk/res/android" 129 android:label="@string/app_label" 130 android:hint="@string/search_hint" 131 android:searchSuggestAuthority="my.package.MyCustomSuggestionProvider" > 132</searchable> 133</pre> 134 135<p>You may require some additional attributes, depending on the type of Intent you attach 136to each suggestion and how you want to format queries to your content provider. The other optional 137attributes are discussed in the relevant sections below.</p> 138 139 140<h2 id="CustomContentProvider">Creating a Content Provider</h2> 141 142<p>Creating a content provider for custom suggestions requires previous knowledge about Content 143Providers that's covered in the <a 144href="{@docRoot}guide/topics/providers/content-providers.html">Content Provider</a> developer 145guide. For the most part, a content provider for custom suggestions is the 146same as any other content provider. However, for each suggestion you provide, the respective row in 147the {@link android.database.Cursor} must include specific columns that the Search Manager 148understands.</p> 149 150<p>When the user starts typing into the search dialog, the Search Manager will query your Content 151Provider for suggestions by calling {@link 152android.content.ContentProvider#query(Uri,String[],String,String[],String) query()} each time 153a letter is typed. In your implementation of {@link 154android.content.ContentProvider#query(Uri,String[],String,String[],String) query()}, your 155content provider must search your suggestion data and return a {@link 156android.database.Cursor} that points to the rows you determine to be good suggestions.</p> 157 158<p>The following two sections describe how the Search Manager will send requests to your Content 159Provider and how you can handle them, and define the columns that the Search Manager understands and 160expects to be provided in the {@link android.database.Cursor} returned with each query.</p> 161 162 163<h3 id="HandlingSuggestionQuery">Handling the suggestion query</h3> 164 165<p>When the Search Manager makes a request for suggestions from your content provider, it will call 166{@link android.content.ContentProvider#query(Uri,String[],String,String[],String)}. You must 167implement this method in your content provider so that it will search your suggestions and return a 168Cursor that contains the suggestions you deem relevant.</p> 169 170<p>Here's a summary of the parameters that the Search Manager will pass to your {@link 171android.content.ContentProvider#query(Uri,String[],String,String[],String) query()} method 172(listed in order):</p> 173 174<dl> 175 <dt><code>uri</code></dt> 176 <dd>This will always be a content {@link android.net.Uri}, formatted as: 177<pre class="no-pretty-print"> 178content://<em>your.authority</em>/<em>optional.suggest.path</em>/<em>{@link 179android.app.SearchManager#SUGGEST_URI_PATH_QUERY}</em> 180</pre> 181<p>The default behavior is for Search Manager to pass this URI and append it with the query text. 182For example:</p> 183<pre class="no-pretty-print"> 184content://<em>your.authority</em>/<em>optional.suggest.path</em>/<em>{@link 185android.app.SearchManager#SUGGEST_URI_PATH_QUERY}</em>/puppies 186</pre> 187<p>The query text on the end will be encoded using URI encoding rules, so you may need to decode 188it.</p> 189<p>The <em>{@code optional.suggest.path}</em> portion is only included in the URI if you have set 190such a path in your searchable configuration file with the {@code android:searchSuggestPath} 191attribute. This is only needed if you use the same content provider for multiple searchable 192activities, in which case you need to disambiguate the source of the suggestion query.</p> 193<p>Note that {@link android.app.SearchManager#SUGGEST_URI_PATH_QUERY} is not the literal 194string provided in the URI, but a constant that you should use if you need to refer to this 195path.</p> 196 </dd> 197 198 <dt><code>projection</code></dt> 199 <dd>This is always null</dd> 200 201 <dt><code>selection</code></dt> 202 <dd>This is the value provided in the {@code android:searchSuggestSelection} attribute of 203your searchable configuration file, or null if you have not declared the {@code 204android:searchSuggestSelection} attribute. More about this below.</dd> 205 206 <dt><code>selectionArgs</code></dt> 207 <dd>This contains the search query as the first (and only) element of the array if you have 208declared the {@code android:searchSuggestSelection} attribute in your searchable configuration. If 209you have not declared {@code android:searchSuggestSelection}, then this parameter is null. More 210about this below.</dd> 211 212 <dt><code>sortOrder</code></dt> 213 <dd>This is always null</dd> 214</dl> 215 216<p>As you may have realized, there are two ways by which the Search Manager can send you the search 217query text. The default manner is for the query text to be included as the last path of the content 218URI that is passed in the {@code uri} parameter. However, if you include a selection value in your 219searchable configuration's {@code 220android:searchSuggestSelection} attribute, then the query text will instead be passed as the first 221element of the {@code selectionArgs} string array. Both options are summarized below.</p> 222 223 224<h4>Get the query in the Uri</h4> 225 226<p>By default, the query will be appended as the last segment of the {@code uri} 227parameter (a {@link android.net.Uri} object). To retrieve the query text in this case, simply use 228{@link android.net.Uri#getLastPathSegment()}. For example:</p> 229 230<pre> 231String query = uri.getLastPathSegment().toLowerCase(); 232</pre> 233 234<p>This will return the last segment of the Uri, which is the query text entered in the search 235dialog.</p> 236 237 238 239<h4>Get the query in the selection arguments</h4> 240 241<p>Instead of using the URI, you may decide it makes more sense for your {@link 242android.content.ContentProvider#query(Uri,String[],String,String[],String) query()} method to 243receive everything it needs to perform the look-up and you want the 244{@code selection} and {@code selectionArgs} parameters to carry values. In this case, you can 245add the {@code android:searchSuggestSelection} attribute to your searchable configuration with your 246SQLite selection string. In this selection string, you can include a question mark ("?") as 247a placeholder for the actual search query. This selection string will be delivered as the 248{@code selection} string parameter, and the query entered into the search dialog will be delivered 249as the first element in the {@code selectionArgs} string array parameter.</p> 250 251<p>For example, here's how you might form the {@code android:searchSuggestSelection} attribute to 252create a full-text search statement:</p> 253 254<pre> 255<?xml version="1.0" encoding="utf-8"?> 256<searchable xmlns:android="http://schemas.android.com/apk/res/android" 257 android:label="@string/app_label" 258 android:hint="@string/search_hint" 259 android:searchSuggestAuthority="my.package.MyCustomSuggestionProvider" 260 android:searchSuggestIntentAction="android.Intent.action.VIEW" 261 android:searchSuggestSelection="word MATCH ?"> 262</searchable> 263</pre> 264 265<p>When you then receive the {@code selection} and {@code selectionArgs} parameters in your {@link 266android.content.ContentProvider#query(Uri,String[],String,String[],String) ContentProvider.query()} 267method, they will carry the selection ("word MATCH ?") and the query text, respectively. When 268these are passed to an SQLite {@link 269android.database.sqlite.SQLiteDatabase#query(String,String[],String,String[],String,String, 270String) query} method, they will be synthesized together (replacing the question mark with the query 271text, wrapped in single-quotes). Note that if you chose this method and need to add any wildcards to 272your query text, you must do so by appending (and/or prefixing) them to the {@code selectionArgs} 273parameter, because this is the value that will be wrapped in quotes and inserted in place of the 274question mark.</p> 275 276<p class="note"><strong>Tip:</strong> If you don't want to define a selection clause in 277the {@code android:searchSuggestSelection} attribute, but would still like to receive the query 278text in the {@code selectionArgs} parameter, simply provide a non-null value for the {@code 279android:searchSuggestSelection} attribute. This will trigger the query to be passed in {@code 280selectionArgs} and you can ignore the {@code selection} parameter. In this way, you can instead 281define the actual selection clause at a lower level so that your content provider doesn't have to 282handle it.</p> 283 284 285 286<h3 id="SuggestionTable">Building a suggestion table</h3> 287 288<div class="sidebox-wrapper"> 289<div class="sidebox"> 290<h2>Creating a Cursor on the fly</h2> 291<p>If your search suggestions are not stored in a table format using the columns required by the 292Search Manager, then you can search your suggestion data for matches and then format them 293into the necessary table on the fly. To do so, create a {@link android.database.MatrixCursor} using 294the required column names and then add a row for each suggestion using {@link 295android.database.MatrixCursor#addRow(Object[])}. Return the final product from your Content 296Provider's {@link 297android.content.ContentProvider#query(Uri,String[],String,String[],String) query()} method.</p> 298</div> 299</div> 300 301<p>When you return suggestions to the Search Manager with a {@link android.database.Cursor}, the 302Search Manager expects there to be specific columns in each row. So, regardless of whether you 303decide to store 304your suggestion data in an SQLite database on the device, a database on a web server, or another 305format on the device or web, you must format the suggestions as rows in a table and 306present them with a {@link android.database.Cursor}. There are several columns that the Search 307Manager will understand, but only two are required:</p> 308 309<dl> 310 <dt>{@link android.provider.BaseColumns#_ID}</dt> 311 <dd>This is the unique row ID for each suggestion. The search dialog requires this in order 312to present the suggestions in a ListView.</dd> 313 <dt>{@link android.app.SearchManager#SUGGEST_COLUMN_TEXT_1}</dt> 314 <dd>This is the line of text that will be presented to the user as a suggestion.</dd> 315</dl> 316 317<p>The following columns are all optional (and most will be discussed further in the following 318sections, so you may want to skip this list for now):</p> 319 320<dl> 321 <dt>{@link android.app.SearchManager#SUGGEST_COLUMN_TEXT_2}</dt> 322 <dd>If your Cursor includes this column, then all suggestions will be provided in a two-line 323format. The data in this column will be displayed as a second, smaller line of text below the 324primary suggestion text. It can be null or empty to indicate no secondary text.</dd> 325 <dt>{@link android.app.SearchManager#SUGGEST_COLUMN_ICON_1}</dt> 326 <dd>If your Cursor includes this column, then all suggestions will be provided in an 327icon-plus-text format with the icon on the left side. This value should be a reference to the 328icon. It can be null or zero to indicate no icon in this row.</dd> 329 <dt>{@link android.app.SearchManager#SUGGEST_COLUMN_ICON_2}</dt> 330 <dd>If your Cursor includes this column, then all suggestions will be provided in an 331icon-plus-text format with the icon on the right side. This value should be a reference to the 332icon. It can be null or zero to indicate no icon in this row.</dd> 333 <dt>{@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_ACTION}</dt> 334 <dd>If this column exists and this element exists at the given row, this is the action that will 335be used when forming the suggestion's Intent . If the element is not provided, the action will be 336taken from the {@code android:searchSuggestIntentAction} field in your searchable configuration. At 337least one of these 338must be present for the suggestion to generate an Intent. Note: If your action is the same for all 339suggestions, it is more efficient to specify the action using {@code 340android:searchSuggestIntentAction} and omit this column from the Cursor .</dd> 341 <dt>{@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA}</dt> 342 <dd>If this column exists and this element exists at the given row, this is the data that will be 343used when forming the suggestion's Intent. If the element is not provided, the data will be taken 344from the {@code android:searchSuggestIntentData} field in your searchable configuration. If neither 345source is provided, 346the Intent's data field will be null. Note: If your data is the same for all suggestions, or can be 347described using a constant part and a specific ID, it is more efficient to specify it using {@code 348android:searchSuggestIntentData} and omit this column from the Cursor . 349</dd> 350 <dt>{@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA_ID}</dt> 351 <dd>If this column exists and this element exists at the given row, then "/" and this value will 352be appended to the data field in the Intent. This should only be used if the data field specified 353by the {@code android:searchSuggestIntentData} attribute in the searchable configuration has already 354been set to an appropriate base string.</dd> 355 <dt>{@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_EXTRA_DATA}</dt> 356 <dd>If this column exists and this element exists at a given row, this is the <em>extra</em> data 357that will be used when forming the suggestion's Intent. If not provided, the Intent's extra data 358field will be 359null. This column allows suggestions to provide additional arbitrary data which will be 360included as an extra in the Intent's {@link android.app.SearchManager#EXTRA_DATA_KEY} key.</dd> 361 <dt>{@link android.app.SearchManager#SUGGEST_COLUMN_QUERY}</dt> 362 <dd>If this column exists and this element exists at the given row, this is the data that will be 363used when forming the suggestion's query, included as an extra in the Intent's {@link 364android.app.SearchManager#QUERY} key. Required if suggestion's action is {@link 365android.content.Intent#ACTION_SEARCH}, optional otherwise.</dd> 366 <dt>{@link android.app.SearchManager#SUGGEST_COLUMN_SHORTCUT_ID}</dt> 367 <dd>Only used when providing suggestions for Quick Search Box. This column is used to indicate 368whether a search suggestion should be stored as a 369shortcut, and whether it should be validated. Shortcuts are usually formed when the user clicks a 370suggestion from Quick Search Box. If missing, the result will be stored as a shortcut and never 371refreshed. If set to {@link android.app.SearchManager#SUGGEST_NEVER_MAKE_SHORTCUT}, the result will 372not be stored as a shortcut. 373Otherwise, the shortcut id will be used to check back for for an up to date suggestion using 374{@link android.app.SearchManager#SUGGEST_URI_PATH_SHORTCUT}.</dd> 375 <dt>{@link android.app.SearchManager#SUGGEST_COLUMN_SPINNER_WHILE_REFRESHING}</dt> 376 <dd>Only used when providing suggestions for Quick Search Box. This column is used to specify that 377a spinner should be shown instead of an icon from {@link 378android.app.SearchManager#SUGGEST_COLUMN_ICON_2} 379while the shortcut of this suggestion is being refreshed in Quick Search Box.</dd> 380</dl> 381 382<p>Again, most of these columns will be discussed in the relevant sections below, so don't worry if 383they don't make sense to you now.</p> 384 385 386 387<h2 id="IntentForSuggestions">Declaring an Intent for suggestions</h2> 388 389<p>When the user selects a suggestion from the list that appears below the search 390dialog (instead of performing a search), the Search Manager will send 391a custom {@link android.content.Intent} to your searchable Activity. You must define both the 392<em>action</em> and <em>data</em> for the Intent.</p> 393 394 395<h3 id="IntentAction">Declaring the Intent action</h3> 396 397<p>The most common Intent action for a custom suggestion is {@link 398android.content.Intent#ACTION_VIEW}, which is appropriate when 399you want to open something, like the definition for a word, a person's contact information, or a web 400page. However, the Intent action can be whatever you want and can even be different for each 401suggestion.</p> 402 403<p>To declare an Intent action that will be the same for all suggestions, define the action in 404the {@code android:searchSuggestIntentAction} attribute of your searchable configuration file. For 405example:</p> 406 407<pre> 408<?xml version="1.0" encoding="utf-8"?> 409<searchable xmlns:android="http://schemas.android.com/apk/res/android" 410 android:label="@string/app_label" 411 android:hint="@string/search_hint" 412 android:searchSuggestAuthority="my.package.MySuggestionProvider" 413 android:searchSuggestIntentAction="android.Intent.action.VIEW" > 414</searchable> 415</pre> 416 417<p>If you want to declare an Intent action that's unique for each suggestion, add the {@link 418android.app.SearchManager#SUGGEST_COLUMN_INTENT_ACTION} column to 419your suggestions table and, for each suggestion, place in it the action to use (such as 420{@code "android.Intent.action.VIEW"}). </p> 421 422<p>You can also combine these two techniques. For instance, you can include the {@code 423android:searchSuggestIntentAction} attribute with an action to be used with all suggestions by 424default, then override this action for some suggestions by declaring a different action in the 425{@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_ACTION} column. If you do not include 426a value in the {@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_ACTION} column, then the 427Intent provided in the {@code android:searchSuggestIntentAction} attribute will be used.</p> 428 429<p class="note"><strong>Note</strong>: If you do not include the 430{@code android:searchSuggestIntentAction} attribute in your searchable configuration, then you 431<em>must</em> include a value in the {@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_ACTION} 432column for every suggestion, or the Intent will fail.</p> 433 434 435<h3 id="IntentData">Declaring Intent data</h3> 436 437<p>When the user selects a suggestion, your searchable Activity will receive the Intent with the 438action you've defined (as discussed in the previous section), but the Intent must also carry 439data in order for your Activity to identify which suggestions was selected. Specifically, 440the data should be something unique for each suggestion, such as the row ID for the suggestion in 441your suggestions table. When the Intent is received, 442you can retrieve the attached data with {@link android.content.Intent#getData()} or {@link 443android.content.Intent#getDataString()}.</p> 444 445<p>There are two ways to define the data that is included with the Intent:</p> 446 447<ol type="a"> 448 <li>Define the data for each suggestion inside the {@link 449android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA} column of your suggestions table.</li> 450 <li>Fragment a data URI into two pieces: the portion common to all suggestions and the portion 451unique to each suggestion. Place these parts into the {@code android:searchSuggestIntentData} 452attribute of the searchable configuration and the {@link 453android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA_ID} column of your 454suggestions table, respectively.</li> 455</ol> 456 457<p>The first option is straight-forward. Simply provide all necessary data information for each 458Intent in the suggestions table by including the {@link 459android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA} column and then populating it with unique 460data for each row. The data from this column will be attached to the Intent exactly as it 461is found in this column. You can then retrieve it with with {@link android.content.Intent#getData()} 462or {@link android.content.Intent#getDataString()}.</p> 463 464<p class="note"><strong>Tip</strong>: It's usually easiest to use the table's row ID as the 465Intent data because it's always unique. And the easiest way to do that is by using the 466{@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA} column name as an alias for the row ID 467column. See the <a 468href="{@docRoot}resources/samples/SearchableDictionary/index.html">Searchable Dictionary sample 469app</a> for an example in which {@link android.database.sqlite.SQLiteQueryBuilder} is used to 470create a projection map of column names to aliases.</p> 471 472<p>The second option is to fragment your data URI into the common piece and the unique piece. 473Declare the piece of the URI that is common to all suggestions in the {@code 474android:searchSuggestIntentData} attribute of your searchable configuration. For example:</p> 475 476<pre> 477<?xml version="1.0" encoding="utf-8"?> 478<searchable xmlns:android="http://schemas.android.com/apk/res/android" 479 android:label="@string/app_label" 480 android:hint="@string/search_hint" 481 android:searchSuggestAuthority="my.package.MySuggestionProvider" 482 android:searchSuggestIntentAction="android.Intent.action.VIEW" 483 android:searchSuggestIntentData="content://my.package/datatable" > 484</searchable> 485</pre> 486 487<p>Now include the final path for each suggestion (the unique part) in the {@link 488android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA_ID} 489column of your suggestions table. When the user selects a suggestion, the Search Manager will take 490the string from {@code android:searchSuggestIntentData}, append a slash ("/") and then add the 491respective value from the {@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA_ID} column to 492form a complete content URI. You can then retrieve the {@link android.net.Uri} with with {@link 493android.content.Intent#getData()}.</p> 494 495<h4>Add more data</h4> 496 497<p>If you need to express even more information with your Intent, you can add another table column, 498{@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_EXTRA_DATA}, which can store additional 499information about the suggestion. The data saved in this column will be placed in {@link 500android.app.SearchManager#EXTRA_DATA_KEY} of the Intent's extra Bundle.</p> 501 502 503<h2 id="HandlingIntent">Handling the Intent</h2> 504 505<p>Now that your search dialog provides custom search suggestions with custom formatted Intents, you 506need your searchable Activity to handle these Intents as they are delivered once the user selects a 507suggestion. (This is, of course, in addition to handling the {@link 508android.content.Intent#ACTION_SEARCH} Intent, which your searchable Activity already does.) 509Accepting the new Intent is rather self-explanatory, so we'll skip straight to an example:</p> 510 511<pre> 512Intent intent = getIntent(); 513if (Intent.ACTION_SEARCH.equals(intent.getAction())) { 514 // Handle the normal search query case 515 String query = intent.getStringExtra(SearchManager.QUERY); 516 doSearch(query); 517} else if (Intent.ACTION_VIEW.equals(intent.getAction())) { 518 // Handle a suggestions click (because my suggestions all use ACTION_VIEW) 519 Uri data = intent.getData()); 520 showResult(rowId); 521} 522</pre> 523 524<p>In this example, the Intent action is {@link 525android.content.Intent#ACTION_VIEW} and the data carries a complete URI pointing to the suggested 526item, as synthesized by the {@code android:searchSuggestIntentData} string and {@link 527android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA_ID} column. The URI is then passed to a local 528method that will query the content provider for the item specified by the URI and show it.</p> 529 530 531 532<h2 id="RewritingQueryText">Rewriting the query text</h2> 533 534<p>If the user navigates through the suggestions list using the device directional controls, the 535text in the search dialog won't change, by default. However, you can temporarily rewrite the 536user's query text as it appears in the text box with 537a query that matches the currently selected suggestion. This enables the user to see what query is 538being suggested (if appropriate) and then select the search box and edit the query before 539dispatching it as a search.</p> 540 541<p>You can rewrite the query text in the following ways:</p> 542 543<ol type="a"> 544 <li>Add the {@code android:searchMode} attribute to your searchable configuration with the 545"queryRewriteFromText" value. In this case, the content from the suggestion's {@link 546android.app.SearchManager#SUGGEST_COLUMN_TEXT_1} 547column will be used to rewrite the query text.</li> 548 <li>Add the {@code android:searchMode} attribute to your searchable configuration with the 549"queryRewriteFromData" value. In this case, the content from the suggestion's 550{@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA} column will be used to rewrite the 551query text. Note that this should only 552be used with Uri's or other data formats that are intended to be user-visible, such as HTTP URLs. 553Internal Uri schemes should not be used to rewrite the query in this way.</li> 554 <li>Provide a unique query text string in the {@link 555android.app.SearchManager#SUGGEST_COLUMN_QUERY} column of your suggestions table. If this column is 556present and contains a value for the current suggestion, it will be used to rewrite the query text 557(and override either of the previous implementations).</li> 558</ol> 559 560 561<h2 id="QSB">Exposing search suggestions to Quick Search Box</h2> 562 563<p>Once your application is configured to provide custom search suggestions, making them available 564to the globally-accessible Quick Search Box is as easy as modifying your searchable configuration to 565include {@code android:includeInGlobalSearch} as "true".</p> 566 567<p>The only scenario in which additional work will be required is if your content provider for 568custom suggestions requires a permission for read access. In which case, you need to add a special 569{@code <path-permission>} element for the provider to grant Quick Search Box read access to your 570content provider. For example:</p> 571 572<pre> 573<provider android:name="MySuggestionProvider" 574 android:authorities="my.package.authority" 575 android:readPermission="com.example.provider.READ_MY_DATA" 576 android:writePermission="com.example.provider.WRITE_MY_DATA"> 577 <path-permission android:pathPrefix="/search_suggest_query" 578 android:readPermission="android.permission.GLOBAL_SEARCH" /> 579</provider> 580</pre> 581 582<p>In this example, the provider restricts read and write access to the content. The 583{@code <path-permission>} element amends the restriction by granting read access to content 584inside the {@code "/search_suggest_query"} path prefix when the {@code 585"android.permission.GLOBAL_SEARCH"} permission exists. This grants access to Quick Search Box 586so that it may query your content provider for suggestions.</p> 587 588<p>Content providers that enforce no permissions are already available to the search 589infrastructure.</p> 590 591 592<h3 id="EnablingSuggestions">Enabling suggestions on a device</h3> 593 594<p>When your application is configured to provide suggestions in Quick Search Box, it is not 595actually enabled to provide suggestions in Quick Search Box, by default. It is the user's choice 596whether to include suggestions from your application in the Quick Search Box. To enable search 597suggestions from your application, the user must open "Searchable items" (in Settings > Search) and 598enable your application as a searchable item.</p> 599 600<p>Each application that is available to Quick Search Box has an entry in the Searchable items 601settings page. The entry includes the name of the application and a short description of what 602content can be searched from the application and made available for suggestions in Quick Search Box. 603To define the description text for your searchable application, add the {@code 604android:searchSettingsDescription} attribute to your searchable configuration. For example:</p> 605 606<pre> 607<?xml version="1.0" encoding="utf-8"?> 608<searchable xmlns:android="http://schemas.android.com/apk/res/android" 609 android:label="@string/app_label" 610 android:hint="@string/search_hint" 611 android:searchSuggestAuthority="my.package.MySuggestionProvider" 612 android:searchSuggestIntentAction="android.Intent.action.VIEW" 613 android:includeInGlobalSearch="true" 614 android:searchSettingsDescription="@string/search_description" > 615</searchable> 616</pre> 617 618<p>The string for {@code android:searchSettingsDescription} should be as concise as possible and 619state the content that is searchable. For example, "Artists, albums, and tracks" for a music 620application, or "Saved notes" for a notepad application. Providing this description is important so 621the user knows what kind of suggestions will be provided. This attribute should always be included 622when {@code android:includeInGlobalSearch} is "true".</p> 623 624<p>Remember that the user must visit this settings menu to enable search suggestions for your 625application before your search suggestions will appear in Quick Search Box. As such, if search is an 626important aspect of your application, then you may want to consider a way to message this to your 627users — perhaps with a note the first time they launch the app about how to enable search 628suggestions for Quick Search Box.</p> 629 630 631<h3 id="ManagingShortcuts">Managing Quick Search Box suggestion shortcuts</h3> 632 633<p>Suggestions that the user selects from Quick Search Box may be automatically made into shortcuts. 634These are suggestions that the Search Manager has copied from your content provider so it can 635quickly access the suggestion without the need to re-query your content provider. </p> 636 637<p>By default, this is enabled for all suggestions retrieved by Quick Search Box, but if your 638suggestion data may change over time, then you can request that the shortcuts be refreshed. For 639instance, if your suggestions refer to dynamic data, such as a contact's presence status, then you 640should request that the suggestion shortcuts be refreshed when shown to the user. To do so, 641include the {@link android.app.SearchManager#SUGGEST_COLUMN_SHORTCUT_ID} in your suggestions table. 642Using this column, you can 643configure the shortcut behavior for each suggestion in the following ways:</p> 644 645<ol type="a"> 646 <li>Have Quick Search Box re-query your content provider for a fresh version of the shortcutted 647suggestion. 648 <p>Provide a value in the {@link android.app.SearchManager#SUGGEST_COLUMN_SHORTCUT_ID} column 649and the suggestion will be 650re-queried for a fresh version of the suggestion each time the shortcut is displayed. The shortcut 651will be quickly displayed with whatever data was most recently available until the refresh query 652returns, after which the suggestion will be dynamically refreshed with the new information. The 653refresh query will be sent to your content provider with a URI path of {@link 654android.app.SearchManager#SUGGEST_URI_PATH_SHORTCUT} 655(instead of {@link android.app.SearchManager#SUGGEST_URI_PATH_QUERY}). The Cursor you return should 656contain one suggestion using the 657same columns as the original suggestion, or be empty, indicating that the shortcut is no 658longer valid (in which case, the suggestion will disappear and the shortcut will be removed).</p> 659 <p>If a suggestion refers to data that could take longer to refresh, such as a network based 660refresh, you may also add the {@link 661android.app.SearchManager#SUGGEST_COLUMN_SPINNER_WHILE_REFRESHING} column to your suggestions 662table with a value 663of "true" in order to show a progress spinner for the right hand icon until the refresh is complete. 664(Any value other than "true" will not show the progress spinner.)</p></li> 665 <li>Prevent the suggestion from being copied into a shortcut at all. 666 <p>Provide a value of {@link android.app.SearchManager#SUGGEST_NEVER_MAKE_SHORTCUT} in the 667{@link android.app.SearchManager#SUGGEST_COLUMN_SHORTCUT_ID} column. In 668this case, the suggestion will never be copied into a shortcut. This should only be necessary if you 669absolutely do not want the previously copied suggestion to appear at all. (Recall that if you 670provide a normal value for the column then the suggestion shortcut will appear only until the 671refresh query returns.)</p></li> 672 <li>Allow the default shortcut behavior to apply. 673 <p>Simply leave the {@link android.app.SearchManager#SUGGEST_COLUMN_SHORTCUT_ID} empty for each 674suggestion that will not change and can be saved as a shortcut.</p></li> 675</ol> 676 677<p>Of course, if none of your suggestions will ever change, then you do not need the 678{@link android.app.SearchManager#SUGGEST_COLUMN_SHORTCUT_ID} column at all.</p> 679 680<p class="note"><strong>Note</strong>: Quick Search Box will ultimately decide whether to shortcut 681your app's suggestions, considering these values as a strong request from your application.</p> 682 683 684<h3 id="AboutRanking">About Quick Search Box suggestion ranking</h3> 685 686<p>Once your application's search results are made available to Quick Search Box, how they surface 687to the user for a particular query will be determined as appropriate by Quick Search Box ranking. 688This may depend on how many other apps have results for that query, and how often the user has 689selected on your results compared to those of the other apps. There is no guarantee about how 690ranking will occur, or whether your app's suggestions will show at all for a given query. In 691general, you can expect that providing quality results will increase the likelihood that your app's 692suggestions are provided in a prominent position, and apps that provide lower quality suggestions 693will be more likely to be ranked lower and/or not displayed.</p> 694 695<div class="special"> 696<p>See the <a href="{@docRoot}resources/samples/SearchableDictionary/index.html">Searchable 697Dictionary sample app</a> for a complete demonstration of custom search suggestions.</p> 698</div> 699 700