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