| page.title=Adding Custom Suggestions |
| parent.title=Search |
| parent.link=index.html |
| @jd:body |
| |
| <div id="qv-wrapper"> |
| <div id="qv"> |
| <h2>In this document</h2> |
| <ol> |
| <li><a href="#TheBasics">The Basics</a></li> |
| <li><a href="#CustomSearchableConfiguration">Modifying the Searchable Configuration</a></li> |
| <li><a href="#CustomContentProvider">Creating a Content Provider</a> |
| <ol> |
| <li><a href="#HandlingSuggestionQuery">Handling a suggestion query</a></li> |
| <li><a href="#SuggestionTable">Building a suggestion table</a></li> |
| </ol> |
| </li> |
| <li><a href="#IntentForSuggestions">Declaring an Intent for Suggestions</a> |
| <ol> |
| <li><a href="#IntentAction">Declaring the intent action</a></li> |
| <li><a href="#IntentData">Declaring the intent data</a></li> |
| </ol> |
| </li> |
| <li><a href="#HandlingIntent">Handling the Intent</a></li> |
| <li><a href="#RewritingQueryText">Rewriting the Query Text</a></li> |
| <li><a href="#QSB">Exposing Search Suggestions to Quick Search Box</a></li> |
| </ol> |
| |
| <h2>Key classes</h2> |
| <ol> |
| <li>{@link android.app.SearchManager}</li> |
| <li>{@link android.content.SearchRecentSuggestionsProvider}</li> |
| <li>{@link android.content.ContentProvider}</li> |
| </ol> |
| |
| <h2>Related samples</h2> |
| <ol> |
| <li><a href="{@docRoot}resources/samples/SearchableDictionary/index.html">Searchable |
| Dictionary</a></li> |
| </ol> |
| |
| <h2>See also</h2> |
| <ol> |
| <li><a href="searchable-config.html">Searchable Configuration</a></li> |
| <li><a href="{@docRoot}guide/topics/providers/content-providers.html">Content Providers</a></li> |
| </ol> |
| </div> |
| </div> |
| |
| <p>When using the Android search dialog or search widget, you can provide custom search suggestions |
| that are created from data in your application. For example, if your application is a word |
| dictionary, you can suggest words from the |
| dictionary that match the text entered so far. These are the most valuable suggestions, because you |
| can effectively predict what the user wants and provide instant access to it. Figure 1 shows |
| an example of a search dialog with custom suggestions.</p> |
| |
| <p>Once you provide custom suggestions, you can also make them available to the system-wide Quick |
| Search Box, providing access to your content from outside your application.</p> |
| |
| <p>Before you begin with this guide to add custom suggestions, you need to have implemented the |
| Android search dialog or a search widget for searches in your |
| application. If you haven't, see <a href="search-dialog.html">Creating a Search Interface</a>.</p> |
| |
| |
| <h2 id="TheBasics">The Basics</h2> |
| |
| <div class="figure" style="width:250px"> |
| <img src="{@docRoot}images/search/search-suggest-custom.png" alt="" height="417" /> |
| <p class="img-caption"><strong>Figure 1.</strong> Screenshot of a search dialog with custom |
| search suggestions.</p> |
| </div> |
| |
| <p>When the user selects a custom suggestion, the Android system sends an {@link |
| android.content.Intent} to |
| your searchable activity. Whereas a normal search query sends an intent with the {@link |
| android.content.Intent#ACTION_SEARCH} action, you can instead define your custom suggestions to use |
| {@link android.content.Intent#ACTION_VIEW} (or any other intent action), and also include data |
| that's relevant to the selected suggestion. Continuing |
| the dictionary example, when the user selects a suggestion, your application can immediately |
| open the definition for that word, instead of searching the dictionary for matches.</p> |
| |
| <p>To provide custom suggestions, do the following:</p> |
| |
| <ul> |
| <li>Implement a basic searchable activity, as described in <a |
| href="search-dialog.html">Creating a Search Interface</a>.</li> |
| <li>Modify the searchable configuration with information about the content provider that |
| provides custom suggestions.</li> |
| <li>Build a table (such as in an {@link android.database.sqlite.SQLiteDatabase}) for your |
| suggestions and format the table with required columns.</li> |
| <li>Create a <a href="{@docRoot}guide/topics/providers/content-providers.html">Content |
| Provider</a> that has access to your suggestions table and declare the provider |
| in your manifest.</li> |
| <li>Declare the type of {@link android.content.Intent} to be sent when the user selects a |
| suggestion (including a custom action and custom data). </li> |
| </ul> |
| |
| <p>Just as the Android system displays the search dialog, it also displays your search |
| suggestions. All you need is a content provider from which the system can retrieve your |
| suggestions. If you're not familiar with creating content |
| providers, read the <a href="{@docRoot}guide/topics/providers/content-providers.html">Content |
| Providers</a> developer guide before you continue.</p> |
| |
| <p>When the system identifies that your activity is searchable and provides search |
| suggestions, the following procedure takes place when the user types a query:</p> |
| |
| <ol> |
| <li>The system takes the search query text (whatever has been typed so far) and performs a |
| query to your content provider that manages your suggestions.</li> |
| <li>Your content provider returns a {@link android.database.Cursor} that points to all |
| suggestions that are relevant to the search query text.</li> |
| <li>The system displays the list of suggestions provided by the Cursor.</li> |
| </ol> |
| |
| <p>Once the custom suggestions are displayed, the following might happen:</p> |
| |
| <ul> |
| <li>If the user types another key, or changes the query in any way, the above steps are repeated |
| and the suggestion list is updated as appropriate. </li> |
| <li>If the user executes the search, the suggestions are ignored and the search is delivered |
| to your searchable activity using the normal {@link android.content.Intent#ACTION_SEARCH} |
| intent.</li> |
| <li>If the user selects a suggestion, an intent is sent to your searchable activity, carrying a |
| custom action and custom data so that your application can open the suggested content.</li> |
| </ul> |
| |
| |
| |
| <h2 id="CustomSearchableConfiguration">Modifying the searchable configuration</h2> |
| |
| <p>To add support for custom suggestions, add the {@code android:searchSuggestAuthority} attribute |
| to the {@code <searchable>} element in your searchable configuration file. For example:</p> |
| |
| <pre> |
| <?xml version="1.0" encoding="utf-8"?> |
| <searchable xmlns:android="http://schemas.android.com/apk/res/android" |
| android:label="@string/app_label" |
| android:hint="@string/search_hint" |
| <b>android:searchSuggestAuthority="com.example.MyCustomSuggestionProvider"</b>> |
| </searchable> |
| </pre> |
| |
| <p>You might need some additional attributes, depending on the type of intent you attach |
| to each suggestion and how you want to format queries to your content provider. The other optional |
| attributes are discussed in the following sections.</p> |
| |
| |
| |
| <h2 id="CustomContentProvider">Creating a Content Provider</h2> |
| |
| <p>Creating a content provider for custom suggestions requires previous knowledge about content |
| providers that's covered in the <a |
| href="{@docRoot}guide/topics/providers/content-providers.html">Content Provider</a> developer |
| guide. For the most part, a content provider for custom suggestions is the |
| same as any other content provider. However, for each suggestion you provide, the respective row in |
| the {@link android.database.Cursor} must include specific columns that the system |
| understands and uses to format the suggestions.</p> |
| |
| <p>When the user starts typing into the search dialog or search widget, the system queries |
| your content provider for suggestions by calling {@link |
| android.content.ContentProvider#query(Uri,String[],String,String[],String) query()} each time |
| a letter is typed. In your implementation of {@link |
| android.content.ContentProvider#query(Uri,String[],String,String[],String) query()}, your |
| content provider must search your suggestion data and return a {@link |
| android.database.Cursor} that points to the rows you have determined to be good suggestions.</p> |
| |
| <p>Details about creating a content provider for custom suggestions are discussed in the following |
| two sections:</p> |
| <dl> |
| <dt><a href="#HandlingSuggestionQuery">Handling the suggestion query</a></dt> |
| <dd>How the system sends requests to your content provider and how to handle them</dd> |
| <dt><a href="#SuggestionTable">Building a suggestion table</a></dt> |
| <dd>How to define the columns that the system expects in the {@link |
| android.database.Cursor} returned with each query</dd> |
| </dl> |
| |
| |
| <h3 id="HandlingSuggestionQuery">Handling the suggestion query</h3> |
| |
| <p>When the system requests suggestions from your content provider, it calls your content |
| provider's {@link android.content.ContentProvider#query(Uri,String[],String,String[],String) |
| query()} method. You must |
| implement this method to search your suggestion data and return a |
| {@link android.database.Cursor} pointing to the suggestions you deem relevant.</p> |
| |
| <p>Here's a summary of the parameters that the system passes to your {@link |
| android.content.ContentProvider#query(Uri,String[],String,String[],String) query()} method |
| (listed in order):</p> |
| |
| <dl> |
| <dt><code>uri</code></dt> |
| <dd>Always a content {@link android.net.Uri}, formatted as: |
| <pre class="no-pretty-print"> |
| content://<em>your.authority</em>/<em>optional.suggest.path</em>/<em>{@link |
| android.app.SearchManager#SUGGEST_URI_PATH_QUERY}</em> |
| </pre> |
| <p>The default behavior is for system to pass this URI and append it with the query text. |
| For example:</p> |
| <pre class="no-pretty-print"> |
| content://<em>your.authority</em>/<em>optional.suggest.path</em>/<em>{@link |
| android.app.SearchManager#SUGGEST_URI_PATH_QUERY}</em>/puppies |
| </pre> |
| <p>The query text on the end is encoded using URI encoding rules, so you might need to decode |
| it before performing a search.</p> |
| <p>The <em>{@code optional.suggest.path}</em> portion is only included in the URI if you have set |
| such a path in your searchable configuration file with the {@code android:searchSuggestPath} |
| attribute. This is only needed if you use the same content provider for multiple searchable |
| activities, in which case, you need to disambiguate the source of the suggestion query.</p> |
| <p class="note"><strong>Note:</strong> {@link |
| android.app.SearchManager#SUGGEST_URI_PATH_QUERY} is not the literal |
| string provided in the URI, but a constant that you should use if you need to refer to this |
| path.</p> |
| </dd> |
| |
| <dt><code>projection</code></dt> |
| <dd>Always null</dd> |
| |
| <dt><code>selection</code></dt> |
| <dd>The value provided in the {@code android:searchSuggestSelection} attribute of |
| your searchable configuration file, or null if you have not declared the {@code |
| android:searchSuggestSelection} attribute. More about using this to <a href="#GetTheQuery">get the |
| query</a> below.</dd> |
| |
| <dt><code>selectionArgs</code></dt> |
| <dd>Contains the search query as the first (and only) element of the array if you have |
| declared the {@code android:searchSuggestSelection} attribute in your searchable configuration. If |
| you have not declared {@code android:searchSuggestSelection}, then this parameter is null. More |
| about using this to <a href="#GetTheQuery">get the query</a> below.</dd> |
| |
| <dt><code>sortOrder</code></dt> |
| <dd>Always null</dd> |
| </dl> |
| |
| <p>The system can send you the search query text in two ways. The |
| default manner is for the query text to be included as the last path of the content |
| URI passed in the {@code uri} parameter. However, if you include a selection value in your |
| searchable configuration's {@code |
| android:searchSuggestSelection} attribute, then the query text is instead passed as the first |
| element of the {@code selectionArgs} string array. Both options are summarized next.</p> |
| |
| |
| <h4 id="GetTheQueryUri">Get the query in the Uri</h4> |
| |
| <p>By default, the query is appended as the last segment of the {@code uri} |
| parameter (a {@link android.net.Uri} object). To retrieve the query text in this case, simply use |
| {@link android.net.Uri#getLastPathSegment()}. For example:</p> |
| |
| <pre> |
| String query = uri.getLastPathSegment().toLowerCase(); |
| </pre> |
| |
| <p>This returns the last segment of the {@link android.net.Uri}, which is the query text entered |
| by the user.</p> |
| |
| |
| |
| <h4 id="GetTheQuery">Get the query in the selection arguments</h4> |
| |
| <p>Instead of using the URI, you might decide it makes more sense for your {@link |
| android.content.ContentProvider#query(Uri,String[],String,String[],String) query()} method to |
| receive everything it needs to perform the look-up and you want the |
| {@code selection} and {@code selectionArgs} parameters to carry the appropriate values. In such a |
| case, add the {@code android:searchSuggestSelection} attribute to your searchable configuration with |
| your SQLite selection string. In the selection string, include a question mark ("?") as |
| a placeholder for the actual search query. The system calls {@link |
| android.content.ContentProvider#query(Uri,String[],String,String[],String) query()} with the |
| selection string as the {@code selection} parameter and the search query as the first |
| element in the {@code selectionArgs} array.</p> |
| |
| <p>For example, here's how you might form the {@code android:searchSuggestSelection} attribute to |
| create a full-text search statement:</p> |
| |
| <pre> |
| <?xml version="1.0" encoding="utf-8"?> |
| <searchable xmlns:android="http://schemas.android.com/apk/res/android" |
| android:label="@string/app_label" |
| android:hint="@string/search_hint" |
| android:searchSuggestAuthority="com.example.MyCustomSuggestionProvider" |
| android:searchSuggestIntentAction="android.intent.action.VIEW" |
| <b>android:searchSuggestSelection="word MATCH ?"</b>> |
| </searchable> |
| </pre> |
| |
| <p>With this configuration, your {@link |
| android.content.ContentProvider#query(Uri,String[],String,String[],String) query()} method |
| delivers the {@code selection} parameter as {@code "word MATCH ?"} and the {@code selectionArgs} |
| parameter as the search query. When you pass these to an SQLite |
| {@link android.database.sqlite.SQLiteDatabase#query(String,String[],String,String[],String,String, |
| String) query()} method, as their respective arguments, they are synthesized together (the |
| question mark is replaced with the query |
| text). If you chose to receive suggestion queries this way and need to add wildcards to |
| the query text, append (and/or prefix) them to the {@code selectionArgs} |
| parameter, because this value is wrapped in quotes and inserted in place of the |
| question mark.</p> |
| |
| <p>Another new attribute in the example above is {@code android:searchSuggestIntentAction}, which |
| defines the intent action sent with each intent when the user selects a suggestion. It is |
| discussed further in the section about <a href="#IntentForSuggestions">Declaring an Intent for |
| Suggestions</a>.</p> |
| |
| <p class="note"><strong>Tip:</strong> If you don't want to define a selection clause in |
| the {@code android:searchSuggestSelection} attribute, but would still like to receive the query |
| text in the {@code selectionArgs} parameter, simply provide a non-null value for the {@code |
| android:searchSuggestSelection} attribute. This triggers the query to be passed in {@code |
| selectionArgs} and you can ignore the {@code selection} parameter. In this way, you can instead |
| define the actual selection clause at a lower level so that your content provider doesn't have to |
| handle it.</p> |
| |
| |
| |
| <h3 id="SuggestionTable">Building a suggestion table</h3> |
| |
| <div class="sidebox-wrapper"> |
| <div class="sidebox"> |
| <h2>Creating a Cursor without a table</h2> |
| <p>If your search suggestions are not stored in a table format (such as an SQLite table) using the |
| columns required by the |
| system, then you can search your suggestion data for matches and then format them |
| into the necessary table on each request. To do so, create a {@link android.database.MatrixCursor} |
| using the required column names and then add a row for each suggestion using {@link |
| android.database.MatrixCursor#addRow(Object[])}. Return the final product from your Content |
| Provider's {@link |
| android.content.ContentProvider#query(Uri,String[],String,String[],String) query()} method.</p> |
| </div> |
| </div> |
| |
| <p>When you return suggestions to the system with a {@link android.database.Cursor}, the |
| system expects specific columns in each row. So, regardless of whether you |
| decide to store |
| your suggestion data in an SQLite database on the device, a database on a web server, or another |
| format on the device or web, you must format the suggestions as rows in a table and |
| present them with a {@link android.database.Cursor}. The system understands several columns, but |
| only two are required:</p> |
| |
| <dl> |
| <dt>{@link android.provider.BaseColumns#_ID}</dt> |
| <dd>A unique integer row ID for each suggestion. The system requires this in order |
| to present suggestions in a {@link android.widget.ListView}.</dd> |
| <dt>{@link android.app.SearchManager#SUGGEST_COLUMN_TEXT_1}</dt> |
| <dd>The string that is presented as a suggestion.</dd> |
| </dl> |
| |
| <p>The following columns are all optional (and most are discussed further in the following |
| sections):</p> |
| |
| <dl> |
| <dt>{@link android.app.SearchManager#SUGGEST_COLUMN_TEXT_2}</dt> |
| <dd>A string. If your Cursor includes this column, then all suggestions are provided in a |
| two-line format. The string in this column is displayed as a second, smaller line of text below the |
| primary suggestion text. It can be null or empty to indicate no secondary text.</dd> |
| <dt>{@link android.app.SearchManager#SUGGEST_COLUMN_ICON_1}</dt> |
| <dd>A drawable resource, content, or file URI string. If your Cursor includes this column, then |
| all suggestions are provided in an icon-plus-text format with the drawable icon on the left side. |
| This can be null or zero to indicate no icon in this row.</dd> |
| <dt>{@link android.app.SearchManager#SUGGEST_COLUMN_ICON_2}</dt> |
| <dd>A drawable resource, content, or file URI string. If your Cursor includes this column, then |
| all suggestions are provided in an icon-plus-text format with the icon on the right side. This can |
| be null or zero to indicate no icon in this row.</dd> |
| <dt>{@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_ACTION}</dt> |
| <dd>An intent action string. If this column exists and contains a value at the given row, the |
| action defined here is used when forming the suggestion's intent. If the element is not |
| provided, the action is taken from the {@code android:searchSuggestIntentAction} field in your |
| searchable configuration. If your action is the same for all |
| suggestions, it is more efficient to specify the action using {@code |
| android:searchSuggestIntentAction} and omit this column.</dd> |
| <dt>{@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA}</dt> |
| <dd>A data URI string. If this column exists and contains a value at the given row, this is the |
| data that is used when forming the suggestion's intent. If the element is not provided, the data is |
| taken from the {@code android:searchSuggestIntentData} field in your searchable configuration. If |
| neither source is provided, |
| the intent's data field is null. If your data is the same for all suggestions, or can be |
| described using a constant part and a specific ID, it is more efficient to specify it using {@code |
| android:searchSuggestIntentData} and omit this column. |
| </dd> |
| <dt>{@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA_ID}</dt> |
| <dd>A URI path string. If this column exists and contains a value at the given row, then "/" and |
| this value is appended to the data field in the intent. This should only be used if the data field |
| specified |
| by the {@code android:searchSuggestIntentData} attribute in the searchable configuration has already |
| been set to an appropriate base string.</dd> |
| <dt>{@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_EXTRA_DATA}</dt> |
| <dd>Arbitrary data. If this column exists and contains a value at a given row, this is the |
| <em>extra</em> data used when forming the suggestion's intent. If not provided, the |
| intent's extra data field is null. This column allows suggestions to provide additional data that is |
| included as an extra in the intent's {@link android.app.SearchManager#EXTRA_DATA_KEY} key.</dd> |
| <dt>{@link android.app.SearchManager#SUGGEST_COLUMN_QUERY}</dt> |
| <dd>If this column exists and this element exists at the given row, this is the data that is |
| used when forming the suggestion's query, included as an extra in the intent's {@link |
| android.app.SearchManager#QUERY} key. Required if suggestion's action is {@link |
| android.content.Intent#ACTION_SEARCH}, optional otherwise.</dd> |
| <dt>{@link android.app.SearchManager#SUGGEST_COLUMN_SHORTCUT_ID}</dt> |
| <dd>Only used when providing suggestions for Quick Search Box. This column indicates |
| whether a search suggestion should be stored as a |
| shortcut and whether it should be validated. Shortcuts are usually formed when the user clicks a |
| suggestion from Quick Search Box. If missing, the result is stored as a shortcut and never |
| refreshed. If set to {@link android.app.SearchManager#SUGGEST_NEVER_MAKE_SHORTCUT}, the result is |
| not stored as a shortcut. |
| Otherwise, the shortcut ID is used to check back for an up to date suggestion using |
| {@link android.app.SearchManager#SUGGEST_URI_PATH_SHORTCUT}.</dd> |
| <dt>{@link android.app.SearchManager#SUGGEST_COLUMN_SPINNER_WHILE_REFRESHING}</dt> |
| <dd>Only used when providing suggestions for Quick Search Box. This column specifies that |
| a spinner should be shown instead of an icon from {@link |
| android.app.SearchManager#SUGGEST_COLUMN_ICON_2} |
| while the shortcut of this suggestion is being refreshed in Quick Search Box.</dd> |
| </dl> |
| |
| <p>Some of these columns are discussed more in the following sections.</p> |
| |
| |
| |
| <h2 id="IntentForSuggestions">Declaring an Intent for Suggestions</h2> |
| |
| <p>When the user selects a suggestion from the list that appears below the search dialog or widget, |
| the system sends a custom {@link android.content.Intent} to your searchable activity. You |
| must define the action and data for the intent.</p> |
| |
| |
| <h3 id="IntentAction">Declaring the intent action</h3> |
| |
| <p>The most common intent action for a custom suggestion is {@link |
| android.content.Intent#ACTION_VIEW}, which is appropriate when |
| you want to open something, like the definition for a word, a person's contact information, or a web |
| page. However, the intent action can be any other action and can even be different for each |
| suggestion.</p> |
| |
| <p>Depending on whether you want all suggestions to use the same intent action, you |
| can define the action in two ways:</p> |
| |
| <ol type="a"> |
| <li>Use the {@code android:searchSuggestIntentAction} attribute of your searchable configuration |
| file to define the action for all suggestions. <p>For example:</p> |
| |
| <pre> |
| <?xml version="1.0" encoding="utf-8"?> |
| <searchable xmlns:android="http://schemas.android.com/apk/res/android" |
| android:label="@string/app_label" |
| android:hint="@string/search_hint" |
| android:searchSuggestAuthority="com.example.MyCustomSuggestionProvider" |
| <b>android:searchSuggestIntentAction="android.Intent.action.VIEW"</b> > |
| </searchable> |
| </pre> |
| |
| </li> |
| <li>Use the {@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_ACTION} column to define the |
| action for individual suggestions. |
| <p>Add the {@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_ACTION} column to |
| your suggestions table and, for each suggestion, place in it the action to use (such as |
| {@code "android.Intent.action.VIEW"}).</p> |
| |
| </li> |
| </ol> |
| |
| <p>You can also combine these two techniques. For instance, you can include the {@code |
| android:searchSuggestIntentAction} attribute with an action to be used with all suggestions by |
| default, then override this action for some suggestions by declaring a different action in the |
| {@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_ACTION} column. If you do not include |
| a value in the {@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_ACTION} column, then the |
| intent provided in the {@code android:searchSuggestIntentAction} attribute is used.</p> |
| |
| <p class="note"><strong>Note</strong>: If you do not include the |
| {@code android:searchSuggestIntentAction} attribute in your searchable configuration, then you |
| <em>must</em> include a value in the {@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_ACTION} |
| column for every suggestion, or the intent will fail.</p> |
| |
| |
| |
| <h3 id="IntentData">Declaring intent data</h3> |
| |
| <p>When the user selects a suggestion, your searchable activity receives the intent with the |
| action you've defined (as discussed in the previous section), but the intent must also carry |
| data in order for your activity to identify which suggestion was selected. Specifically, |
| the data should be something unique for each suggestion, such as the row ID for the suggestion in |
| your SQLite table. When the intent is received, |
| you can retrieve the attached data with {@link android.content.Intent#getData()} or {@link |
| android.content.Intent#getDataString()}.</p> |
| |
| <p>You can define the data included with the intent in two ways:</p> |
| |
| <ol type="a"> |
| <li>Define the data for each suggestion inside the {@link |
| android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA} column of your suggestions table. |
| |
| <p>Provide all necessary data information for each intent in the suggestions table by including the |
| {@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA} column and then populating it with |
| unique data for each row. The data from this column is attached to the intent exactly as you |
| define it in this column. You can then retrieve it with with {@link |
| android.content.Intent#getData()} or {@link android.content.Intent#getDataString()}.</p> |
| |
| <p class="note"><strong>Tip</strong>: It's usually easiest to use the table's row ID as the |
| Intent data, because it's always unique. And the easiest way to do that is by using the |
| {@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA} column name as an alias for the row ID |
| column. See the <a |
| href="{@docRoot}resources/samples/SearchableDictionary/index.html">Searchable Dictionary sample |
| app</a> for an example in which {@link android.database.sqlite.SQLiteQueryBuilder} creates a |
| projection map of column names to aliases.</p> |
| </li> |
| |
| <li>Fragment a data URI into two pieces: the portion common to all suggestions and the portion |
| unique to each suggestion. Place these parts into the {@code android:searchSuggestintentData} |
| attribute of the searchable configuration and the {@link |
| android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA_ID} column of your |
| suggestions table, respectively. |
| |
| <p>Declare the piece of the URI that is common to all suggestions in the {@code |
| android:searchSuggestIntentData} attribute of your searchable configuration. For example:</p> |
| |
| <pre> |
| <?xml version="1.0" encoding="utf-8"?> |
| <searchable xmlns:android="http://schemas.android.com/apk/res/android" |
| android:label="@string/app_label" |
| android:hint="@string/search_hint" |
| android:searchSuggestAuthority="com.example.MyCustomSuggestionProvider" |
| android:searchSuggestIntentAction="android.intent.action.VIEW" |
| <b>android:searchSuggestIntentData="content://com.example/datatable"</b> > |
| </searchable> |
| </pre> |
| |
| <p>Then include the final path for each suggestion (the unique part) in the {@link |
| android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA_ID} |
| column of your suggestions table. When the user selects a suggestion, the system takes |
| the string from {@code android:searchSuggestIntentData}, appends a slash ("/") and then adds the |
| respective value from the {@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA_ID} column to |
| form a complete content URI. You can then retrieve the {@link android.net.Uri} with with {@link |
| android.content.Intent#getData()}.</p> |
| |
| </li> |
| </ol> |
| |
| <h4>Add more data</h4> |
| |
| <p>If you need to express even more information with your intent, you can add another table column, |
| {@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_EXTRA_DATA}, which can store additional |
| information about the suggestion. The data saved in this column is placed in {@link |
| android.app.SearchManager#EXTRA_DATA_KEY} of the intent's extra Bundle.</p> |
| |
| |
| |
| <h2 id="HandlingIntent">Handling the Intent</h2> |
| |
| <p>Now that you provide custom search suggestions with custom intents, you |
| need your searchable activity to handle these intents when the user selects a |
| suggestion. This is in addition to handling the {@link |
| android.content.Intent#ACTION_SEARCH} intent, which your searchable activity already does. |
| Here's an example of how you can handle the intents during your activity {@link |
| android.app.Activity#onCreate(Bundle) onCreate()} callback:</p> |
| |
| <pre> |
| Intent intent = getIntent(); |
| if (Intent.ACTION_SEARCH.equals(intent.getAction())) { |
| // Handle the normal search query case |
| String query = intent.getStringExtra(SearchManager.QUERY); |
| doSearch(query); |
| } else if (Intent.ACTION_VIEW.equals(intent.getAction())) { |
| // Handle a suggestions click (because the suggestions all use ACTION_VIEW) |
| Uri data = intent.getData(); |
| showResult(data); |
| } |
| </pre> |
| |
| <p>In this example, the intent action is {@link |
| android.content.Intent#ACTION_VIEW} and the data carries a complete URI pointing to the suggested |
| item, as synthesized by the {@code android:searchSuggestIntentData} string and {@link |
| android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA_ID} column. The URI is then passed to the local |
| {@code showResult()} method that queries the content provider for the item specified by the URI.</p> |
| |
| <p class="note"><strong>Note:</strong> You do <em>not</em> need to add an intent filter to your |
| Android manifest file for the intent action you defined with the {@code |
| android:searchSuggestIntentAction} attribute or {@link |
| android.app.SearchManager#SUGGEST_COLUMN_INTENT_ACTION} column. The system opens your |
| searchable activity by name to deliver the suggestion's intent, so the activity does not need to |
| declare the accepted action.</p> |
| |
| |
| <h2 id="RewritingQueryText">Rewriting the query text</h2> |
| |
| <p>If the user navigates through the suggestions list using the directional controls (such |
| as with a trackball or d-pad), the query text does not update, by default. However, you |
| can temporarily rewrite the user's query text as it appears in the text box with |
| a query that matches the suggestion currently in focus. This enables the user to see what query is |
| being suggested (if appropriate) and then select the search box and edit the query before |
| dispatching it as a search.</p> |
| |
| <p>You can rewrite the query text in the following ways:</p> |
| |
| <ol type="a"> |
| <li>Add the {@code android:searchMode} attribute to your searchable configuration with the |
| "queryRewriteFromText" value. In this case, the content from the suggestion's {@link |
| android.app.SearchManager#SUGGEST_COLUMN_TEXT_1} |
| column is used to rewrite the query text.</li> |
| <li>Add the {@code android:searchMode} attribute to your searchable configuration with the |
| "queryRewriteFromData" value. In this case, the content from the suggestion's |
| {@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA} column is used to rewrite the |
| query text. This should only |
| be used with URI's or other data formats that are intended to be user-visible, such as HTTP URLs. |
| Internal URI schemes should not be used to rewrite the query in this way.</li> |
| <li>Provide a unique query text string in the {@link |
| android.app.SearchManager#SUGGEST_COLUMN_QUERY} column of your suggestions table. If this column is |
| present and contains a value for the current suggestion, it is used to rewrite the query text |
| (and override either of the previous implementations).</li> |
| </ol> |
| |
| |
| |
| <h2 id="QSB">Exposing search suggestions to Quick Search Box</h2> |
| |
| <p>Once you configure your application to provide custom search suggestions, making them available |
| to the globally accessible Quick Search Box is as easy as modifying your searchable configuration to |
| include {@code android:includeInGlobalSearch} as "true".</p> |
| |
| <p>The only scenario in which additional work is necessary is when your content provider demands a |
| read permission. In which case, you need to add a special |
| {@code <path-permission>} element for the provider to grant Quick Search Box read access to |
| your content provider. For example:</p> |
| |
| <pre> |
| <provider android:name="MySuggestionProvider" |
| android:authorities="com.example.MyCustomSuggestionProvider" |
| android:readPermission="com.example.provider.READ_MY_DATA" |
| android:writePermission="com.example.provider.WRITE_MY_DATA"> |
| <path-permission android:pathPrefix="/search_suggest_query" |
| android:readPermission="android.permission.GLOBAL_SEARCH" /> |
| </provider> |
| </pre> |
| |
| <p>In this example, the provider restricts read and write access to the content. The |
| {@code <path-permission>} element amends the restriction by granting read access to content |
| inside the {@code "/search_suggest_query"} path prefix when the {@code |
| "android.permission.GLOBAL_SEARCH"} permission exists. This grants access to Quick Search Box |
| so that it may query your content provider for suggestions.</p> |
| |
| <p>If your content provider does not enforce read permissions, then Quick Search Box can read |
| it by default.</p> |
| |
| |
| <h3 id="EnablingSuggestions">Enabling suggestions on a device</h3> |
| |
| <p>When your application is configured to provide suggestions in Quick Search Box, it is not |
| actually enabled to provide suggestions in Quick Search Box, by default. It is the user's choice |
| whether to include suggestions from your application in the Quick Search Box. To enable search |
| suggestions from your application, the user must open "Searchable items" (in Settings > Search) and |
| enable your application as a searchable item.</p> |
| |
| <p>Each application that is available to Quick Search Box has an entry in the Searchable items |
| settings page. The entry includes the name of the application and a short description of what |
| content can be searched from the application and made available for suggestions in Quick Search Box. |
| To define the description text for your searchable application, add the {@code |
| android:searchSettingsDescription} attribute to your searchable configuration. For example:</p> |
| |
| <pre> |
| <?xml version="1.0" encoding="utf-8"?> |
| <searchable xmlns:android="http://schemas.android.com/apk/res/android" |
| android:label="@string/app_label" |
| android:hint="@string/search_hint" |
| android:searchSuggestAuthority="com.example.MyCustomSuggestionProvider" |
| android:searchSuggestIntentAction="android.intent.action.VIEW" |
| android:includeInGlobalSearch="true" |
| <b>android:searchSettingsDescription="@string/search_description"</b> > |
| </searchable> |
| </pre> |
| |
| <p>The string for {@code android:searchSettingsDescription} should be as concise as possible and |
| state the content that is searchable. For example, "Artists, albums, and tracks" for a music |
| application, or "Saved notes" for a notepad application. Providing this description is important so |
| the user knows what kind of suggestions are provided. You should always include this attribute |
| when {@code android:includeInGlobalSearch} is "true".</p> |
| |
| <p>Remember that the user must visit the settings menu to enable search suggestions for your |
| application before your search suggestions appear in Quick Search Box. As such, if search is an |
| important aspect of your application, then you might want to consider a way to convey that to |
| your users — you might provide a note the first time they launch the app that instructs |
| them how to enable search suggestions for Quick Search Box.</p> |
| |
| |
| <h3 id="ManagingShortcuts">Managing Quick Search Box suggestion shortcuts</h3> |
| |
| <p>Suggestions that the user selects from Quick Search Box can be automatically made into shortcuts. |
| These are suggestions that the system has copied from your content provider so it can |
| quickly access the suggestion without the need to re-query your content provider. </p> |
| |
| <p>By default, this is enabled for all suggestions retrieved by Quick Search Box, but if your |
| suggestion data changes over time, then you can request that the shortcuts be refreshed. For |
| instance, if your suggestions refer to dynamic data, such as a contact's presence status, then you |
| should request that the suggestion shortcuts be refreshed when shown to the user. To do so, |
| include the {@link android.app.SearchManager#SUGGEST_COLUMN_SHORTCUT_ID} in your suggestions table. |
| Using this column, you can |
| configure the shortcut behavior for each suggestion in one of the following ways:</p> |
| |
| <ol type="a"> |
| <li>Have Quick Search Box re-query your content provider for a fresh version of the suggestion |
| shortcut. |
| <p>Provide a value in the {@link android.app.SearchManager#SUGGEST_COLUMN_SHORTCUT_ID} column |
| and the suggestion is |
| re-queried for a fresh version each time the shortcut is displayed. The shortcut |
| is quickly displayed with whatever data was most recently available until the refresh query |
| returns, at which point the suggestion is refreshed with the new information. The |
| refresh query is sent to your content provider with a URI path of {@link |
| android.app.SearchManager#SUGGEST_URI_PATH_SHORTCUT} |
| (instead of {@link android.app.SearchManager#SUGGEST_URI_PATH_QUERY}).</p> |
| <p>The {@link android.database.Cursor} you return should contain one suggestion using the |
| same columns as the original suggestion, or be empty, indicating that the shortcut is no |
| longer valid (in which case, the suggestion disappears and the shortcut is removed).</p> |
| <p>If a suggestion refers to data that could take longer to refresh, such as a network-based |
| refresh, you can also add the {@link |
| android.app.SearchManager#SUGGEST_COLUMN_SPINNER_WHILE_REFRESHING} column to your suggestions |
| table with a value |
| of "true" in order to show a progress spinner for the right hand icon until the refresh is complete. |
| Any value other than "true" does not show the progress spinner.</p> |
| </li> |
| |
| <li>Prevent the suggestion from being copied into a shortcut at all. |
| <p>Provide a value of {@link android.app.SearchManager#SUGGEST_NEVER_MAKE_SHORTCUT} in the |
| {@link android.app.SearchManager#SUGGEST_COLUMN_SHORTCUT_ID} column. In |
| this case, the suggestion is never copied into a shortcut. This should only be necessary if you |
| absolutely do not want the previously copied suggestion to appear. (Recall that if you |
| provide a normal value for the column, then the suggestion shortcut appears only until the |
| refresh query returns.)</p></li> |
| <li>Allow the default shortcut behavior to apply. |
| <p>Leave the {@link android.app.SearchManager#SUGGEST_COLUMN_SHORTCUT_ID} empty for each |
| suggestion that will not change and can be saved as a shortcut.</p></li> |
| </ol> |
| |
| <p>If none of your suggestions ever change, then you do not need the |
| {@link android.app.SearchManager#SUGGEST_COLUMN_SHORTCUT_ID} column at all.</p> |
| |
| <p class="note"><strong>Note</strong>: Quick Search Box ultimately decides whether or not to create |
| a shortcut for a suggestion, considering these values as a strong request from your |
| application—there is no guarantee that the behavior you have requested for your suggestion |
| shortcuts will be honored.</p> |
| |
| |
| <h3 id="AboutRanking">About Quick Search Box suggestion ranking</h3> |
| |
| <p>Once you make your application's search suggestions available to Quick Search Box, the Quick |
| Search Box ranking determines how the suggestions are surfaced to the user for a particular query. |
| This might depend on how many other apps have results for that query, and how often the user has |
| selected your results compared to those from other apps. There is no guarantee about how your |
| suggestions are ranked, or whether your app's suggestions show at all for a given query. In |
| general, you can expect that providing quality results increases the likelihood that your app's |
| suggestions are provided in a prominent position and apps that provide low quality suggestions |
| are more likely to be ranked lower or not displayed.</p> |
| |
| <div class="special"> |
| <p>See the <a href="{@docRoot}resources/samples/SearchableDictionary/index.html">Searchable |
| Dictionary sample app</a> for a complete demonstration of custom search suggestions.</p> |
| </div> |
| |