new docs: Search (topic home page) Using the Android Search Box Adding Recent Query Suggestions Adding Custom Suggestions Searchable Configuration Also adds screenshots and a ZIP file with search icons Change-Id: I44ae31b23d4878b052e91d368e95d50192776c80
700 lines
38 KiB
Plaintext
700 lines
38 KiB
Plaintext
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>
|
|
|