diff options
Diffstat (limited to 'docs/html')
-rw-r--r-- | docs/html/guide/guide_toc.cs | 14 | ||||
-rw-r--r-- | docs/html/guide/topics/search/adding-custom-suggestions.jd | 699 | ||||
-rw-r--r-- | docs/html/guide/topics/search/adding-recent-query-suggestions.jd | 225 | ||||
-rw-r--r-- | docs/html/guide/topics/search/index.jd | 111 | ||||
-rw-r--r-- | docs/html/guide/topics/search/search-dialog.jd | 542 | ||||
-rw-r--r-- | docs/html/guide/topics/search/searchable-config.jd | 356 | ||||
-rw-r--r-- | docs/html/images/search/search-suggest-custom.png | bin | 0 -> 46670 bytes | |||
-rw-r--r-- | docs/html/images/search/search-suggest-recent-queries.png | bin | 0 -> 47170 bytes | |||
-rw-r--r-- | docs/html/images/search/search-ui.png | bin | 0 -> 51727 bytes | |||
-rw-r--r-- | docs/html/shareables/search_icons.zip | bin | 0 -> 9753 bytes |
10 files changed, 1946 insertions, 1 deletions
diff --git a/docs/html/guide/guide_toc.cs b/docs/html/guide/guide_toc.cs index 13d752a..dce78c4 100644 --- a/docs/html/guide/guide_toc.cs +++ b/docs/html/guide/guide_toc.cs @@ -143,6 +143,7 @@ <li><a href="<?cs var:toroot ?>guide/topics/manifest/intent-filter-element.html"><intent-filter></a></li> <li><a href="<?cs var:toroot ?>guide/topics/manifest/manifest-element.html"><manifest></a></li> <li><a href="<?cs var:toroot ?>guide/topics/manifest/meta-data-element.html"><meta-data></a></li> + <li><a href="<?cs var:toroot ?>guide/topics/manifest/path-permission-element.html"><path-permission></a></li> <li><a href="<?cs var:toroot ?>guide/topics/manifest/permission-element.html"><permission></a></li> <li><a href="<?cs var:toroot ?>guide/topics/manifest/permission-group-element.html"><permission-group></a></li> <li><a href="<?cs var:toroot ?>guide/topics/manifest/permission-tree-element.html"><permission-tree></a></li> @@ -198,7 +199,18 @@ </a></li> <li><a href="<?cs var:toroot?>guide/topics/wireless/bluetooth.html"> <span class="en">Bluetooth</span> - </a> <span class="new">new!</span></li> + </a></li> + <li class="toggle-list"> + <div><a href="<?cs var:toroot?>guide/topics/search/index.html"> + <span class="en">Search</span> + </a> <span class="new">new!</span></div> + <ul> + <li><a href="<?cs var:toroot?>guide/topics/search/search-dialog.html">Using the Android Search Dialog</a></li> + <li><a href="<?cs var:toroot?>guide/topics/search/adding-recent-query-suggestions.html">Adding Recent Query Suggestions</a></li> + <li><a href="<?cs var:toroot?>guide/topics/search/adding-custom-suggestions.html">Adding Custom Suggestions</a></li> + <li><a href="<?cs var:toroot?>guide/topics/search/searchable-config.html">Searchable Configuration</a></li> + </ul> + </li> </ul> </li> diff --git a/docs/html/guide/topics/search/adding-custom-suggestions.jd b/docs/html/guide/topics/search/adding-custom-suggestions.jd new file mode 100644 index 0000000..9ea4c8b --- /dev/null +++ b/docs/html/guide/topics/search/adding-custom-suggestions.jd @@ -0,0 +1,699 @@ +page.title=Adding Custom Suggestions +parent.title=Search +parent.link=index.html +@jd:body + +<div id="qv-wrapper"> +<div id="qv"> +<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>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>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> +<li><a href="{@docRoot}resources/samples/SearchableDictionary/index.html">Searchable +Dictionary sample app</a></li> +</ol> +</div> +</div> + +<p>The Android search framework provides the ability for your application to +provide suggestions while the user types into the Android search dialog. In this guide, you'll learn +how to create custom suggestions. These are suggestions based on custom data provided by 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. Once you provide +custom suggestions, you then make them available to the system-wide Quick Search Box, providing +access to your content from outside your application.</p> + +<p>Before you begin, you need to have implemented the Android search dialog for searches in your +application. If you haven't done this, see <a href="search-dialog.html">Using the Android Search +Dialog</a>.</p> + + +<h2 id="TheBasics">The Basics</h2> + +<img src="{@docRoot}images/search/search-suggest-custom.png" alt="" height="417" +style="float:right;clear:right;" /> + +<p>When the user selects a custom suggestions, the Search Manager will send a customized Intent to +your searchable Activity. Whereas a normal search query will send 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 action), and also include additional 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, you need to do the following:</p> + +<ul> + <li>Implement a basic searchable Activity, as described in <a +href="search-dialog.html">Using the Android Search Dialog</a>.</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> + <li>Modify the searchable configuration with information about the content provider.</li> +</ul> + +<p>Just like the Search Manager handles the rendering of the search dialog, it will also do the work +to display all search suggestions below the search dialog. All you need to do is provide a source +from which the suggestions can be retrieved.</p> + +<p class="note"><strong>Note:</strong> If you're not familiar with creating Content +Providers, please read the <a href="{@docRoot}guide/topics/providers/content-providers.html">Content +Providers</a> developer guide before you continue.</p> + +<p>When the Search Manager identifies that your Activity is searchable and also provides search +suggestions, the following procedure will take place as soon as the user types into the Android +search box:</p> + +<ul> + <li>The Search Manager takes the search query text (whatever has been typed so far) and performs a +query to the content provider that manages your suggestions.</li> + <li>Your content provider then returns a {@link android.database.Cursor} that points to all +suggestions that are relevant to the search query text.</li> + <li>The Search Manager then displays the list of suggestions provided by the Cursor (as +demonstrated in the screenshot to the right).</li> +</ul> + +<p>At this point, the following may 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" + android:searchSuggestAuthority="my.package.MyCustomSuggestionProvider" > +</searchable> +</pre> + +<p>You may require 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 relevant sections below.</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 Search Manager +understands.</p> + +<p>When the user starts typing into the search dialog, the Search Manager will query 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 determine to be good suggestions.</p> + +<p>The following two sections describe how the Search Manager will send requests to your Content +Provider and how you can handle them, and define the columns that the Search Manager understands and +expects to be provided in the {@link android.database.Cursor} returned with each query.</p> + + +<h3 id="HandlingSuggestionQuery">Handling the suggestion query</h3> + +<p>When the Search Manager makes a request for suggestions from your content provider, it will call +{@link android.content.ContentProvider#query(Uri,String[],String,String[],String)}. You must +implement this method in your content provider so that it will search your suggestions and return a +Cursor that contains the suggestions you deem relevant.</p> + +<p>Here's a summary of the parameters that the Search Manager will pass 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>This will always be 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 Search Manager 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 will be encoded using URI encoding rules, so you may need to decode +it.</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>Note that {@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>This is always null</dd> + + <dt><code>selection</code></dt> + <dd>This is 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 this below.</dd> + + <dt><code>selectionArgs</code></dt> + <dd>This 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 this below.</dd> + + <dt><code>sortOrder</code></dt> + <dd>This is always null</dd> +</dl> + +<p>As you may have realized, there are two ways by which the Search Manager can send you the search +query text. The default manner is for the query text to be included as the last path of the content +URI that is 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 will instead be passed as the first +element of the {@code selectionArgs} string array. Both options are summarized below.</p> + + +<h4>Get the query in the Uri</h4> + +<p>By default, the query will be 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 will return the last segment of the Uri, which is the query text entered in the search +dialog.</p> + + + +<h4>Get the query in the selection arguments</h4> + +<p>Instead of using the URI, you may 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 values. In this case, you can +add the {@code android:searchSuggestSelection} attribute to your searchable configuration with your +SQLite selection string. In this selection string, you can include a question mark ("?") as +a placeholder for the actual search query. This selection string will be delivered as the +{@code selection} string parameter, and the query entered into the search dialog will be delivered +as the first element in the {@code selectionArgs} string array parameter.</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="my.package.MyCustomSuggestionProvider" + android:searchSuggestIntentAction="android.Intent.action.VIEW" + android:searchSuggestSelection="word MATCH ?"> +</searchable> +</pre> + +<p>When you then receive the {@code selection} and {@code selectionArgs} parameters in your {@link +android.content.ContentProvider#query(Uri,String[],String,String[],String) ContentProvider.query()} +method, they will carry the selection ("word MATCH ?") and the query text, respectively. When +these are passed to an SQLite {@link +android.database.sqlite.SQLiteDatabase#query(String,String[],String,String[],String,String, +String) query} method, they will be synthesized together (replacing the question mark with the query +text, wrapped in single-quotes). Note that if you chose this method and need to add any wildcards to +your query text, you must do so by appending (and/or prefixing) them to the {@code selectionArgs} +parameter, because this is the value that will be wrapped in quotes and inserted in place of the +question mark.</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 will trigger 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 on the fly</h2> +<p>If your search suggestions are not stored in a table format using the columns required by the +Search Manager, then you can search your suggestion data for matches and then format them +into the necessary table on the fly. 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 Search Manager with a {@link android.database.Cursor}, the +Search Manager expects there to be 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}. There are several columns that the Search +Manager will understand, but only two are required:</p> + +<dl> + <dt>{@link android.provider.BaseColumns#_ID}</dt> + <dd>This is the unique row ID for each suggestion. The search dialog requires this in order +to present the suggestions in a ListView.</dd> + <dt>{@link android.app.SearchManager#SUGGEST_COLUMN_TEXT_1}</dt> + <dd>This is the line of text that will be presented to the user as a suggestion.</dd> +</dl> + +<p>The following columns are all optional (and most will be discussed further in the following +sections, so you may want to skip this list for now):</p> + +<dl> + <dt>{@link android.app.SearchManager#SUGGEST_COLUMN_TEXT_2}</dt> + <dd>If your Cursor includes this column, then all suggestions will be provided in a two-line +format. The data in this column will be 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>If your Cursor includes this column, then all suggestions will be provided in an +icon-plus-text format with the icon on the left side. This value should be a reference to the +icon. It can be null or zero to indicate no icon in this row.</dd> + <dt>{@link android.app.SearchManager#SUGGEST_COLUMN_ICON_2}</dt> + <dd>If your Cursor includes this column, then all suggestions will be provided in an +icon-plus-text format with the icon on the right side. This value should be a reference to the +icon. It can be null or zero to indicate no icon in this row.</dd> + <dt>{@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_ACTION}</dt> + <dd>If this column exists and this element exists at the given row, this is the action that will +be used when forming the suggestion's Intent . If the element is not provided, the action will be +taken from the {@code android:searchSuggestIntentAction} field in your searchable configuration. At +least one of these +must be present for the suggestion to generate an Intent. Note: 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 from the Cursor .</dd> + <dt>{@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA}</dt> + <dd>If this column exists and this element exists at the given row, this is the data that will be +used when forming the suggestion's Intent. If the element is not provided, the data will be taken +from the {@code android:searchSuggestIntentData} field in your searchable configuration. If neither +source is provided, +the Intent's data field will be null. Note: 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 from the Cursor . +</dd> + <dt>{@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA_ID}</dt> + <dd>If this column exists and this element exists at the given row, then "/" and this value will +be 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>If this column exists and this element exists at a given row, this is the <em>extra</em> data +that will be used when forming the suggestion's Intent. If not provided, the Intent's extra data +field will be +null. This column allows suggestions to provide additional arbitrary data which will be +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 will be +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 is used to indicate +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 will be stored as a shortcut and never +refreshed. If set to {@link android.app.SearchManager#SUGGEST_NEVER_MAKE_SHORTCUT}, the result will +not be stored as a shortcut. +Otherwise, the shortcut id will be used to check back for 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 is used to specify 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>Again, most of these columns will be discussed in the relevant sections below, so don't worry if +they don't make sense to you now.</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 (instead of performing a search), the Search Manager will send +a custom {@link android.content.Intent} to your searchable Activity. You must define both the +<em>action</em> and <em>data</em> 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 whatever you want and can even be different for each +suggestion.</p> + +<p>To declare an Intent action that will be the same for all suggestions, define the action in +the {@code android:searchSuggestIntentAction} attribute of 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" + android:searchSuggestAuthority="my.package.MySuggestionProvider" + android:searchSuggestIntentAction="android.Intent.action.VIEW" > +</searchable> +</pre> + +<p>If you want to declare an Intent action that's unique for each suggestion, 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> + +<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 will be 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 will receive 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 suggestions was selected. Specifically, +the data should be something unique for each suggestion, such as the row ID for the suggestion in +your suggestions 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>There are two ways to define the data that is included with the Intent:</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.</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.</li> +</ol> + +<p>The first option is straight-forward. Simply 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 will be attached to the Intent exactly as it +is found 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} is used to +create a projection map of column names to aliases.</p> + +<p>The second option is to fragment your data URI into the common piece and the unique piece. +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="my.package.MySuggestionProvider" + android:searchSuggestIntentAction="android.Intent.action.VIEW" + android:searchSuggestIntentData="content://my.package/datatable" > +</searchable> +</pre> + +<p>Now 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 Search Manager will take +the string from {@code android:searchSuggestIntentData}, append a slash ("/") and then add 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> + +<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 will be 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 your search dialog provides custom search suggestions with custom formatted Intents, you +need your searchable Activity to handle these Intents as they are delivered once the user selects a +suggestion. (This is, of course, in addition to handling the {@link +android.content.Intent#ACTION_SEARCH} Intent, which your searchable Activity already does.) +Accepting the new Intent is rather self-explanatory, so we'll skip straight to an example:</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 my suggestions all use ACTION_VIEW) + Uri data = intent.getData()); + showResult(rowId); +} +</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 a local +method that will query the content provider for the item specified by the URI and show it.</p> + + + +<h2 id="RewritingQueryText">Rewriting the query text</h2> + +<p>If the user navigates through the suggestions list using the device directional controls, the +text in the search dialog won't change, 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 currently selected suggestion. 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 will be 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 will be used to rewrite the +query text. Note that 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 will be 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 your application is configured 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 will be required is if your content provider for +custom suggestions requires a permission for read access. 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="my.package.authority" + 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>Content providers that enforce no permissions are already available to the search +infrastructure.</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="my.package.MySuggestionProvider" + android:searchSuggestIntentAction="android.Intent.action.VIEW" + android:includeInGlobalSearch="true" + android:searchSettingsDescription="@string/search_description" > +</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 will be provided. This attribute should always be included +when {@code android:includeInGlobalSearch} is "true".</p> + +<p>Remember that the user must visit this settings menu to enable search suggestions for your +application before your search suggestions will appear in Quick Search Box. As such, if search is an +important aspect of your application, then you may want to consider a way to message this to your +users — perhaps with a note the first time they launch the app about 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 may be automatically made into shortcuts. +These are suggestions that the Search Manager 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 may change 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 the following ways:</p> + +<ol type="a"> + <li>Have Quick Search Box re-query your content provider for a fresh version of the shortcutted +suggestion. + <p>Provide a value in the {@link android.app.SearchManager#SUGGEST_COLUMN_SHORTCUT_ID} column +and the suggestion will be +re-queried for a fresh version of the suggestion each time the shortcut is displayed. The shortcut +will be quickly displayed with whatever data was most recently available until the refresh query +returns, after which the suggestion will be dynamically refreshed with the new information. The +refresh query will be 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}). The 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 will disappear and the shortcut will be removed).</p> + <p>If a suggestion refers to data that could take longer to refresh, such as a network based +refresh, you may 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" will 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 will never be copied into a shortcut. This should only be necessary if you +absolutely do not want the previously copied suggestion to appear at all. (Recall that if you +provide a normal value for the column then the suggestion shortcut will appear only until the +refresh query returns.)</p></li> + <li>Allow the default shortcut behavior to apply. + <p>Simply 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>Of course, if none of your suggestions will 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 will ultimately decide whether to shortcut +your app's suggestions, considering these values as a strong request from your application.</p> + + +<h3 id="AboutRanking">About Quick Search Box suggestion ranking</h3> + +<p>Once your application's search results are made available to Quick Search Box, how they surface +to the user for a particular query will be determined as appropriate by Quick Search Box ranking. +This may depend on how many other apps have results for that query, and how often the user has +selected on your results compared to those of the other apps. There is no guarantee about how +ranking will occur, or whether your app's suggestions will show at all for a given query. In +general, you can expect that providing quality results will increase the likelihood that your app's +suggestions are provided in a prominent position, and apps that provide lower quality suggestions +will be more likely to be ranked lower and/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> + diff --git a/docs/html/guide/topics/search/adding-recent-query-suggestions.jd b/docs/html/guide/topics/search/adding-recent-query-suggestions.jd new file mode 100644 index 0000000..37e0e82 --- /dev/null +++ b/docs/html/guide/topics/search/adding-recent-query-suggestions.jd @@ -0,0 +1,225 @@ +page.title=Adding Recent Query Suggestions +parent.title=Search +parent.link=index.html +@jd:body + +<div id="qv-wrapper"> +<div id="qv"> +<h2>Key classes</h2> +<ol> +<li>{@link android.provider.SearchRecentSuggestions}</li> +<li>{@link android.content.SearchRecentSuggestionsProvider}</li> +</ol> +<h2>In this document</h2> +<ol> +<li><a href="#TheBasics">The Basics</a></li> +<li><a href="#RecentQuerySearchableConfiguration">Modifying the searchable +configuration</a></li> +<li><a href="#RecentQueryContentProvider">Creating a Content Provider</a></li> +<li><a href="#SavingQueries">Saving queries</a></li> +<li><a href="#ClearingSuggestionData">Clearing the suggestion data</a></li> +</ol> +<h2>See also</h2> +<ol> +<li><a href="searchable-config.html">Searchable Configuration</a></li> +</ol> +</div> +</div> + +<p>The Android search framework provides the ability for your application to +provide suggestions while the user types into the Android search dialog. In this guide, you'll learn +how to create recent query suggestions. These are suggestions based +on queries previously entered by the user. So, if the user previously searched for "puppies" then it +will appear as a suggestion as they begin typing the same string of text. The screenshot below +shows an example of recent query suggestions.</p> + +<p>Before you begin, you need to have implemented the Android search dialog for searches in your +application. If you haven't done this, see <a href="search-dialog.html">Using the Android Search +Dialog</a>.</p> + + +<h2 id="TheBasics">The Basics</h2> + +<img src="{@docRoot}images/search/search-suggest-recent-queries.png" alt="" height="417" +style="float:right;clear:right;" /> + +<p>Recent query suggestions are simply saved searches. When the user selects one of +the suggestions, your searchable Activity will receive a normal {@link +android.content.Intent#ACTION_SEARCH} Intent with the suggestion as the search query, which your +searchable Activity will already handle.</p> + +<p>To provide recent queries suggestions, you need to:</p> + +<ul> + <li>Implement a basic searchable Activity, as documented in <a +href="{@docRoot}guide/topics/search/search-dialog.html">Using the Android Search Dialog</a>.</li> + <li>Create a content provider that extends {@link +android.content.SearchRecentSuggestionsProvider} and declare it in your application manifest.</li> + <li>Modify the searchable configuration with information about the content provider.</li> + <li>Save queries to your content provider each time a search is made.</li> +</ul> + +<p>Just like the Search Manager handles the rendering of the search dialog, it will also do the work +to display all search suggestions below the search dialog. All you need to do is provide a source +from which the suggestions can be retrieved.</p> + +<p>When the Search Manager identifies that your Activity is searchable and also provides search +suggestions, the following procedure will take place as soon as the user types into the Android +search box:</p> + +<ul> + <li>The Search Manager takes the search query text (whatever has been typed so far) and performs a +query to the content provider that manages your suggestions.</li> + <li>Your content provider then returns a {@link android.database.Cursor} that points to all +suggestions that are relevant to the search query text.</li> + <li>The Search Manager then displays the list of suggestions provided by the Cursor (as +demonstrated in the screenshot to the right).</li> +</ul> + +<p>At this point, the following may 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, a normal +{@link android.content.Intent#ACTION_SEARCH} Intent is triggered, using the suggested text as the +query.</li> +</ul> + +<p>As you'll soon discover, the {@link android.content.SearchRecentSuggestionsProvider} class that +you'll extend for your content provider will automatically do the work described above, so there's +actually very little code to write.</p> + + +<h2 id="RecentQuerySearchableConfiguration">Modifying the searchable configuration</h2> + +<p>First, you need to add the {@code android:searchSuggestAuthority} and +{@code android:searchSuggestSelection} attributes 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" + android:searchSuggestAuthority="my.package.MySuggestionProvider" + android:searchSuggestSelection=" ?" > +</searchable> +</pre> + +<p>The value for {@code android:searchSuggestAuthority} should be a fully-qualified name for +your content provider: your application package name followed by the name of your content provider. +This string must match the authority used in the content provider (discussed in the next section). +</p> + +<p>The value for {@code android:searchSuggestSelection} must be a single question-mark, preceded by +a space (" ?"), which is simply a placeholder for the SQLite selection argument (which will be +automatically replaced by the query text entered by the user).</p> + + +<h2 id="RecentQueryContentProvider">Creating a Content Provider</h2> + +<p>The content provider that you need for recent query suggestions must be an implementation +of {@link android.content.SearchRecentSuggestionsProvider}. This class does practically everything +for you. All you have to do is write a class constructor that executes one line of code.</p> + +<p>For example, here's a complete implementation of a content provider for recent query +suggestions:</p> + +<pre> +public class MySuggestionProvider extends SearchRecentSuggestionsProvider { + public final static String AUTHORITY = "my.package.MySuggestionProvider"; + public final static int MODE = DATABASE_MODE_QUERIES; + + public MySuggestionProvider() { + setupSuggestions(AUTHORITY, MODE); + } +} +</pre> + +<p>The call to {@link android.content.SearchRecentSuggestionsProvider#setupSuggestions(String,int)} +passes the name of the search authority (matching the one in the searchable configuration) and a +database mode. The database mode must include {@link +android.content.SearchRecentSuggestionsProvider#DATABASE_MODE_QUERIES} and can optionally include +{@link +android.content.SearchRecentSuggestionsProvider#DATABASE_MODE_2LINES}, which will add another column +to the suggestions table that allows you to provide a second line of text with each suggestion. For +example:</p> +<pre> +public final static int MODE = DATABASE_MODE_QUERIES | DATABASE_MODE_2LINES; +</pre> + +<p>In the following section, you'll see how to save both lines of text.</p> + +<p>Now simply declare the content provider in your application manifest with the same authority +string used in the class (and in the searchable configuration). For example:</p> + +<pre> +<application> + <provider android:name=".MySuggestionProvider" + android:authorities="my.package.authority" /> + ... +</application> +</pre> + + +<h2 id="SavingQueries">Saving queries</h2> + +<p>In order to populate your collection of recent queries, you need to add each query +received by your searchable Activity to the content provider you've just built. To do this, create +an instance of {@link +android.provider.SearchRecentSuggestions} and call {@link +android.provider.SearchRecentSuggestions#saveRecentQuery(String,String)} each time your searchable +Activity receives a query. For example, here's how you can save the query during your +Activity's {@link android.app.Activity#onCreate(Bundle) onCreate()} method:</p> + +<pre> +@Override +public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.main); + + Intent Intent = getIntent(); + + if (Intent.ACTION_SEARCH.equals(Intent .getAction())) { + String query = Intent .getStringExtra(SearchManager.QUERY); + SearchRecentSuggestions suggestions = new SearchRecentSuggestions(this, + MySuggestionProvider.AUTHORITY, MySuggestionProvider.MODE); + suggestions.saveRecentQuery(query, null); + } +} +</pre> + +<p>Notice that the {@link android.content.SearchRecentSuggestionsProvider} constructor requires the +same authority and database mode declared by your content provider.</p> + +<p>The {@link android.provider.SearchRecentSuggestions#saveRecentQuery(String,String)} method takes +the search query string as the first parameter and, optionally, a second string to include as the +second line of the suggestion. The second parameter is only used if you've enabled two-line mode +for the search suggestions with {@link +android.content.SearchRecentSuggestionsProvider#DATABASE_MODE_2LINES}. If you have enabled +two-line mode, then the query text will be matched against this second line as well.</p> + +<p>That's all that's needed to build a recent queries suggestion provider. However, there's one +other important thing to do: provide the ability for the user to clear this search history.</p> + + +<h2 id="ClearingSuggestionData">Clearing the suggestion data</h2> + +<p>To protect the user's privacy, you should always provide a way for the user to clear the recent +query suggestions. To clear the recent queries, simply call {@link +android.provider.SearchRecentSuggestions#clearHistory()}. For example:</p> + +<pre> +SearchRecentSuggestions suggestions = new SearchRecentSuggestions(this, + HelloSuggestionProvider.AUTHORITY, HelloSuggestionProvider.MODE); +suggestions.clearHistory(); +</pre> + +<p>Simply execute this from your choice of a "Clear Search History" menu item, +preference item, or button. You should also provide a confirmation dialog when this is pressed, to +verify that the user wants to delete their search history.</p> + diff --git a/docs/html/guide/topics/search/index.jd b/docs/html/guide/topics/search/index.jd new file mode 100644 index 0000000..b2252bb --- /dev/null +++ b/docs/html/guide/topics/search/index.jd @@ -0,0 +1,111 @@ +page.title=Search +@jd:body + +<div id="qv-wrapper"> +<div id="qv"> +<h2>Topics</h2> +<ol> +<li><a href="search-dialog.html">Using the Android Search Dialog</a></li> +<li><a href="adding-recent-query-suggestions.html">Adding Recent Query Suggestions</a></li> +<li><a href="adding-custom-suggestions.html">Adding Custom Suggestions</a></li> +</ol> +<h2>Reference</h2> +<ol> +<li><a href="searchable-config.html">Searchable Configuration</a></li> +</ol> +<h2>See also</h2> +<ol> +<li><a href="{@docRoot}resources/samples/SearchableDictionary/index.html">Searchable +Dictionary sample app</a></li> +</ol> +</div> +</div> + + +<p>The ability to search is considered to be a core user feature on Android. The user should be able +to search any data that is available to them, whether the content is located on the device or the +Internet. This experience should be seamless and consistent across the entire +system, which is why Android provides a simple search framework to help you provide users with +a familiar search dialog and a great search experience.</p> + +<img src="{@docRoot}images/search/search-suggest-custom.png" alt="" height="417" +style="float:right;clear:right;" /> + +<p>Android's search framework provides a user interface in which the user can perform a search and +an interaction layer that communicates with your application. This way, you don't have to build +a search box that the user must find in order to begin a search. Instead, +a custom search dialog will appear at the top of the screen at the user's command. +The search framework will manage the search dialog and when the user executes their search, the +search framework will pass the query text to your application so that your application can begin a +search. The screenshot to the right shows an example of the search dialog (using +search suggestions).</p> + +<p>Once your application is set up to use the search dialog, you can:</p> + +<ul> +<li>Customize some of the search dialog characteristics</li> +<li>Enable voice search</li> +<li>Provide search suggestions based on recent user queries</li> +<li>Provide search suggestions that match actual results in your application data</li> +<li>Offer your application's search suggestions in the system-wide Quick Search Box</li> +</ul> + +<p>The following documents will teach you how to use the search dialog in +your application:</p> + +<dl> + <dt><strong><a href="search-dialog.html">Using the Android Search Dialog</a></strong></dt> + <dd>How to set up your application to use the search dialog for searches. </dd> + <dt><strong><a href="adding-recent-query-suggestions.html">Adding Recent Query +Suggestions</a></strong></dt> + <dd>How to show suggestions based on queries previously used in the search dialog.</dd> + <dt><strong><a href="adding-custom-suggestions.html">Adding Custom Suggestions</a></strong></dt> + <dd>How to show suggestions based on custom data from your application and offer your suggestions +in the system-wide Quick Search Box.</dd> +</dl> + +<p>Also, the <strong><a href="searchable-config.html">Searchable Configuration</a></strong> document +provides a reference for the searchable configuration file (though the above +documents also discuss the configuration file in terms of specific behaviors).</p> + +<p class="note"><strong>Note</strong>: The search framework does <em>not</em> provide APIs to +perform searches on your data. Performing actual searches is a task that you must accomplish +using APIs appropriate for your data, such as those in {@link android.database.sqlite} +if your data is in an SQLite database.</p> + + +<h2>Protecting User Privacy</h2> + +<p>When you implement search in your application, you should take steps to protect the user's +privacy whenever possible. Many users consider their activities on the phone, including searches, to +be private information. To protect the user's privacy, you should abide by the following +principles:</p> + +<ul> +<li><strong>Don't send personal information to servers, and if you do, don't log it.</strong> +<p>"Personal information" is information that can personally identify your users, such as their +name, email address, billing information, or other data which can be reasonably linked to such +information. If +your application implements search with the assistance of a server, try to avoid sending personal +information along with the search queries. For example, if you are searching for businesses near a +zip code, +you don't need to send the user ID as well — send only the zip code to the server. If you must +send the personal information, you should take steps to avoid logging it. If you must log it, you +should protect that data very carefully and erase it as soon as possible.</p> +</li> +<li><strong>Provide the user with a way to clear their search history.</strong> +<p>The search framework helps your application provide context-specific suggestions while they type. +Sometimes these +suggestions are based on previous searches, or other actions taken by the user in an earlier +session. A user may not wish for previous searches to be revealed to other users, for instance if +they share their phone with a friend. If your application provides suggestions that can reveal +previous activities, you should implement a "Clear History" menu item, preference, or button. If you +are +using {@link android.provider.SearchRecentSuggestions}, you can simply call its {@link +android.provider.SearchRecentSuggestions#clearHistory()} method. If you are implementing custom +suggestions, you'll need to provide a +similar "clear history" method in your provider that can be invoked by the user.</p> +</li> +</ul> + + diff --git a/docs/html/guide/topics/search/search-dialog.jd b/docs/html/guide/topics/search/search-dialog.jd new file mode 100644 index 0000000..718668c --- /dev/null +++ b/docs/html/guide/topics/search/search-dialog.jd @@ -0,0 +1,542 @@ +page.title=Using the Android Search Dialog +parent.title=Search +parent.link=index.html +@jd:body + +<div id="qv-wrapper"> +<div id="qv"> +<h2>Key classes</h2> +<ol> +<li>{@link android.app.SearchManager}</li> +</ol> +<h2>In this document</h2> +<ol> +<li><a href="#TheBasics">The Basics</a></li> +<li><a href="#SearchableConfiguration">Creating a Searchable Configuration</a></li> +<li><a href="#SearchableActivity">Creating a Searchable Activity</a> + <ol> + <li><a href="#DeclaringSearchableActivity">Declaring a searchable Activity</a></li> + <li><a href="#PerformingSearch">Performing a search</a></li> + </ol> +</li> +<li><a href="#InvokingTheSearchDialog">Invoking the Search Dialog</a> + <ol> + <li><a href="#LifeCycle">The impact of the search dialog on your Activity life-cycle</a></li> + </ol> +</li> +<li><a href="#SearchContextData">Passing Search Context Data</a></li> +<li><a href="#VoiceSearch">Adding Voice Search</a></li> +</ol> +<h2>See also</h2> +<ol> +<li><a href="adding-recent-query-suggestions.html">Adding Recent Query Suggestions</a></li> +<li><a href="adding-custom-suggestions.html">Adding Custom Suggestions</a></li> +<li><a href="searchable-config.html">Searchable Configuration</a></li> +<li><a href="{@docRoot}resources/samples/SearchableDictionary/index.html">Searchable +Dictionary App</a></li> +</ol> +</div> +</div> + +<p>When you want to provide search in your application, the last thing you should have to worry +about is where to put your search box. By using the Android search framework, your application will +reveal a custom search dialog whenever the user requests it. At the +press of a dedicated search key or an API call from your application, the search dialog will +appear at the top of the screen and will automatically show your application icon. An example is +shown in the screenshot below.</p> + +<p>This guide will teach you how to set up your application to provide search in a custom search +dialog. In doing so, you will provide a standardized search experience and be able to add +features like voice search and search suggestions.</p> + + +<h2 id="TheBasics">The Basics</h2> + +<img src="{@docRoot}images/search/search-ui.png" alt="" height="417" +style="float:right;clear:right;" /> + +<p>The Android search framework will manage the search dialog on your behalf; you never need +to draw it or worry about where it is, and your current Activity will not be +interrupted. The {@link android.app.SearchManager} is the component that does this work for +you (hereafter, referred to as "the Search Manager"). It manages the life of the Android search +dialog and will send your application the search query when executed by the user.</p> + +<p>When the user executes a search, the Search Manager will use a specially-formed Intent to pass +the search query to the Activity that you've declared to handle searches. Essentially, all you +need is an Activity that receives this Intent, performs the search, and presents the results. +Specifically, what you need is the following:</p> + +<dl> + <dt>A searchable configuration</dt> + <dd>This is an XML file that configures the search dialog and includes settings for +features such as the hint text shown in text box and settings voice search and search +suggestion.</dd> + <dt>A searchable Activity</dt> + <dd>This is the {@link android.app.Activity} that receives the search query then +searches your data and displays the search results.</dd> + <dt>A mechanism by which the user can invoke search</dt> + <dd>By default, the device search key (if available) will invoke the search dialog once +you've configured a searchable Activity. However, you should always provide another means by +which the user can invoke a search, such as with a search button in the Options Menu or elsewhere in +the Activity UI, because not all devices provide a dedicated search key.</dd> +</dl> + + +<h2 id="SearchableConfiguration">Creating a Searchable Configuration</h2> + +<p>The searchable configuration is an XML file that defines several settings for the Android search +dialog in your application. This file is traditionally named {@code searchable.xml} and must be +saved in the {@code res/xml/} project directory.</p> + +<p>The file must consist of the {@code <searchable>} element as the root node and specify one +or more attributes that configure your search dialog. 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" > +</searchable> +</pre> + +<p>This is the minimum configuration required in order to provide the search dialog. The {@code +android:label} attribute is the only required attribute and points to a string resource, which +should normally be the same as the application. (Although it's required, this +label isn't actually shown to the user until you enable suggestions for Quick Search Box.)</p> + +<p>There are several other attributes accepted by the {@code <searchable>} element. Most of +which apply only when configuring features such as search suggestions and voice +search. However, we recommend that you always include the {@code android:hint} attribute, which +specifies a string resource for the text to display in the search dialog's text box before the user +enters their query—it provides important clues to the user about what they can search. </p> + +<p class="note"><strong>Tip:</strong> For consistency among other +Android applications, you should format the string for {@code android:hint} as "Search +<em><content-or-product></em>". For example, "Search songs and artists" or "Search +YouTube".</p> + +<p>Next, you'll hook this configuration into your application.</p> + + +<h2 id="SearchableActivity">Creating a Searchable Activity</h2> + +<p>When the user executes a search from the search dialog, the Search Manager will send +your searchable {@link android.app.Activity} the search query with the {@link +android.content.Intent#ACTION_SEARCH} {@link android.content.Intent}. Your searchable Activity will +then search your data and present the results.</p> + + +<h3 id="DeclaringSearchableActivity">Declaring a searchable Activity</h3> + +<p>If you don't have one already, create an {@link android.app.Activity} that will be used to +perform searches, then declare it to +accept the {@link android.content.Intent#ACTION_SEARCH} {@link android.content.Intent} and apply the +searchable configuration. To do so, you need to add an {@code +<intent-filter>} element and a {@code <meta-data>} element to the +appropriate {@code <activity>} element in your manifest file. For example:</p> + +<pre> +<application ... > + <activity android:name=".MySearchableActivity" > + <intent-filter> + <action android:name="android.intent.action.SEARCH" /> + </intent-filter> + <meta-data android:name="android.app.searchable" + android:resource="@xml/searchable"/> + </activity> + ... +</application> +</pre> + +<p>The {@code android:name} attribute in the {@code <meta-data>} element must be defined with +{@code "android.app.searchable"} and the {@code android:resource} attribute value must be a +reference to the searchable configuration file saved in {@code res/xml} (in this example, it +refers to the {@code res/xml/searchable.xml} file).</p> + +<p class="note">If you're wondering why the {@code +<intent-filter>} does not include a {@code <category>} with the {@code DEFAULT} +value, it's because the Intent that is delivered to this Activity when a search is executed will +explicitly define this Activity as the component for the Intent (which the Search Manager knows +from the searcahble meta-data declared for the Activity).</p> + +<p>Be aware that the search dialog will not be available from within every +Activity of your application, by default. Rather, the search dialog will be presented to +users only when they +invoke search from a searchable context of your application. A searchable context is any Activity +for which you have +declared searchable meta-data in the manifest file. For example, the searchable Activity itself +(declared in the manifest snippet above) is +a searchable context because it contains searchable meta-data that defines the +searchable configuration. Any other Activity in your application is not a searchable context, by +default, and thus, will not reveal the search dialog. You probably do want the +search dialog to be available from every Activity in your application, so this can be easily +fixed.</p> + +<p>If you want all of your activities to provide the search dialog, add another {@code +<meta-data>} element inside the {@code +<application>} element. Use this element to declare the existing searchable Activity as the +default searchable Activity. For example:</p> + +<pre> +<application ... > + <activity android:name=".MySearchableActivity" > + <intent-filter> + <action android:name="android.intent.action.SEARCH" /> + </intent-filter> + <meta-data android:name="android.app.searchable" + android:resource="@xml/searchable"/> + </activity> + <activity android:name=".AnotherActivity" ... > + </activity> + <!-- this one declares the searchable Activity for the whole app --> + <meta-data android:name="android.app.default_searchable" + android:value=".MySearchableActivity" /> + ... +</application> +</pre> + +<p>The {@code <meta-data>} element with the {@code android:name} attribute value of +{@code "android.app.default_searchable"} specifies a default searchable Activity for the context in +which it is placed (which, in this case, is the entire application). The searchable Activity to +use is specified with the {@code android:value} attribute. All other activities in the +application, such as {@code AnotherActivity}, are now considered a searchable context and can invoke +the search dialog. When a search is executed, {@code MySearchableActivity} will +be launched to handle the search query.</p> + +<p>Notice that this allows you to control which activities provide search at a more granular level. +To specify only an individual Activity as a searchable context, simply place the {@code +<meta-data>} with the {@code +"android.app.default_searchable"} name inside the respective {@code <activity>} +element (rather than inside the {@code <application>}). And, while it is uncommon, you can +even create more than one searchable Activity and provide each one in different contexts of your +application, either by declaring a different searchable Activity in each {@code <activity>} +element, or declaring a default searchable Activity for the entire application and then overriding +it with a different {@code <meta-data>} element inside certain activities.</p> + + +<h3 id="PerformingSearch">Performing a search</h3> + +<p>Once your Activity is declared searchable, performing the actual search involves three steps: +receiving the query, searching your data, and presenting the results.</p> + +<p>Traditionally, your search results should be presented in a {@link android.widget.ListView} +(assuming that our results are text-based), so +you may want your searchable Activity to extend {@link android.app.ListActivity}, which +provides easy access to {@link android.widget.ListView} APIs. (See the <a +href="{@docRoot}resources/tutorials/views/hello-listview.html">List View Tutorial</a> for a simple +{@link android.app.ListActivity} sample.)</p> + + +<h4 id="ReceivingTheQuery">Receiving the query</h4> + +<p>When a search is executed from the search dialog, your searchable Activity will be opened +with the {@link android.content.Intent#ACTION_SEARCH} {@link android.content.Intent}, which carries +the search query in the +{@link android.app.SearchManager#QUERY QUERY} extra. All you need to do is check for +this Intent and extract the string. For example, here's how you can get the query when your +Activity launches:</p> + +<pre> +@Override +public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.search); + + Intent intent = getIntent(); + + if (Intent.ACTION_SEARCH.equals(intent.getAction())) { + String query = intent.getStringExtra(SearchManager.QUERY); + doMySearch(query); + } +} +</pre> + +<p>The {@link android.app.SearchManager#QUERY QUERY} string is always included with +the {@link android.content.Intent#ACTION_SEARCH} Intent. In this example, the query is +retrieved and passed to a local {@code doMySearch()} method where the actual search operation +is done.</p> + + +<h4 id="SearchingYourData">Searching your data</h4> + +<p>The process of storing and searching your data is a process that's unique to your application. +There are many ways that you might do this and discussing all options is beyond the scope of +this document. This guide will not teach you how to store your data and search it; this +is something you must carefully consider in terms of your needs and your data. However, here are +some tips you may be able to apply:</p> + + <ul> + <li>If your data is stored in a SQLite database on the device, performing a full-text search +(using FTS3, rather than a LIKE query) can provide a more robust search across text data and can +produce results many, many times faster. See <a href="http://sqlite.org/fts3.html">sqlite.org</a> +for information about FTS3 and the {@link android.database.sqlite.SQLiteDatabase} class for +information about SQLite on Android. Also look at the <a +href="{@docRoot}resources/samples/SearchableDictionary/index.html">Searchable Dictionary</a> sample +application to see a complete SQLite implementation that performs searches with FTS3.</li> + <li>If your data is stored online, then the perceived search performance may be +inhibited by the user's data connection. You may want to display a spinning progress wheel until +your search returns. See {@link android.net} for a reference of network APIs and <a +href="guide/topics/ui/dialogs.html#ProgressDialog">Creating a Progress Dialog</a> to see how +you can display a progress wheel.</li> + </ul> + + +<div class="sidebox-wrapper"> +<div class="sidebox"> +<h2>About Adapters</h2> +<p>An Adapter will bind individual items from a set of data into individual {@link +android.view.View} objects. When the Adapter +is applied to a {@link android.widget.ListView}, the Views are injected as individual items of the +list. {@link +android.widget.Adapter} is simply an interface, so implementations such as {@link +android.widget.CursorAdapter} (for binding data from a {@link android.database.Cursor}) are needed. +If none of the existing implementations work for your data, then you should implement your own from +{@link android.widget.BaseAdapter}. Install the SDK Samples package for API Level 4 to see a +version of the Searchable Dictionary that creates a custom BaseAdapter.</p> +</div> +</div> + +<p>Regardless of where your data lives and how you search it, we recommend that you return search +results to your searchable Activity with an {@link android.widget.Adapter}. This way, you can easily +present all the search results in a {@link android.widget.ListView}. If your data comes from a +SQLite database query, then you can easily apply your results to a {@link android.widget.ListView} +using a {@link android.widget.CursorAdapter}. If your data comes in some other type of format, then +you can create an extension of the {@link android.widget.BaseAdapter}.</p> + +<h4 id="PresentingTheResults">Presenting the results</h4> + +<p>Presenting your search results is mostly a UI detail and not something covered by the search +framework APIs. However, a simple solution is to create your searchable Activity to extend {@link +android.app.ListActivity} and then call {@link +android.app.ListActivity#setListAdapter(ListAdapter)}, passing it an {@link +android.widget.Adapter} that is bound to your data. This will automatically project all the +results into the Activity {@link android.widget.ListView}.</p> + +<p>For more help presenting your results, see the {@link android.app.ListActivity} +documentation.</p> + +<p>Also see the <a +href="{@docRoot}resources/samples/SearchableDictionary/index.html">Searchable Dictionary</a> sample +application for an a complete demonstration of how to search an SQLite database and use an +{@link android.widget.Adapter} to provide resuls in a {@link android.widget.ListView}.</p> + + +<h2 id="InvokingTheSearchDialog">Invoking the Search Dialog</h2> + +<p>Once you have a searchable Activity in place, invoking the search dialog so the user can +submit a +query is easy. Many Android devices provide a dedicated search key and when it is pressed while the +user is within a searchable context of your application, the search dialog will be revealed. +However, +you should never assume that a search key is available on the user's device and should always +provide a search button in your UI that will invoke search.</p> + +<p>To invoke search from your Activity, simply call {@link +android.app.Activity#onSearchRequested()}.</p> + +<p>For example, you should provide a menu item in your <a +href="{@docRoot}guide/topics/ui/menus.html#options-menu">Options Menu</a> or a button in your UI to +invoke search with this method. For your convenience, this <a +href="{@docRoot}shareables/search_icons.zip">search_icons.zip</a> file includes icons for +medium and high density screens, which you can use for your menu item or button (low density +screens will automatically scale-down the hdpi image by one half). </p> + +<!-- ... maybe this should go into the Creating Menus document .... +<p>If you chose to provide a shortcut key for the menu item, using {@link +android.view.MenuItem#setAlphabeticShortcut(char)}, then SearchManager.MENU_KEY is the recommended +key character, representing the default search key.</p> +--> + +<p>You can also enable "type-to-search" functionality in your Activity by calling {@link +android.app.Activity#setDefaultKeyMode(int) setDefaultKeyMode}({@link +android.app.Activity#DEFAULT_KEYS_SEARCH_LOCAL}). When this is enabled and the user begins typing on +the keyboard, search will automatically be +invoked and the keystrokes will be inserted in the search dialog. Be sure to enable this mode +during your Activity {@link android.app.Activity#onCreate(Bundle) onCreate()} method.</p> + + +<h3 id="LifeCycle">The impact of the search dialog on your Activity life-cycle</h3> + +<p>The search dialog behaves like a {@link android.app.Dialog} that floats at the top of the +screen. It +does not cause any change in the Activity stack, so no life-cycle methods (such as {@link +android.app.Activity#onPause()}) will +be called. All that happens is your Activity loses input focus as it is given to the search dialog. +</p> + +<p>If you want to be notified when search is invoked, simply override the {@link +android.app.Activity#onSearchRequested()} method. When this is called, you can do any work you may +want to do when your Activity looses input focus (such as pause animations). But unless you are +<a href="#SearchContextData">Passing Search Context Data</a> (discussed above), you should always +call the super class implementation. For example:</p> + +<pre> +@Override +public boolean onSearchRequested() { + pauseSomeStuff(); + return super.onSearchRequested(); +} +</pre> + +<p>If the user cancels search by pressing the device Back key, the Activity in which search was +invoked will re-gain input focus. You can register to be notified when the search dialog is +closed with {@link android.app.SearchManager#setOnDismissListener(SearchManager.OnDismissListener)} +and/or {@link android.app.SearchManager#setOnCancelListener(SearchManager.OnCancelListener)}. You +should normally only need to register the {@link android.app.SearchManager.OnDismissListener +OnDismissListener}, because this is called every time that the search dialog is closed. The {@link +android.app.SearchManager.OnCancelListener OnCancelListener} only pertains to events in which the +user explicitly left the search dialog, so it is not called when a search is executed (in which +case, the search dialog naturally disappears).</p> + +<p>If the current Activity is not the searchable Activity, then the normal Activity life-cycle +events will be triggered once the user executes a search (the current Activity will receive {@link +android.app.Activity#onPause()} and so forth, as +described in <a href="{@docRoot}guide/topics/fundamentals.html#lcycles">Application +Fundamentals</a>). If, however, the current Activity is the searchable Activity, then one of two +things will happen:</p> + +<ul> + <li>By default, the searchable Activity will receive the {@link +android.content.Intent#ACTION_SEARCH} Intent with a call to {@link +android.app.Activity#onCreate(Bundle) onCreate()} and a new instance of the +Activity will be brought to the top of the stack. You'll now have two instances of your searchable +Activity in the Activity stack (so pressing the Back key will go back to the previous instance of +the searchable Activity, rather than exiting the searchable Activity).</li> + <li>On the other hand, if the Activity has set {@code android:launchMode} to "singleTop" then the +searchable Activity will receive the {@link android.content.Intent#ACTION_SEARCH} Intent with a call +to {@link android.app.Activity#onNewIntent(Intent)}, passing the new {@link +android.content.Intent#ACTION_SEARCH} Intent here. For example, here's how you might want to handle +this case: +<pre> +@Override +public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.search); + handleIntent(getIntent()); +} + +@Override +protected void onNewIntent(Intent intent) { + setIntent(intent); + handleIntent(intent); +} + +private void handleIntent(Intent intent) { + if (Intent.ACTION_SEARCH.equals(intent.getAction())) { + String query = intent.getStringExtra(SearchManager.QUERY); + doMySearch(query); + } +} +</pre> + +<p>Compared to the example code in the section about <a href="#PerfomingSearch">Performing a +Search</a>, all the code to handle the +search Intent has been moved outside the {@link android.app.Activity#onCreate(Bundle) +onCreate()} method so it can also be executed from {@link android.app.Activity#onNewIntent(Intent) +onNewIntent()}. +It's important to note that when {@link android.app.Activity#onNewIntent(Intent)} is +called, the Activity has not been restarted, so the {@link android.app.Activity#getIntent()} method +will still return the Intent that was first received with {@link +android.app.Activity#onCreate(Bundle) onCreate()}. This is why {@link +android.app.Activity#setIntent(Intent)} is called inside {@link +android.app.Activity#onNewIntent(Intent)} (just in case you call {@link +android.app.Activity#getIntent()} at a later time).</p> + +</li> +</ul> + +<p>This second scenario is normally ideal, because the chances are good that once a search is +completed, the user will perform additional searches and it's a bad experience if your application +piles multiple instances of the searchable Activity on the stack. So we recommend that you set your +searchable Activity to "singleTop" launch mode in the application manifest. For example:</p> + +<pre> +<activity android:name=".MySearchableActivity" + android:launchMode="singleTop" > + <intent-filter> + <action android:name="android.intent.action.SEARCH" /> + </intent-filter> + <meta-data android:name="android.app.searchable" + android:resource="@xml/searchable"/> + </activity> +</pre> + + +<h2 id="SearchContextData">Passing Search Context Data</h2> + +<p>In order to refine your search criteria, you may want to provide some additional +data to your searchable Activity when a search is executed. For instance, when you search your data, +you may want to filter results based on more than just the search query text. In a simple +case, you could just make your refinements inside the searchable Activity, for every search made. +If, however, your +search criteria may vary from one searchable context to another, then you can pass whatever data is +necessary to refine your search in the {@link android.app.SearchManager#APP_DATA} Bundle, which is +included in the {@link android.content.Intent#ACTION_SEARCH} Intent.</p> + +<p>To pass this kind of data to your searchable Activity, you need to override {@link +android.app.Activity#onSearchRequested()} method for the Activity in which search will be invoked. +For example:</p> + +<pre> +@Override +public boolean onSearchRequested() { + Bundle appData = new Bundle(); + appData.putBoolean(MySearchableActivity.JARGON, true); + startSearch(null, false, appData, false); + return true; + } +</pre> + +<p>Returning "true" indicates that you have successfully handled this callback event. Then in your +searchable Activity, you can extract this data from the {@link +android.app.SearchManager#APP_DATA} {@link android.os.Bundle} to refine the search. For example:</p> + +<pre> + Bundle appData = getIntent().getBundleExtra(SearchManager.APP_DATA); + if (appData != null) { + boolean jargon = appData.getBoolean(MySearchableActivity.JARGON); + } +</pre> + +<p class="caution"><strong>Note:</strong> You should never call the {@link +android.app.Activity#startSearch(String,boolean,Bundle,boolean) startSearch()} method from outside +the {@link android.app.Activity#onSearchRequested()} callback method. When you want to invoke the +search dialog, always call {@link android.app.Activity#onSearchRequested()} so that custom +implementations (such as the addition of {@code appData}, in the above example) can be accounted +for.</p> + + +<h2 id="VoiceSearch">Adding Voice Search</h2> + +<p>You can easily add voice search functionality to your search dialog by adding the {@code +android:voiceSearchMode} attribute to your searchable configuration. This will add a voice search +button in the search dialog that, when clicked, will launch a voice prompt. When the user +has finished speaking, the transcribed search query will be sent to your searchable +Activity.</p> + +<p>To enable voice search for your activity, add the {@code android:voiceSearchMode} +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/search_label" + android:hint="@string/search_hint" + android:voiceSearchMode="showVoiceSearchButton|launchRecognizer" > +</searchable> +</pre> + +<p>The value {@code showVoiceSearchButton} is required to enable voice +search, while the second value, {@code launchRecognizer}, specifies that the voice search button +should launch a recognizer that returns the transcribed text to the searchable Activity. This is +how most applications should declare this attribute.</p> + +<p>There are some additional attributes you can provide to specify the voice search behavior, such +as the language to be expected and the maximum number of results to return. See the <a +href="searchable-config.html">Searchable Configuration</a> for more information about the +available attributes.</p> + +<p class="note"><strong>Note:</strong> Carefully consider whether voice search is appropriate for +your application. All searches performed with the voice search button will be immediately sent to +your searchable Activity without a chance for the user to review the transcribed query. Be sure to +sufficiently test the voice recognition and ensure that it understands the types of queries that +the user will submit inside your application.</p> diff --git a/docs/html/guide/topics/search/searchable-config.jd b/docs/html/guide/topics/search/searchable-config.jd new file mode 100644 index 0000000..f3a5bb1 --- /dev/null +++ b/docs/html/guide/topics/search/searchable-config.jd @@ -0,0 +1,356 @@ +page.title=Searchable Configuration +parent.title=Search +parent.link=index.html +@jd:body + +<div id="qv-wrapper"> +<div id="qv"> +<h2>See also</h2> +<ol> + <li><a href="search-dialog.html">Using the Android Search Dialog</a></li> + <li><a href="adding-recent-query-suggestions.html">Adding Recent Query Suggestions</a></li> + <li><a href="adding-custom-suggestions.html">Adding Custom Suggestions</a></li> +</ol> +</div> +</div> + +<p>In order to utilize the Android search framework and provide a custom search dialog, your +application must provide a search +configuration in the form of an XML resource. This document describes the search configuration XML +in terms of its syntax and usage. For a more complete discussion about how to implement search +features for your application, see the companion documents about <a +href="index.html">Search</a>.</p> + +<dl class="xml"> + +<dt>file location:</dt> +<dd><code>res/xml/<em>filename</em>.xml</code><br/> +The filename will be used as the resource ID.</dd> + +<dt>syntax:</dt> +<dd> +<pre class="stx"> +<?xml version="1.0" encoding="utf-8"?> +<<a href="#searchable-element">searchable</a> xmlns:android="http://schemas.android.com/apk/res/android" + android:label="<em>string resource</em>" + android:hint="<em>string resource</em>" + android:searchMode=["queryRewriteFromData" | "queryRewriteFromText"] + android:searchButtonText="<em>string resource</em>" + android:inputType="<em>{@link android.R.attr#inputType}</em>" + android:imeOptions="<em>{@link android.R.attr#imeOptions}</em>" + android:searchSuggestAuthority="<em>string</em>" + android:searchSuggestPath="<em>string</em>" + android:searchSuggestSelection="<em>string</em>" + android:searchSuggestIntentAction="<em>string</em>" + android:searchSuggestIntentData="<em>string</em>" + android:searchSuggestThreshold="<em>int</em>" + android:includeInGlobalSearch=["true" | "false"] + android:searchSettingsDescription="<em>string resource</em>" + android:queryAfterZeroResults=["true" | "false"] + android:voiceSearchMode=["showVoiceSearchButton" | "launchWebSearch" | "launchRecognizer"] + android:voiceLanguageModel=["free-form" | "web_search"] + android:voicePromptText="<em>string resource</em>" + android:voiceLanguage="<em>string</em>" + android:voiceMaxResults="<em>int</em>" + > + <<a href="#actionkey-element">actionkey</a> + android:keycode="<em>{@link android.view.KeyEvent KEYCODE}</em>" + android:queryActionMsg="<em>string</em>" + android:suggestActionMsg="<em>string</em>" + android:suggestActionMsgColumn="<em>string</em>" > +</searchable> +</pre> +</dd> + +<dt>elements:</dt> +<dd> +<dl class="tag-list"> + <dt id="searchable-element"><code><searchable></code></dt> + <dd>Defines all search configurations used with the search dialog. + <p class="caps">attributes:</p> + <dl class="atn-list"> + <dt><code>android:label</code></dt> + <dd><em>String resource</em>. <strong>Required</strong>. This is the name of your application. +It should normally be the same as the name applied to the {@code android:label} attribute of your <a +href="{@docRoot}guide/topics/manifest/activity-element.html#label">{@code <activity>}</a> or +<a href="{@docRoot}guide/topics/manifest/application-element.html#label">{@code +<application>}</a> manifest element. This is only visible to the user when you set +<code>android:includeInGlobalSearch</code> "true", in which case, this label is used to identify +your application as a searchable item in the system's search settings.</dd> + <dt><code>android:hint</code></dt> + <dd><em>String resource</em>. The text to display in the search text field when no text has + been entered. This is recommended in order to provide a hint to the user about what +content is searchable. For consistency among other Android applications, you should format the +string for {@code android:hint} as "Search <em><content-or-product></em>". For example, +"Search songs and artists" or "Search YouTube".</dd> + <dt><code>android:searchMode</code></dt> + <dd><em>Keyword</em>. Sets additional modes that control the search presentation. +Specifically, the available modes define how the query text in the search dialog's text box +should be rewritten when a suggestion is focused. The following mode values are accepted: + <table> + <tr><th>Value</th><th>Description</th></tr> + <tr> + <td><code>"queryRewriteFromData"</code></td> + <td>If set, this causes the suggestion column + {@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA} to be considered as the +text for suggestion query + rewriting. This should only be used when the values in + {@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA} are suitable for user +inspection and editing - + typically, HTTP/HTTPS Uri's.</td> + </tr> + <tr> + <td><code>"queryRewriteFromText"</code></td> + <td>If set, this causes the suggestion + column {@link android.app.SearchManager#SUGGEST_COLUMN_TEXT_1} to be considered as the +text for suggestion query + rewriting. This should be used for suggestions in which no query + text is provided and the {@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA} +values are not suitable + for user inspection and editing.</td> + </tr> + </table> + <p>For more information, see the discussion about rewriting the query text in <a +href="adding-custom-suggestions.html#RewritingQueryText">Adding Custom Suggestions</a>.</p> + </dd> + <dt><code>android:searchButtonText</code></dt> + <dd><em>String resource</em>. The text to display in the button that executes the search. By +default, the button shows a search icon (a magnifying glass), which is ideal for +internationalization.</dd> + <dt><code>android:inputType</code></dt> + <dd><em>Keyword</em>. Defines the type of input method (soft-keyboard) to use with the search +dialog. For most searches, in which free form text is expected, this attribute is not needed and +the default input method should be used. See {@link android.R.attr#inputType} for a list of suitable +values for this attribute.</dd> + <dt><code>android:imeOptions</code></dt> + <dd><em>Keyword</em>. Supplies additional options for the input method. + For most searches, in which free form text is expected, this attribute is not needed, + and will default to "actionSearch" (provides the "search" button instead of a carriage +return). See {@link android.R.attr#imeOptions} for a list of suitable values for this attribute. + </dd> + </dl> + + <p>If you have defined a content provider to generate search suggestions, you need to + define additional attributes in order to configure communications with the Content + Provider. When providing search suggestions, you'll need some of the following + {@code <searchable>} attributes:</p><br/> + + <dl class="atn-list"> + <dt><code>android:searchSuggestAuthority</code></dt> + <dd><em>String</em>. <strong>Required to provide search suggestions</strong>. + This value must match the authority string provided in the {@code android:authorities} +attribute of the {@code <provider>} element.</dd> + <dt><code>android:searchSuggestPath</code></dt> + <dd><em>String</em>. This path will be used as a portion of the suggestions + query {@link android.net.Uri}, after the prefix and authority, but before +the standard suggestions path. + This is only required if you have a single content provider issuing different types + of suggestions (e.g. for different data types) and you need + a way to disambiguate the suggestions queries when they are received.</dd> + <dt><code>android:searchSuggestSelection</code></dt> + <dd><em>String</em>. This value will be passed into your + query function as the {@code selection} parameter. Typically this will be a WHERE clause +for your database, and should contain a single question mark, which is a place-holder for the +actual query string that has been typed by the user. However, you can also use any non-null +value to simply trigger the delivery of the query text via the {@code +selectionArgs} parameter (and then ignore the {@code selection} parameter).</dd> + <dt><code>android:searchSuggestIntentAction</code></dt> + <dd><em>String</em>. The default Intent action to be used when a user + clicks on a search suggestion (such as {@code "android.intent.action.VIEW"}). + If not overridden by the selected suggestion (via the {@link +android.app.SearchManager#SUGGEST_COLUMN_INTENT_ACTION} column), this value will + be placed in the action field of the {@link android.content.Intent} when the + user clicks a suggestion.</dd> + <dt><code>android:searchSuggestIntentData</code></dt> + <dd><em>String</em>. The default Intent data to be used when a user + clicks on a search suggestion. + If not overridden by the selected suggestion (via the {@link +android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA} column), this value will be + placed in the data field of the {@link android.content.Intent} when the user clicks + a suggestion.</dd> + <dt><code>android:searchSuggestThreshold</code></dt> + <dd><em>Integer</em>. The minimum number of characters needed to + trigger a suggestion look-up. Only guarantees that a source will not be + queried for anything shorter than the threshold. The default value is 0.</dd> + </dl> + + <p>For more information about the above attributes for search suggestions, see the guides for + <a href="adding-recent-query-suggestions.html">Adding Recent Query Suggestions</a> and + <a href="adding-custom-suggestions.html">Adding Custom Suggestions</a>.</p> + + <p>Beyond providing search suggestions while using your application's search dialog, you + can also configure your search suggestions to be made available to Quick Search Box, + which will allow users so receive search suggestions from your application content from outside + your application. When providing search suggestions to Quick Search Box, you'll need some of the + following {@code <searchable>} attributes:</p><br/> + + <dl class="atn-list"> + <dt><code>android:includeInGlobalSearch</code></dt> + <dd><em>Boolean</em>. <strong>Required to provide search suggestions in + Quick Search Box</strong>. "true" if you want your suggestions to be + included in the globally accessible Quick Search Box. Note that the user must + still enable your application as a searchable item in the system search settings in order + for your suggestions to appear in Quick Search Box.</dd> + <dt><code>android:searchSettingsDescription</code></dt> + <dd><em>String</em>. Provides a brief description of the search suggestions that you provide +to Quick Search Box, which will be displayed in the searchable items entry for your application. +Your description should concisely describe the content that is searchable. For example, "Artists, +albums, and tracks" for a music application, or "Saved notes" for a notepad application.</dd> + <dt><code>android:queryAfterZeroResults</code></dt> + <dd><em>Boolean</em>. "true" if you want your content provider to be invoked for + supersets of queries that have returned zero results for in the past. For example, if a + source returned zero results for "bo", it would be ignored for "bob". If "false", + this source will only be ignored for a single session; the next time the search dialog + is invoked, all sources will be queried. The default value is false.</dd> + </dl> + + <p>To enable voice search for your search dialog, you'll need some of the + following {@code <searchable>} attributes:</p><br/> + + <dl class="atn-list"> + <dt><code>android:voiceSearchMode</code></dt> + <dd><em>Keyword</em>. <strong>Required to provide voice search capabilities</strong>. + Enables voice search for the search dialog, with a specific mode for voice search. + (Voice search may not be provided by the device, in which case these flags will + have no effect.) The following mode values are accepted: + <table> + <tr><th>Value</th><th>Description</th></tr> + <tr> + <td><code>"showVoiceSearchButton"</code></td> + <td>Display a voice search button. This only + takes effect if voice search is available on the device. If set, then either + {@code "launchWebSearch"} or {@code "launchRecognizer"} must also be set + (separated by the pipe | character).</td> + </tr> + <tr> + <td><code>"launchWebSearch"</code></td> + <td>The voice search button will take the user directly + to a built-in voice web search activity. Most applications will not use this flag, as + it will take the user away from the Activity in which search was invoked.</td> + </tr> + <tr> + <td><code>"launchRecognizer"</code></td> + <td>The voice search button will take + the user directly to a built-in voice recording activity. This Activity + will prompt the user to speak, transcribe the spoken text, and forward the resulting + query text to the searchable Activity, just as if the user had typed it into the + search UI and clicked the search button.</td> + </tr> + </table> + </dd> + <dt><code>android:voiceLanguageModel</code></dt> + <dd><em>Keyword</em>. The language model that + should be used by the voice recognition system. The following values are accepted: + <table> + <tr><th>Value</th><th>Description</th></tr> + <tr> + <td><code>"free_form"</code></td> + <td>Use a language model based on free-form speech recognition. This is the +default.</td> + </tr> + <tr> + <td><code>"web_search"</code></td> + <td>Use a language model based on web search terms.</td> + </tr> + </table> + <p>Also see + {@link android.speech.RecognizerIntent#EXTRA_LANGUAGE_MODEL} for more + information.</p></dd> + <dt><code>android:voicePromptText</code></dt> + <dd><em>String</em>. An additional message to display in the voice input dialog.</dd> + <dt><code>android:voiceLanguage</code></dt> + <dd><em>String</em>. The spoken language to be expected, expressed as the string value of +a constants in {@link java.util.Locale} (for example, {@code "de"} for German or {@code "fr"} for +French). This is only needed if it is different from the current value of {@link +java.util.Locale#getDefault() Locale.getDefault()}.</dd> + <dt><code>android:voiceMaxResults</code></dt> + <dd><em>Integer</em>. Forces the maximum number of results to return, + including the "best" result which will always be provided as the {@link +android.content.Intent#ACTION_SEARCH} Intent's primary + query. Must be 1 or greater. Use {@link android.speech.RecognizerIntent#EXTRA_RESULTS} to +get the results from the Intent. + If not provided, the recognizer will choose how many results to return.</dd> + </dl> + </dd> <!-- end searchable element --> + + + <dt id="actionkey-element"><code><actionkey></code></dt> + <dd>Defines a shortcut key for a search action, in order to provide special behaviors at the touch +of a button, based on the current query or selected suggestion. For example, the Contacts +application enables the device call key for suggestions. So, when +the user focuses on a search suggestion using the directional controls and then presses the call +key, the application will immediately initiate a phone call to the suggested contact. See the +{@link android.app.SearchManager} for more information about action keys. + <p class="caps">attributes:</p> + <dl class="atn-list"> + <dt><code>android:keycode</code></dt> + <dd><em>String</em>. <strong>Required</strong>. A key code from {@link +android.view.KeyEvent} that represents the action key + you wish to respond to (for example {@code "KEYCODE_CALL"}). This will be added to the + {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} Intent that is passed to your + searchable Activity. To examine the key code, use + {@link android.content.Intent#getIntExtra getIntExtra(SearchManager.ACTION_KEY)}. + In addition to the key code, you must also provide one or more of + the action specifier attributes below. Not all action keys +are actually supported using this mechanism, as many of them are used for typing, + navigation, or system functions. Note that although each of the action message elements are +optional, at least one must be present for the action key to have any effect.</dd> + <dt><code>android:queryActionMsg</code></dt> + <dd><em>String</em>. An action message to be sent if the action key is pressed while the +user is simply entering query text. This will be added to the + {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} Intent that is + passed to your searchable Activity. To examine the string, use + {@link android.content.Intent#getStringExtra + getStringExtra(SearchManager.ACTION_MSG)}.</dd> + <dt><code>android:suggestActionMsg</code></dt> + <dd><em>String</em>. An action message to be sent if the action key is pressed while a + suggestion is being displayed and is currently selected. This will be added to the + Intent that is passed to your searchable Activity (using the action you've defined for + suggestions). To examine the string, + use {@link android.content.Intent#getStringExtra + getStringExtra(SearchManager.ACTION_MSG)}. Note that this should only be used if all your +suggestions support this action key. If not all suggestions can handle the same action key, then +you must instead use the following {@code android:suggestActionMsgColumn} attribute.</dd> + <dt><code>android:suggestActionMsgColumn</code></dt> + <dd><em>String</em>. The name of the column in your content provider that defines the +action message for this action key, which is to be sent if the action key is pressed while a + suggestion is being displayed and is currently selected. This attribute lets you control the +action key on a suggestion-by-suggestion basis, because, instead of using the {@code +android:suggestActionMsg} attribute to define the action message for all suggestions, each entry in +your content provider provides its own action message. First, you must define a column in your +content provider for each suggestion to provide an action message, then provide the name of that +column in this attribute. The search manager will look at your suggestion cursor, + using the string provided here in order to select your action message column, and + then select the action message string from the cursor. That string will be added to the + Intent that is passed to your searchable Activity (using the action you've defined for + suggestions). To examine the string, use {@link +android.content.Intent#getStringExtra getStringExtra(SearchManager.ACTION_MSG)}. If the data +does not exist for the selected suggestion, the action key will be ignored.</dd> + </dl> + </dd><!-- end action key --> + </dl> +</dd><!-- end elements --> + + +<dt>example:</dt> +<dd>XML file saved at <code>res/xml/searchable.xml</code>: +<pre> +<?xml version="1.0" encoding="utf-8"?> +<searchable xmlns:android="http://schemas.android.com/apk/res/android" + android:label="@string/search_label" + android:hint="@string/search_hint" + android:searchSuggestAuthority="dictionary" + android:searchSuggestIntentAction="android.intent.action.VIEW" + android:includeInGlobalSearch="true" + android:searchSettingsDescription="@string/settings_description" > +</searchable> +</pre> + +</dd> <!-- end example --> + + +</dl> + + + + diff --git a/docs/html/images/search/search-suggest-custom.png b/docs/html/images/search/search-suggest-custom.png Binary files differnew file mode 100644 index 0000000..be14219 --- /dev/null +++ b/docs/html/images/search/search-suggest-custom.png diff --git a/docs/html/images/search/search-suggest-recent-queries.png b/docs/html/images/search/search-suggest-recent-queries.png Binary files differnew file mode 100644 index 0000000..869edf2 --- /dev/null +++ b/docs/html/images/search/search-suggest-recent-queries.png diff --git a/docs/html/images/search/search-ui.png b/docs/html/images/search/search-ui.png Binary files differnew file mode 100644 index 0000000..19ecc19 --- /dev/null +++ b/docs/html/images/search/search-ui.png diff --git a/docs/html/shareables/search_icons.zip b/docs/html/shareables/search_icons.zip Binary files differnew file mode 100644 index 0000000..bc98465 --- /dev/null +++ b/docs/html/shareables/search_icons.zip |