Home | History | Annotate | Download | only in search
      1 page.title=Adding Custom Suggestions
      2 parent.title=Search
      3 parent.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
     39 Dictionary sample app</a></li>
     40 </ol>
     41 </div>
     42 </div>
     43 
     44 <p>The Android search framework provides the ability for your application to
     45 provide suggestions while the user types into the Android search dialog. In this guide, you'll learn
     46 how to create custom suggestions. These are suggestions based on custom data provided by your
     47 application. For example, if your application is a word dictionary, you can suggest words from the
     48 dictionary that match the text entered so far. These are the most valuable suggestions because you
     49 can effectively predict what the user wants and provide instant access to it. Once you provide
     50 custom suggestions, you then make them available to the system-wide Quick Search Box, providing
     51 access 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
     54 application. If you haven't done this, see <a href="search-dialog.html">Using the Android Search
     55 Dialog</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"
     61 style="float:right;clear:right;" />
     62 
     63 <p>When the user selects a custom suggestions, the Search Manager will send a customized Intent to
     64 your searchable Activity. Whereas a normal search query will send an Intent with the {@link
     65 android.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
     67 that's relevant to the selected suggestion. Continuing
     68 the dictionary example, when the user selects a suggestion, your application can immediately
     69 open 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
     75 href="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
     77 suggestions and format the table with required columns.</li>
     78   <li>Create a <a href="{@docRoot}guide/topics/providers/content-providers.html">Content
     79 Provider</a> that has access to your suggestions table and declare the provider
     80 in your manifest.</li>
     81   <li>Declare the type of {@link android.content.Intent} to be sent when the user selects a
     82 suggestion (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
     87 to display all search suggestions below the search dialog. All you need to do is provide a source
     88 from which the suggestions can be retrieved.</p>
     89 
     90 <p class="note"><strong>Note:</strong> If you're not familiar with creating Content
     91 Providers, please read the <a href="{@docRoot}guide/topics/providers/content-providers.html">Content
     92 Providers</a> developer guide before you continue.</p>
     93 
     94 <p>When the Search Manager identifies that your Activity is searchable and also provides search
     95 suggestions, the following procedure will take place as soon as the user types into the Android
     96 search box:</p>
     97 
     98 <ul>
     99   <li>The Search Manager takes the search query text (whatever has been typed so far) and performs a
    100 query 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
    102 suggestions 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
    104 demonstrated 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
    111 and 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 
    113 to your searchable Activity using the normal {@link android.content.Intent#ACTION_SEARCH}
    114 Intent.</li>
    115   <li>If the user selects a suggestion, an Intent is sent to your searchable Activity, carrying a
    116 custom 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
    124 to the {@code &lt;searchable&gt;} element in your searchable configuration file. For example:</p>
    125 
    126 <pre>
    127 &lt;?xml version="1.0" encoding="utf-8"?>
    128 &lt;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 &lt;/searchable>
    133 </pre>
    134 
    135 <p>You may require some additional attributes, depending on the type of Intent you attach
    136 to each suggestion and how you want to format queries to your content provider. The other optional
    137 attributes 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
    143 Providers that's covered in the <a
    144 href="{@docRoot}guide/topics/providers/content-providers.html">Content Provider</a> developer
    145 guide. For the most part, a content provider for custom suggestions is the
    146 same as any other content provider. However, for each suggestion you provide, the respective row in
    147 the {@link android.database.Cursor} must include specific columns that the Search Manager
    148 understands.</p>
    149 
    150 <p>When the user starts typing into the search dialog, the Search Manager will query your Content
    151 Provider for suggestions by calling {@link
    152 android.content.ContentProvider#query(Uri,String[],String,String[],String) query()} each time
    153 a letter is typed. In your implementation of {@link
    154 android.content.ContentProvider#query(Uri,String[],String,String[],String) query()}, your
    155 content provider must search your suggestion data and return a {@link
    156 android.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
    159 Provider and how you can handle them, and define the columns that the Search Manager understands and
    160 expects 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
    167 implement this method in your content provider so that it will search your suggestions and return a
    168 Cursor 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
    171 android.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">
    178 content://<em>your.authority</em>/<em>optional.suggest.path</em>/<em>{@link
    179 android.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.
    182 For example:</p>
    183 <pre class="no-pretty-print">
    184 content://<em>your.authority</em>/<em>optional.suggest.path</em>/<em>{@link
    185 android.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
    188 it.</p>
    189 <p>The <em>{@code optional.suggest.path}</em> portion is only included in the URI if you have set
    190 such a path in your searchable configuration file with the {@code android:searchSuggestPath}
    191 attribute. This is only needed if you use the same content provider for multiple searchable
    192 activities, 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
    194 string provided in the URI, but a constant that you should use if you need to refer to this
    195 path.</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
    203 your searchable configuration file, or null if you have not declared the {@code
    204 android: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
    208 declared the {@code android:searchSuggestSelection} attribute in your searchable configuration. If
    209 you have not declared {@code android:searchSuggestSelection}, then this parameter is null. More
    210 about 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
    217 query text. The default manner is for the query text to be included as the last path of the content
    218 URI that is passed in the {@code uri} parameter. However, if you include a selection value in your 
    219 searchable configuration's {@code
    220 android:searchSuggestSelection} attribute, then the query text will instead be passed as the first
    221 element 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}
    227 parameter (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>
    231 String 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
    235 dialog.</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
    242 android.content.ContentProvider#query(Uri,String[],String,String[],String) query()} method to
    243 receive 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
    245 add the {@code android:searchSuggestSelection} attribute to your searchable configuration with your
    246 SQLite selection string. In this selection string, you can include a question mark ("?") as
    247 a 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
    249 as 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
    252 create a full-text search statement:</p>
    253 
    254 <pre>
    255 &lt;?xml version="1.0" encoding="utf-8"?>
    256 &lt;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 &lt;/searchable>
    263 </pre>
    264 
    265 <p>When you then receive the {@code selection} and {@code selectionArgs} parameters in your {@link
    266 android.content.ContentProvider#query(Uri,String[],String,String[],String) ContentProvider.query()}
    267 method, they will carry the selection ("word MATCH ?") and the query text, respectively. When
    268 these are passed to an SQLite {@link
    269 android.database.sqlite.SQLiteDatabase#query(String,String[],String,String[],String,String,
    270 String) query} method, they will be synthesized together (replacing the question mark with the query
    271 text, wrapped in single-quotes). Note that if you chose this method and need to add any wildcards to
    272 your query text, you must do so by appending (and/or prefixing) them to the {@code selectionArgs}
    273 parameter, because this is the value that will be wrapped in quotes and inserted in place of the
    274 question mark.</p>
    275 
    276 <p class="note"><strong>Tip:</strong> If you don't want to define a selection clause in
    277 the {@code android:searchSuggestSelection} attribute, but would still like to receive the query
    278 text in the {@code selectionArgs} parameter, simply provide a non-null value for the {@code
    279 android:searchSuggestSelection} attribute. This will trigger the query to be passed in {@code
    280 selectionArgs} and you can ignore the {@code selection} parameter. In this way, you can instead
    281 define the actual selection clause at a lower level so that your content provider doesn't have to
    282 handle 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
    292 Search Manager, then you can search your suggestion data for matches and then format them
    293 into the necessary table on the fly. To do so, create a {@link android.database.MatrixCursor} using
    294 the required column names and then add a row for each suggestion using {@link
    295 android.database.MatrixCursor#addRow(Object[])}. Return the final product from your Content
    296 Provider's {@link
    297 android.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
    302 Search Manager expects there to be specific columns in each row. So, regardless of whether you
    303 decide to store
    304 your suggestion data in an SQLite database on the device, a database on a web server, or another
    305 format on the device or web, you must format the suggestions as rows in a table and
    306 present them with a {@link android.database.Cursor}. There are several columns that the Search
    307 Manager 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
    312 to 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
    318 sections, 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
    323 format. The data in this column will be displayed as a second, smaller line of text below the
    324 primary 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
    327 icon-plus-text format with the icon on the left side. This value should be a reference to the
    328 icon. 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
    331 icon-plus-text format with the icon on the right side. This value should be a reference to the
    332 icon. 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
    335 be used when forming the suggestion's Intent . If the element is not provided, the action will be
    336 taken from the {@code android:searchSuggestIntentAction} field in your searchable configuration. At
    337 least one of these
    338 must be present for the suggestion to generate an Intent. Note: If your action is the same for all
    339 suggestions, it is more efficient to specify the action using {@code
    340 android: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
    343 used when forming the suggestion's Intent. If the element is not provided, the data will be taken
    344 from the {@code android:searchSuggestIntentData} field in your searchable configuration. If neither
    345 source is provided,
    346 the Intent's data field will be null. Note: If your data is the same for all suggestions, or can be
    347 described using a constant part and a specific ID, it is more efficient to specify it using {@code
    348 android: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
    352 be appended to the data field in the Intent. This should only be used if the data field specified
    353 by the {@code android:searchSuggestIntentData} attribute in the searchable configuration has already
    354 been 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
    357 that will be used when forming the suggestion's Intent. If not provided, the Intent's extra data
    358 field will be
    359 null. This column allows suggestions to provide additional arbitrary data which will be
    360 included 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
    363 used when forming the suggestion's query, included as an extra in the Intent's {@link
    364 android.app.SearchManager#QUERY} key. Required if suggestion's action is {@link
    365 android.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
    368 whether a search suggestion should be stored as a
    369 shortcut, and whether it should be validated. Shortcuts are usually formed when the user clicks a
    370 suggestion from Quick Search Box. If missing, the result will be stored as a shortcut and never
    371 refreshed. If set to {@link android.app.SearchManager#SUGGEST_NEVER_MAKE_SHORTCUT}, the result will
    372 not be stored as a shortcut.
    373 Otherwise, 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
    377 a spinner should be shown instead of an icon from {@link
    378 android.app.SearchManager#SUGGEST_COLUMN_ICON_2}
    379 while 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
    383 they 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
    390 dialog (instead of performing a search), the Search Manager will send
    391 a 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
    398 android.content.Intent#ACTION_VIEW}, which is appropriate when
    399 you want to open something, like the definition for a word, a person's contact information, or a web
    400 page. However, the Intent action can be whatever you want and can even be different for each
    401 suggestion.</p>
    402 
    403 <p>To declare an Intent action that will be the same for all suggestions, define the action in
    404 the {@code android:searchSuggestIntentAction} attribute of your searchable configuration file. For
    405 example:</p>
    406 
    407 <pre>
    408 &lt;?xml version="1.0" encoding="utf-8"?>
    409 &lt;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 &lt;/searchable>
    415 </pre>
    416 
    417 <p>If you want to declare an Intent action that's unique for each suggestion, add the {@link
    418 android.app.SearchManager#SUGGEST_COLUMN_INTENT_ACTION} column to
    419 your 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
    423 android:searchSuggestIntentAction} attribute with an action to be used with all suggestions by
    424 default, 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
    426 a value in the {@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_ACTION} column, then the
    427 Intent 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}
    432 column 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
    438 action you've defined (as discussed in the previous section), but the Intent must also carry
    439 data in order for your Activity to identify which suggestions was selected. Specifically,
    440 the data should be something unique for each suggestion, such as the row ID for the suggestion in
    441 your suggestions table. When the Intent is received,
    442 you can retrieve the attached data with {@link android.content.Intent#getData()} or {@link
    443 android.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
    449 android.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
    451 unique to each suggestion. Place these parts into the {@code android:searchSuggestIntentData}
    452 attribute of the searchable configuration and the {@link
    453 android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA_ID} column of your
    454 suggestions table, respectively.</li>
    455 </ol>
    456 
    457 <p>The first option is straight-forward. Simply provide all necessary data information for each
    458 Intent in the suggestions table by including the {@link
    459 android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA} column and then populating it with unique
    460 data for each row. The data from this column will be attached to the Intent exactly as it
    461 is found in this column. You can then retrieve it with with {@link android.content.Intent#getData()}
    462 or {@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
    465 Intent 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
    467 column. See the <a
    468 href="{@docRoot}resources/samples/SearchableDictionary/index.html">Searchable Dictionary sample
    469 app</a> for an example in which {@link android.database.sqlite.SQLiteQueryBuilder} is used to
    470 create 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.
    473 Declare the piece of the URI that is common to all suggestions in the {@code
    474 android:searchSuggestIntentData} attribute of your searchable configuration. For example:</p>
    475 
    476 <pre>
    477 &lt;?xml version="1.0" encoding="utf-8"?>
    478 &lt;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 &lt;/searchable>
    485 </pre>
    486 
    487 <p>Now include the final path for each suggestion (the unique part) in the {@link
    488 android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA_ID}
    489 column of your suggestions table. When the user selects a suggestion, the Search Manager will take
    490 the string from {@code android:searchSuggestIntentData}, append a slash ("/") and then add the
    491 respective value from the {@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA_ID} column to
    492 form a complete content URI. You can then retrieve the {@link android.net.Uri} with with {@link
    493 android.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
    499 information about the suggestion. The data saved in this column will be placed in {@link
    500 android.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
    506 need your searchable Activity to handle these Intents as they are delivered once the user selects a
    507 suggestion. (This is, of course, in addition to handling the {@link
    508 android.content.Intent#ACTION_SEARCH} Intent, which your searchable Activity already does.)
    509 Accepting the new Intent is rather self-explanatory, so we'll skip straight to an example:</p>
    510 
    511 <pre>
    512 Intent intent = getIntent();
    513 if (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
    525 android.content.Intent#ACTION_VIEW} and the data carries a complete URI pointing to the suggested
    526 item, as synthesized by the {@code android:searchSuggestIntentData} string and {@link
    527 android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA_ID} column. The URI is then passed to a local
    528 method 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
    535 text in the search dialog won't change, by default. However, you can temporarily rewrite the
    536 user's query text as it appears in the text box with
    537 a query that matches the currently selected suggestion. This enables the user to see what query is
    538 being suggested (if appropriate) and then select the search box and edit the query before
    539 dispatching 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
    546 android.app.SearchManager#SUGGEST_COLUMN_TEXT_1}
    547 column 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
    551 query text. Note that this should only
    552 be used with Uri's or other data formats that are intended to be user-visible, such as HTTP URLs.
    553 Internal 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
    555 android.app.SearchManager#SUGGEST_COLUMN_QUERY} column of your suggestions table. If this column is
    556 present 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
    564 to the globally-accessible Quick Search Box is as easy as modifying your searchable configuration to
    565 include {@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
    568 custom suggestions requires a permission for read access. In which case, you need to add a special
    569 {@code &lt;path-permission>} element for the provider to grant Quick Search Box read access to your
    570 content provider. For example:</p>
    571 
    572 <pre>
    573 &lt;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   &lt;path-permission android:pathPrefix="/search_suggest_query"
    578                    android:readPermission="android.permission.GLOBAL_SEARCH" />
    579 &lt;/provider>
    580 </pre>
    581 
    582 <p>In this example, the provider restricts read and write access to the content. The
    583 {@code &lt;path-permission>} element amends the restriction by granting read access to content
    584 inside the {@code "/search_suggest_query"} path prefix when the {@code
    585 "android.permission.GLOBAL_SEARCH"} permission exists. This grants access to Quick Search Box
    586 so 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
    589 infrastructure.</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
    595 actually enabled to provide suggestions in Quick Search Box, by default. It is the user's choice
    596 whether to include suggestions from your application in the Quick Search Box. To enable search
    597 suggestions from your application, the user must open "Searchable items" (in Settings > Search) and
    598 enable 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
    601 settings page. The entry includes the name of the application and a short description of what
    602 content can be searched from the application and made available for suggestions in Quick Search Box.
    603 To define the description text for your searchable application, add the {@code
    604 android:searchSettingsDescription} attribute to your searchable configuration. For example:</p>
    605 
    606 <pre>
    607 &lt;?xml version="1.0" encoding="utf-8"?>
    608 &lt;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 &lt;/searchable>
    616 </pre>
    617 
    618 <p>The string for {@code android:searchSettingsDescription} should be as concise as possible and
    619 state the content that is searchable. For example, "Artists, albums, and tracks" for a music
    620 application, or "Saved notes" for a notepad application. Providing this description is important so
    621 the user knows what kind of suggestions will be provided. This attribute should always be included
    622 when {@code android:includeInGlobalSearch} is "true".</p>
    623 
    624 <p>Remember that the user must visit this settings menu to enable search suggestions for your
    625 application before your search suggestions will appear in Quick Search Box. As such, if search is an
    626 important aspect of your application, then you may want to consider a way to message this to your
    627 users &mdash; perhaps with a note the first time they launch the app about how to enable search
    628 suggestions 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.
    634 These are suggestions that the Search Manager has copied from your content provider  so it can
    635 quickly 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
    638 suggestion data may change over time, then you can request that the shortcuts be refreshed. For
    639 instance, if your suggestions refer to dynamic data, such as a contact's presence status, then you
    640 should request that the suggestion shortcuts be refreshed when shown to the user. To do so,
    641 include the {@link android.app.SearchManager#SUGGEST_COLUMN_SHORTCUT_ID} in your suggestions table.
    642 Using this column, you can
    643 configure 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
    647 suggestion.
    648     <p>Provide a value in the {@link android.app.SearchManager#SUGGEST_COLUMN_SHORTCUT_ID} column
    649 and the suggestion will be
    650 re-queried for a fresh version of the suggestion each time the shortcut is displayed. The shortcut
    651 will be quickly displayed with whatever data was most recently available until the refresh query
    652 returns, after which the suggestion will be dynamically refreshed with the new information. The
    653 refresh query will be sent to your content provider with a URI path of {@link
    654 android.app.SearchManager#SUGGEST_URI_PATH_SHORTCUT}
    655 (instead of {@link android.app.SearchManager#SUGGEST_URI_PATH_QUERY}). The Cursor you return should
    656 contain one suggestion using the
    657 same columns as the original suggestion, or be empty, indicating that the shortcut is no
    658 longer 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
    660 refresh, you may also add the {@link
    661 android.app.SearchManager#SUGGEST_COLUMN_SPINNER_WHILE_REFRESHING} column to your suggestions
    662 table with a value
    663 of "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
    668 this case, the suggestion will never be copied into a shortcut. This should only be necessary if you
    669 absolutely do not want the previously copied suggestion to appear at all. (Recall that if you
    670 provide a normal value for the column then the suggestion shortcut will appear only until the
    671 refresh 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
    674 suggestion 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
    681 your 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
    687 to the user for a particular query will be determined as appropriate by Quick Search Box ranking.
    688 This may depend on how many other apps have results for that query, and how often the user has
    689 selected on your results compared to those of the other apps. There is no guarantee about how
    690 ranking will occur, or whether your app's suggestions will show at all for a given query. In
    691 general, you can expect that providing quality results will increase the likelihood that your app's
    692 suggestions are provided in a prominent position, and apps that provide lower quality suggestions
    693 will 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
    697 Dictionary sample app</a> for a complete demonstration of custom search suggestions.</p>
    698 </div>
    699 
    700