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