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