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