/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.app; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.DialogInterface; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.RemoteException; import android.os.ServiceManager; import android.server.search.SearchableInfo; import android.util.Log; import android.view.KeyEvent; import java.util.List; /** * This class provides access to the system search services. * *

In practice, you won't interact with this class directly, as search * services are provided through methods in {@link android.app.Activity Activity} * methods and the the {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} * {@link android.content.Intent Intent}. This class does provide a basic * overview of search services and how to integrate them with your activities. * If you do require direct access to the Search Manager, do not instantiate * this class directly; instead, retrieve it through * {@link android.content.Context#getSystemService * context.getSystemService(Context.SEARCH_SERVICE)}. * *

Topics covered here: *

    *
  1. Developer Guide *
  2. How Search Is Invoked *
  3. Query-Search Applications *
  4. Filter-Search Applications *
  5. Search Suggestions *
  6. Action Keys *
  7. Searchability Metadata *
  8. Passing Search Context *
  9. Protecting User Privacy *
* * *

Developer Guide

* *

The ability to search for user, system, or network based data is considered to be * a core user-level feature of the android platform. At any time, the user should be * able to use a familiar command, button, or keystroke to invoke search, and the user * should be able to search any data which is available to them. The goal is to make search * appear to the user as a seamless, system-wide feature. * *

In terms of implementation, there are three broad classes of Applications: *

    *
  1. Applications that are not inherently searchable
  2. *
  3. Query-Search Applications
  4. *
  5. Filter-Search Applications
  6. *
*

These categories, as well as related topics, are discussed in * the sections below. * *

Even if your application is not searchable, it can still support the invocation of * search. Please review the section How Search Is Invoked * for more information on how to support this. * *

Many applications are searchable. These are * the applications which can convert a query string into a list of results. * Within this subset, applications can be grouped loosely into two families: *

*

Generally speaking, you would use query search for network-based data, and filter * search for local data, but this is not a hard requirement and applications * are free to use the model that fits them best (or invent a new model). *

It should be clear that the search implementation decouples "search * invocation" from "searchable". This satisfies the goal of making search appear * to be "universal". The user should be able to launch any search from * almost any context. * * *

How Search Is Invoked

* *

Unless impossible or inapplicable, all applications should support * invoking the search UI. This means that when the user invokes the search command, * a search UI will be presented to them. The search command is currently defined as a menu * item called "Search" (with an alphabetic shortcut key of "S"), or on some devices, a dedicated * search button key. *

If your application is not inherently searchable, you can also allow the search UI * to be invoked in a "web search" mode. If the user enters a search term and clicks the * "Search" button, this will bring the browser to the front and will launch a web-based * search. The user will be able to click the "Back" button and return to your application. *

In general this is implemented by your activity, or the {@link android.app.Activity Activity} * base class, which captures the search command and invokes the Search Manager to * display and operate the search UI. You can also cause the search UI to be presented in response * to user keystrokes in your activity (for example, to instantly start filter searching while * viewing a list and typing any key). *

The search UI is presented as a floating * window and does not cause any change in the activity stack. If the user * cancels search, the previous activity re-emerges. If the user launches a * search, this will be done by sending a search {@link android.content.Intent Intent} (see below), * and the normal intent-handling sequence will take place (your activity will pause, * etc.) *

What you need to do: First, you should consider the way in which you want to * handle invoking search. There are four broad (and partially overlapping) categories for * you to choose from. *

* *

How to define a search menu. The system provides the following resources which may * be useful when adding a search item to your menu: *

* *

How to invoke search directly. In order to invoke search directly, from a button * or menu item, you can launch a generic search by calling * {@link android.app.Activity#onSearchRequested onSearchRequested} as shown: *

 * onSearchRequested();
* *

How to implement type-to-search. While setting up your activity, call * {@link android.app.Activity#setDefaultKeyMode setDefaultKeyMode}: *

 * setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);   // search within your activity
 * setDefaultKeyMode(DEFAULT_KEYS_SEARCH_GLOBAL);  // search using platform global search
* *

How to enable web-based search. In addition to searching within your activity or * application, you can also use the Search Manager to invoke a platform-global search, typically * a web search. There are two ways to do this: *

* *

How to disable search from your activity. search is a system-wide feature and users * will expect it to be available in all contexts. If your UI design absolutely precludes * launching search, override {@link android.app.Activity#onSearchRequested onSearchRequested} * as shown: *

 * @Override
 * public boolean onSearchRequested() {
 *    return false;
 * }
* *

Managing focus and knowing if Search is active. The search UI is not a separate * activity, and when the UI is invoked or dismissed, your activity will not typically be paused, * resumed, or otherwise notified by the methods defined in * Application Fundamentals: * Activity Lifecycle. The search UI is * handled in the same way as other system UI elements which may appear from time to time, such as * notifications, screen locks, or other system alerts: *

When the search UI appears, your activity will lose input focus. *

When the search activity is dismissed, there are three possible outcomes: *

*

This list is provided in order to clarify the ways in which your activities will interact with * the search UI. More details on searchable activities and search intents are provided in the * sections below. * * *

Query-Search Applications

* *

Query-search applications are those that take a single query (e.g. a search * string) and present a set of results that may fit. Primary examples include * web queries, map lookups, or email searches (with the common thread being * network query dispatch). It may also be the case that certain local searches * are treated this way. It's up to the application to decide. * *

What you need to do: The following steps are necessary in order to * implement query search. *

* *

Code snippet showing handling of intents in your search activity: *

 * @Override
 * protected void onCreate(Bundle icicle) {
 *     super.onCreate(icicle);
 *     
 *     final Intent queryIntent = getIntent();
 *     final String queryAction = queryIntent.getAction();
 *     if (Intent.ACTION_SEARCH.equals(queryAction)) {
 *         doSearchWithIntent(queryIntent);
 *     }
 * }
 * 
 * private void doSearchWithIntent(final Intent queryIntent) {
 *     final String queryString = queryIntent.getStringExtra(SearchManager.QUERY);
 *     doSearchWithQuery(queryString);
 * }
* * *

Filter-Search Applications

* *

Filter-search applications are those that use live text entry (e.g. keystrokes)) to * display and continuously update a list of results. Primary examples include applications * that use locally-stored data. * *

Filter search is not directly supported by the Search Manager. Most filter search * implementations will use variants of {@link android.widget.Filterable}, such as a * {@link android.widget.ListView} bound to a {@link android.widget.SimpleCursorAdapter}. However, * you may find it useful to mix them together, by declaring your filtered view searchable. With * this configuration, you can still present the standard search dialog in all activities * within your application, but transition to a filtered search when you enter the activity * and display the results. * * *

Search Suggestions

* *

A powerful feature of the Search Manager is the ability of any application to easily provide * live "suggestions" in order to prompt the user. Each application implements suggestions in a * different, unique, and appropriate way. Suggestions be drawn from many sources, including but * not limited to: *

* *

Another feature of suggestions is that they can expose queries or results before the user * ever visits the application. This reduces the amount of context switching required, and helps * the user access their data quickly and with less context shifting. In order to provide this * capability, suggestions are accessed via a * {@link android.content.ContentProvider Content Provider}. * *

The primary form of suggestions is known as queried suggestions and is based on query * text that the user has already typed. This would generally be based on partial matches in * the available data. In certain situations - for example, when no query text has been typed yet - * an application may also opt to provide zero-query suggestions. * These would typically be drawn from the same data source, but because no partial query text is * available, they should be weighted based on other factors - for example, most recent queries * or most recent results. * *

Overview of how suggestions are provided. When the search manager identifies a * particular activity as searchable, it will check for certain metadata which indicates that * there is also a source of suggestions. If suggestions are provided, the following steps are * taken. *

* *

Simple Recent-Query-Based Suggestions. The Android framework provides a simple Search * Suggestions provider, which simply records and replays recent queries. For many applications, * this will be sufficient. The basic steps you will need to * do, in order to use the built-in recent queries suggestions provider, are as follows: *

*

For complete implementation details, please refer to * {@link android.content.SearchRecentSuggestionsProvider}. The rest of the information in this * section should not be necessary, as it refers to custom suggestions providers. * *

Creating a Customized Suggestions Provider: In order to create more sophisticated * suggestion providers, you'll need to take the following steps: *

* *

Configuring your Content Provider to Receive Suggestion Queries. The basic job of * a search suggestions {@link android.content.ContentProvider Content Provider} is to provide * "live" (while-you-type) conversion of the user's query text into a set of zero or more * suggestions. Each application is free to define the conversion, and as described above there are * many possible solutions. This section simply defines how to communicate with the suggestion * provider. * *

The Search Manager must first determine if your package provides suggestions. This is done * by examination of your searchable meta-data XML file. The android:searchSuggestAuthority * attribute, if provided, is the signal to obtain & display suggestions. * *

Every query includes a Uri, and the Search Manager will format the Uri as shown: *

 * content:// your.suggest.authority / your.suggest.path / SearchManager.SUGGEST_URI_PATH_QUERY
* *

Your Content Provider can receive the query text in one of two ways. *

* *

Handling empty queries. Your application should handle the "empty query" * (no user text entered) case properly, and generate useful suggestions in this case. There are a * number of ways to do this; Two are outlined here: *

* *

The Format of Individual Suggestions. Your suggestions are communicated back to the * Search Manager by way of a {@link android.database.Cursor Cursor}. The Search Manager will * usually pass a null Projection, which means that your provider can simply return all appropriate * columns for each suggestion. The columns currently defined are: * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Column Name Description Required?
{@link #SUGGEST_COLUMN_FORMAT}Unused - can be null.No
{@link #SUGGEST_COLUMN_TEXT_1}This is the line of text that will be presented to the user as the suggestion.Yes
{@link #SUGGEST_COLUMN_TEXT_2}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, or it can be null or empty to indicate no * text in this row's suggestion.No
{@link #SUGGEST_COLUMN_ICON_1}If your cursor includes this column, then all suggestions will be provided in an * icons+text format. This value should be a reference to the icon to * draw on the left side, or it can be null or zero to indicate no icon in this row. * No.
{@link #SUGGEST_COLUMN_ICON_2}If your cursor includes this column, then all suggestions will be provided in an * icons+text format. This value should be a reference to the icon to * draw on the right side, or it can be null or zero to indicate no icon in this row. * No.
{@link #SUGGEST_COLUMN_INTENT_ACTION}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 android:searchSuggestIntentAction * field in your XML metadata. 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 it using XML metadata and omit it from * the cursor.No
{@link #SUGGEST_COLUMN_INTENT_DATA}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 android:searchSuggestIntentData field in * your XML metadata. 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 * XML metadata and omit it from the cursor.No
{@link #SUGGEST_COLUMN_INTENT_DATA_ID}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 has already been set to an appropriate base string.No
{@link #SUGGEST_COLUMN_QUERY}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.Required if suggestion's action is * {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH}, optional otherwise.
Other ColumnsFinally, if you have defined any Action Keys and you wish * for them to have suggestion-specific definitions, you'll need to define one * additional column per action key. The action key will only trigger if the * currently-selection suggestion has a non-empty string in the corresponding column. * See the section on Action Keys for additional details and * implementation steps.No
* *

Clearly there are quite a few permutations of your suggestion data, but in the next section * we'll look at a few simple combinations that you'll select from. * *

The Format Of Intents Sent By Search Suggestions. Although there are many ways to * configure these intents, this document will provide specific information on just a few of them. *

*

This list is not meant to be exhaustive. Applications should feel free to define other types * of suggestions. For example, you could reduce long lists of results to summaries, and use one * of the above intents (or one of your own) with specially formatted Data Uri's to display more * detailed results. Or you could display textual shortcuts as suggestions, but launch a display * in a more data-appropriate format such as media artwork. * *

Suggestion Rewriting. If the user navigates through the suggestions list, the UI * may temporarily rewrite the user's query with a query that matches the currently selected * suggestion. This enables the user to see what query is being suggested, and also allows the user * to click or touch in the entry EditText element and make further edits to the query before * dispatching it. In order to perform this correctly, the Search UI needs to know exactly what * text to rewrite the query with. * *

For each suggestion, the following logic is used to select a new query string: *

* * *

Action Keys

* *

Searchable activities may also wish to provide shortcuts based on the various action keys * available on the device. The most basic example of this is the contacts app, which enables the * green "dial" key for quick access during searching. Not all action keys are available on * every device, and not all are allowed to be overriden in this way. (For example, the "Home" * key must always return to the home screen, with no exceptions.) * *

In order to define action keys for your searchable application, you must do two things. * *

* *

Updating metadata. For each keycode of interest, you must add an <actionkey> * element. Within this element you must define two or three attributes. The first attribute, * <android:keycode>, is required; It is the key code of the action key event, as defined in * {@link android.view.KeyEvent}. The remaining two attributes define the value of the actionkey's * message, which will be passed to your searchable activity in the * {@link android.content.Intent Intent} (see below for more details). Although each of these * attributes is optional, you must define one or both for the action key to have any effect. * <android:queryActionMsg> provides the message that will be sent if the action key is * pressed while the user is simply entering query text. <android:suggestActionMsgColumn> * is used when action keys are tied to specific suggestions. This attribute provides the name * of a column in your suggestion cursor; The individual suggestion, in that column, * provides the message. (If the cell is empty or null, that suggestion will not work with that * action key.) *

See the Searchability Metadata section for more details * and examples. * *

Receiving Action Keys Intents launched by action keys will be specially marked * using a combination of values. This enables your searchable application to examine the intent, * if necessary, and perform special processing. For example, clicking a suggested contact might * simply display them; Selecting a suggested contact and clicking the dial button might * immediately call them. * *

When a search {@link android.content.Intent Intent} is launched by an action key, two values * will be added to the extras field. *

* * *

Searchability Metadata

* *

Every activity that is searchable must provide a small amount of additional information * in order to properly configure the search system. This controls the way that your search * is presented to the user, and controls for the various modalities described previously. * *

If your application is not searchable, * then you do not need to provide any search metadata, and you can skip the rest of this section. * When this search metadata cannot be found, the search manager will assume that the activity * does not implement search. (Note: to implement web-based search, you will need to add * the android.app.default_searchable metadata to your manifest, as shown below.) * *

Values you supply in metadata apply only to each local searchable activity. Each * searchable activity can define a completely unique search experience relevant to its own * capabilities and user experience requirements, and a single application can even define multiple * searchable activities. * *

Metadata for searchable activity. As with your search implementations described * above, you must first identify which of your activities is searchable. In the * manifest entry for this activity, you must * provide two elements: *

* *

Here is a snippet showing the necessary elements in the * manifest entry for your searchable activity. *

 *        <!-- Search Activity - searchable -->
 *        <activity android:name="MySearchActivity" 
 *                  android:label="Search"
 *                  android:launchMode="singleTop">
 *            <intent-filter>
 *                <action android:name="android.intent.action.SEARCH" />
 *                <category android:name="android.intent.category.DEFAULT" />
 *            </intent-filter>
 *            <meta-data android:name="android.app.searchable" 
 *                       android:resource="@xml/searchable" />
 *        </activity>
* *

Next, you must provide the rest of the searchability configuration in * the small XML file, stored in the ../xml/ folder in your build. The XML file is a * simple enumeration of the search configuration parameters for searching within this activity, * application, or package. Here is a sample XML file (named searchable.xml, for use with * the above manifest) for a query-search activity. * *

 * <searchable xmlns:android="http://schemas.android.com/apk/res/android"
 *     android:label="@string/search_label"
 *     android:hint="@string/search_hint" >
 * </searchable>
* *

Note that all user-visible strings must be provided in the form of "@string" * references. Hard-coded strings, which cannot be localized, will not work properly in search * metadata. * *

Attributes you can set in search metadata: * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Attribute Description Required?
android:labelThis is the name for your application that will be presented to the user in a * list of search targets, or in the search box as a label.Yes
android:iconIf provided, this icon will be used in place of the label string. This * is provided in order to present logos or other non-textual banners.No
android:hintThis is the text to display in the search text field when no user text has been * entered.No
android:searchButtonTextIf provided, this text will replace the default text in the "Search" button.No
android:searchModeIf provided and non-zero, sets additional modes for control of the search * presentation. The following mode bits are defined: * * * * * * * * * * * * * * * *
showSearchLabelAsBadgeIf set, this flag enables the display of the search target (label) * within the search bar. If this flag and showSearchIconAsBadge * (see below) are both not set, no badge will be shown.
showSearchIconAsBadgeIf set, this flag enables the display of the search target (icon) within * the search bar. If this flag and showSearchLabelAsBadge * (see above) are both not set, no badge will be shown. If both flags * are set, showSearchIconAsBadge has precedence and the icon will be * shown.
queryRewriteFromDataIf set, this flag causes the suggestion column SUGGEST_COLUMN_INTENT_DATA * to be considered as the text for suggestion query rewriting. This should * only be used when the values in SUGGEST_COLUMN_INTENT_DATA are suitable * for user inspection and editing - typically, HTTP/HTTPS Uri's.
queryRewriteFromTextIf set, this flag causes the suggestion column 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 * SUGGEST_COLUMN_INTENT_DATA values are not suitable for user inspection * and editing.
No
android:inputTypeIf provided, supplies a hint about the type of search text the user will be * entering. For most searches, in which free form text is expected, this attribute * need not be provided. Suitable values for this attribute are described in the * inputType attribute.No
android:imeOptionsIf provided, supplies additional options for the input method. * For most searches, in which free form text is expected, this attribute * need not be provided, and will default to "actionSearch". * Suitable values for this attribute are described in the * imeOptions attribute.No
* *

Styleable Resources in your Metadata. It's possible to provide alternate strings * for your searchable application, in order to provide localization and/or to better visual * presentation on different device configurations. Each searchable activity has a single XML * metadata file, but any resource references can be replaced at runtime based on device * configuration, language setting, and other system inputs. * *

A concrete example is the "hint" text you supply using the android:searchHint attribute. * In portrait mode you'll have less screen space and may need to provide a shorter string, but * in landscape mode you can provide a longer, more descriptive hint. To do this, you'll need to * define two or more strings.xml files, in the following directories: *

* *

For more complete documentation on this capability, see * Resources and * Internationalization: Alternate Resources. * *

Metadata for non-searchable activities. Activities which are part of a searchable * application, but don't implement search itself, require a bit of "glue" in order to cause * them to invoke search using your searchable activity as their primary context. If this is not * provided, then searches from these activities will use the system default search context. * *

The simplest way to specify this is to add a search reference element to the * application entry in the manifest file. * The value of this reference can be either of: *

* *

Here is a snippet showing the necessary addition to the manifest entry for your * non-searchable activities. *

 *        <application>
 *            <meta-data android:name="android.app.default_searchable"
 *                       android:value=".MySearchActivity" />
 *            
 *            <!-- followed by activities, providers, etc... -->
 *        </application>
* *

You can also specify android.app.default_searchable on a per-activity basis, by including * the meta-data element (as shown above) in one or more activity sections. If found, these will * override the reference in the application section. The only reason to configure your application * this way would be if you wish to partition it into separate sections with different search * behaviors; Otherwise this configuration is not recommended. * *

Additional metadata for search suggestions. If you have defined a content provider * to generate search suggestions, you'll need to publish it to the system, and you'll need to * provide a bit of additional XML metadata in order to configure communications with it. * *

First, in your manifest, you'll add the * following lines. *

 *        <!-- Content provider for search suggestions -->
 *        <provider android:name="YourSuggestionProviderClass"
 *                android:authorities="your.suggestion.authority" />
* *

Next, you'll add a few lines to your XML metadata file, as shown: *

 *     <!-- Required attribute for any suggestions provider -->
 *     android:searchSuggestAuthority="your.suggestion.authority"
 *     
 *     <!-- Optional attribute for configuring queries -->
 *     android:searchSuggestSelection="field =?"
 *     
 *     <!-- Optional attributes for configuring intent construction -->
 *     android:searchSuggestIntentAction="intent action string"
 *     android:searchSuggestIntentData="intent data Uri" />
* *

Elements of search metadata that support suggestions: * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Attribute Description Required?
android:searchSuggestAuthorityThis value must match the authority string provided in the provider section * of your manifest.Yes
android:searchSuggestPathIf provided, this will be inserted in the suggestions query Uri, after the authority * you have provide 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.No
android:searchSuggestSelectionIf provided, this value will be passed into your query function as the * selection parameter. Typically this will be a WHERE clause for your database, * and will contain a single question mark, which represents 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 selection arguments), and then * use the query text in any way appropriate for your provider (ignoring the actual * text of the selection parameter.)No
android:searchSuggestIntentActionIf provided, and not overridden by the selected suggestion, this value will be * placed in the action field of the {@link android.content.Intent Intent} when the * user clicks a suggestion.No
android:searchSuggestIntentDataIf provided, and not overridden by the selected suggestion, this value will be * placed in the data field of the {@link android.content.Intent Intent} when the user * clicks a suggestion.No
* *

Additional metadata for search action keys. For each action key that you would like to * define, you'll need to add an additional element defining that key, and using the attributes * discussed in Action Keys. A simple example is shown here: * *

<actionkey
 *     android:keycode="KEYCODE_CALL"
 *     android:queryActionMsg="call"
 *     android:suggestActionMsg="call"
 *     android:suggestActionMsgColumn="call_column" />
* *

Elements of search metadata that support search action keys. Note that although each of the * action message elements are marked as optional, at least one must be present for the * action key to have any effect. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Attribute Description Required?
android:keycodeThis attribute denotes the action key you wish to respond to. Note that not * all action keys are actually supported using this mechanism, as many of them are * used for typing, navigation, or system functions. 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)}. *

Note, in addition to the keycode, you must also provide one or more of the action * specifier attributes.

Yes
android:queryActionMsgIf you wish to handle an action key during normal search query entry, you * must define an action string here. 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)}.No
android:suggestActionMsgIf you wish to handle an action key while a suggestion is being displayed and * selected, there are two ways to handle this. If all of your suggestions * can handle the action key, you can simply define the action message using this * attribute. 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)}.No
android:suggestActionMsgColumnIf you wish to handle an action key while a suggestion is being displayed and * selected, but you do not wish to enable this action key for every suggestion, * then you can use this attribute to control it on a suggestion-by-suggestion basis. * First, you must define a column (and name it here) where your suggestions will * include the action string. Then, in your content provider, you must provide this * column, and when desired, provide data in this column. * The search manager will look at your suggestion cursor, using the string * provided here in order to select a column, and will use that to select a string from * the cursor. That string 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)}. If the data does not exist for the * selection suggestion, the action key will be ignored.No
* *

Additional metadata for enabling voice search. To enable voice search for your * activity, you can add fields to the metadata that enable and configure voice search. When * enabled (and available on the device), a voice search button will be displayed in the * Search UI. Clicking this button will launch a voice search activity. When the user has * finished speaking, the voice search phrase will be transcribed into text and presented to the * searchable activity as if it were a typed query. * *

Elements of search metadata that support voice search: * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Attribute Description Required?
android:voiceSearchModeIf provided and non-zero, enables voice search. (Voice search may not be * provided by the device, in which case these flags will have no effect.) The * following mode bits are defined: * * * * * * * * * * * * *
showVoiceSearchButtonIf set, display a voice search button. This only takes effect if voice * search is available on the device. If set, then launchWebSearch or * launchRecognizer must also be set.
launchWebSearchIf set, 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.
launchRecognizerIf set, 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.
No
android:voiceLanguageModelIf provided, this specifies the language model that should be used by the voice * recognition system. * See {@link android.speech.RecognizerIntent#EXTRA_LANGUAGE_MODEL} * for more information. If not provided, the default value * {@link android.speech.RecognizerIntent#LANGUAGE_MODEL_FREE_FORM} will be used.No
android:voicePromptTextIf provided, this specifies a prompt that will be displayed during voice input. * (If not provided, a default prompt will be displayed.)No
android:voiceLanguageIf provided, this specifies the spoken language to be expected. This is only * needed if it is different from the current value of * {@link java.util.Locale#getDefault()}. * No
android:voiceMaxResultsIf provided, enforces the maximum number of results to return, including the "best" * result which will always be provided as the SEARCH intent's primary query. Must be * one 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.No
* * *

Passing Search Context

* *

In order to improve search experience, an application may wish to specify * additional data along with the search, such as local history or context. For * example, a maps search would be improved by including the current location. * In order to simplify the structure of your activities, this can be done using * the search manager. * *

Any data can be provided at the time the search is launched, as long as it * can be stored in a {@link android.os.Bundle Bundle} object. * *

To pass application data into the Search Manager, you'll need to override * {@link android.app.Activity#onSearchRequested onSearchRequested} as follows: * *

 * @Override
 * public boolean onSearchRequested() {
 *     Bundle appData = new Bundle();
 *     appData.put...();
 *     appData.put...();
 *     startSearch(null, false, appData);
 *     return true;
 * }
* *

To receive application data from the Search Manager, you'll extract it from * the {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} * {@link android.content.Intent Intent} as follows: * *

 * final Bundle appData = queryIntent.getBundleExtra(SearchManager.APP_DATA);
 * if (appData != null) {
 *     appData.get...();
 *     appData.get...();
 * }
* * *

Protecting User Privacy

* *

Many users consider their activities on the phone, including searches, to be private * information. Applications that implement search should take steps to protect users' privacy * wherever possible. This section covers two areas of concern, but you should consider your search * design carefully and take any additional steps necessary. * *

Don't send personal information to servers, and if you do, don't log it. * "Personal information" is information that can personally identify your users, such as name, * email address or 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 with your searches. For example, if you are searching for * businesses near a zip code, you don't need to send the user ID as well - just send the zip code * to the server. If you do need to send 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. * *

Provide the user with a way to clear their search history. The Search Manager helps * your application provide context-specific suggestions. 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, preference, or button. If you are using * {@link android.provider.SearchRecentSuggestions}, you can simply call its * {@link android.provider.SearchRecentSuggestions#clearHistory() clearHistory()} method from * your "Clear History" UI. If you are implementing your own form of recent suggestions, you'll * need to provide a similar a "clear history" API in your provider, and call it from your * "Clear History" UI. */ public class SearchManager implements DialogInterface.OnDismissListener, DialogInterface.OnCancelListener { private static final boolean DBG = false; private static final String TAG = "SearchManager"; /** * This is a shortcut definition for the default menu key to use for invoking search. * * See Menu.Item.setAlphabeticShortcut() for more information. */ public final static char MENU_KEY = 's'; /** * This is a shortcut definition for the default menu key to use for invoking search. * * See Menu.Item.setAlphabeticShortcut() for more information. */ public final static int MENU_KEYCODE = KeyEvent.KEYCODE_S; /** * Intent extra data key: Use this key with * {@link android.content.Intent#getStringExtra * content.Intent.getStringExtra()} * to obtain the query string from Intent.ACTION_SEARCH. */ public final static String QUERY = "query"; /** * Intent extra data key: Use this key with * {@link android.content.Intent#getStringExtra * content.Intent.getStringExtra()} * to obtain the query string typed in by the user. * This may be different from the value of {@link #QUERY} * if the intent is the result of selecting a suggestion. * In that case, {@link #QUERY} will contain the value of * {@link #SUGGEST_COLUMN_QUERY} for the suggestion, and * {@link #USER_QUERY} will contain the string typed by the * user. */ public final static String USER_QUERY = "user_query"; /** * Intent extra data key: Use this key with Intent.ACTION_SEARCH and * {@link android.content.Intent#getBundleExtra * content.Intent.getBundleExtra()} * to obtain any additional app-specific data that was inserted by the * activity that launched the search. */ public final static String APP_DATA = "app_data"; /** * Intent app_data bundle key: Use this key with the bundle from * {@link android.content.Intent#getBundleExtra * content.Intent.getBundleExtra(APP_DATA)} to obtain the source identifier * set by the activity that launched the search. * * @hide */ public final static String SOURCE = "source"; /** * Intent extra data key: Use this key with Intent.ACTION_SEARCH and * {@link android.content.Intent#getIntExtra content.Intent.getIntExtra()} * to obtain the keycode that the user used to trigger this query. It will be zero if the * user simply pressed the "GO" button on the search UI. This is primarily used in conjunction * with the keycode attribute in the actionkey element of your searchable.xml configuration * file. */ public final static String ACTION_KEY = "action_key"; /** * Intent component name key: This key will be used for the extra populated by the * {@link #SUGGEST_COLUMN_INTENT_COMPONENT_NAME} column. * * {@hide} */ public final static String COMPONENT_NAME_KEY = "intent_component_name_key"; /** * Intent extra data key: This key will be used for the extra populated by the * {@link #SUGGEST_COLUMN_INTENT_EXTRA_DATA} column. * * {@hide} */ public final static String EXTRA_DATA_KEY = "intent_extra_data_key"; /** * Defines the constants used in the communication between {@link android.app.SearchDialog} and * the global search provider via {@link Cursor#respond(android.os.Bundle)}. * * @hide */ public static class DialogCursorProtocol { /** * The sent bundle will contain this integer key, with a value set to one of the events * below. */ public final static String METHOD = "DialogCursorProtocol.method"; /** * After data has been refreshed. */ public final static int POST_REFRESH = 0; public final static String POST_REFRESH_RECEIVE_ISPENDING = "DialogCursorProtocol.POST_REFRESH.isPending"; public final static String POST_REFRESH_RECEIVE_DISPLAY_NOTIFY = "DialogCursorProtocol.POST_REFRESH.displayNotify"; /** * Just before closing the cursor. */ public final static int PRE_CLOSE = 1; public final static String PRE_CLOSE_SEND_MAX_DISPLAY_POS = "DialogCursorProtocol.PRE_CLOSE.sendDisplayPosition"; /** * When a position has been clicked. */ public final static int CLICK = 2; public final static String CLICK_SEND_POSITION = "DialogCursorProtocol.CLICK.sendPosition"; public final static String CLICK_RECEIVE_SELECTED_POS = "DialogCursorProtocol.CLICK.receiveSelectedPosition"; /** * When the threshold received in {@link #POST_REFRESH_RECEIVE_DISPLAY_NOTIFY} is displayed. */ public final static int THRESH_HIT = 3; } /** * Intent extra data key: Use this key with Intent.ACTION_SEARCH and * {@link android.content.Intent#getStringExtra content.Intent.getStringExtra()} * to obtain the action message that was defined for a particular search action key and/or * suggestion. It will be null if the search was launched by typing "enter", touched the the * "GO" button, or other means not involving any action key. */ public final static String ACTION_MSG = "action_msg"; /** * Uri path for queried suggestions data. This is the path that the search manager * will use when querying your content provider for suggestions data based on user input * (e.g. looking for partial matches). * Typically you'll use this with a URI matcher. */ public final static String SUGGEST_URI_PATH_QUERY = "search_suggest_query"; /** * MIME type for suggestions data. You'll use this in your suggestions content provider * in the getType() function. */ public final static String SUGGEST_MIME_TYPE = "vnd.android.cursor.dir/vnd.android.search.suggest"; /** * Uri path for shortcut validation. This is the path that the search manager will use when * querying your content provider to refresh a shortcutted suggestion result and to check if it * is still valid. When asked, a source may return an up to date result, or no result. No * result indicates the shortcut refers to a no longer valid sugggestion. * * @see #SUGGEST_COLUMN_SHORTCUT_ID * * @hide pending API council approval */ public final static String SUGGEST_URI_PATH_SHORTCUT = "search_suggest_shortcut"; /** * MIME type for shortcut validation. You'll use this in your suggestions content provider * in the getType() function. * * @hide pending API council approval */ public final static String SHORTCUT_MIME_TYPE = "vnd.android.cursor.item/vnd.android.search.suggest"; /** * The authority of the provider to report clicks to when a click is detected after pivoting * into a specific app's search from global search. * * In addition to the columns below, the suggestion columns are used to pass along the full * suggestion so it can be shortcutted. * * @hide */ public final static String SEARCH_CLICK_REPORT_AUTHORITY = "com.android.globalsearch.stats"; /** * The path the write goes to. * * @hide */ public final static String SEARCH_CLICK_REPORT_URI_PATH = "click"; /** * The column storing the query for the click. * * @hide */ public final static String SEARCH_CLICK_REPORT_COLUMN_QUERY = "query"; /** * The column storing the component name of the application that was pivoted into. * * @hide */ public final static String SEARCH_CLICK_REPORT_COLUMN_COMPONENT = "component"; /** * Column name for suggestions cursor. Unused - can be null or column can be omitted. */ public final static String SUGGEST_COLUMN_FORMAT = "suggest_format"; /** * Column name for suggestions cursor. Required. This is the primary line of text that * will be presented to the user as the suggestion. */ public final static String SUGGEST_COLUMN_TEXT_1 = "suggest_text_1"; /** * Column name for suggestions cursor. Optional. If your cursor includes this column, * then all suggestions will be provided in a two-line format. The second line of text is in * a much smaller appearance. */ public final static String SUGGEST_COLUMN_TEXT_2 = "suggest_text_2"; /** * Column name for suggestions cursor. Optional. If your cursor includes this column, * then all suggestions will be provided in a format that includes space for two small icons, * one at the left and one at the right of each suggestion. The data in the column must * be a resource ID of a drawable, or a URI in one of the following formats: * *

* * See {@link android.content.ContentResolver#openAssetFileDescriptor(Uri, String)} * for more information on these schemes. */ public final static String SUGGEST_COLUMN_ICON_1 = "suggest_icon_1"; /** * Column name for suggestions cursor. Optional. If your cursor includes this column, * then all suggestions will be provided in a format that includes space for two small icons, * one at the left and one at the right of each suggestion. The data in the column must * be a resource ID of a drawable, or a URI in one of the following formats: * * * * See {@link android.content.ContentResolver#openAssetFileDescriptor(Uri, String)} * for more information on these schemes. */ public final static String SUGGEST_COLUMN_ICON_2 = "suggest_icon_2"; /** * Column name for suggestions cursor. Optional. 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 android:searchSuggestIntentAction field in your XML metadata. 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 it using XML metadata and omit * it from the cursor. */ public final static String SUGGEST_COLUMN_INTENT_ACTION = "suggest_intent_action"; /** * Column name for suggestions cursor. Optional. 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 android:searchSuggestIntentData field in your XML metadata. 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 XML metadata and omit it from the cursor. */ public final static String SUGGEST_COLUMN_INTENT_DATA = "suggest_intent_data"; /** * Column name for suggestions cursor. Optional. 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 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 under the key EXTRA_DATA_KEY. * * @hide Pending API council approval. */ public final static String SUGGEST_COLUMN_INTENT_EXTRA_DATA = "suggest_intent_extra_data"; /** * Column name for suggestions cursor. Optional. This column allows suggestions * to provide additional arbitrary data which will be included as an extra under the key * {@link #COMPONENT_NAME_KEY}. For use by the global search system only - if other providers * attempt to use this column, the value will be overwritten by global search. * * @hide */ public final static String SUGGEST_COLUMN_INTENT_COMPONENT_NAME = "suggest_intent_component"; /** * Column name for suggestions cursor. Optional. 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 has already been set to an * appropriate base string. */ public final static String SUGGEST_COLUMN_INTENT_DATA_ID = "suggest_intent_data_id"; /** * Column name for suggestions cursor. Required if action is * {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH}, optional otherwise. 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. */ public final static String SUGGEST_COLUMN_QUERY = "suggest_intent_query"; /** * Column name for suggestions cursor. Optional. This column is used to indicate whether * a search suggestion should be stored as a shortcut, and whether it should be validated. If * missing, the result will be stored as a shortcut and never validated. If set to * {@link #SUGGEST_NEVER_MAKE_SHORTCUT}, the result will not be stored as a shortcut. * Otherwise, the shortcut id will be used to check back for validation via * {@link #SUGGEST_URI_PATH_SHORTCUT}. * * @hide Pending API council approval. */ public final static String SUGGEST_COLUMN_SHORTCUT_ID = "suggest_shortcut_id"; /** * Column name for suggestions cursor. Optional. This column is used to specify the * cursor item's background color if it needs a non-default background color. A non-zero value * indicates a valid background color to override the default. * * @hide For internal use, not part of the public API. */ public final static String SUGGEST_COLUMN_BACKGROUND_COLOR = "suggest_background_color"; /** * Column name for suggestions cursor. Optional. This column is used to specify * that a spinner should be shown in lieu of an icon2 while the shortcut of this suggestion * is being refreshed. * * @hide Pending API council approval. */ public final static String SUGGEST_COLUMN_SPINNER_WHILE_REFRESHING = "suggest_spinner_while_refreshing"; /** * Column value for suggestion column {@link #SUGGEST_COLUMN_SHORTCUT_ID} when a suggestion * should not be stored as a shortcut in global search. * * @hide Pending API council approval. */ public final static String SUGGEST_NEVER_MAKE_SHORTCUT = "_-1"; /** * If a suggestion has this value in {@link #SUGGEST_COLUMN_INTENT_ACTION}, * the search dialog will switch to a different suggestion source when the * suggestion is clicked. * * {@link #SUGGEST_COLUMN_INTENT_DATA} must contain * the flattened {@link ComponentName} of the activity which is to be searched. * * TODO: Should {@link #SUGGEST_COLUMN_INTENT_DATA} instead contain a URI in the format * used by {@link android.provider.Applications}? * * TODO: This intent should be protected by the same permission that we use * for replacing the global search provider. * * The query text field will be set to the value of {@link #SUGGEST_COLUMN_QUERY}. * * @hide Pending API council approval. */ public final static String INTENT_ACTION_CHANGE_SEARCH_SOURCE = "android.search.action.CHANGE_SEARCH_SOURCE"; /** * Intent action for finding the global search activity. * The global search provider should handle this intent. * * @hide Pending API council approval. */ public final static String INTENT_ACTION_GLOBAL_SEARCH = "android.search.action.GLOBAL_SEARCH"; /** * Intent action for starting the global search settings activity. * The global search provider should handle this intent. * * @hide Pending API council approval. */ public final static String INTENT_ACTION_SEARCH_SETTINGS = "android.search.action.SEARCH_SETTINGS"; /** * Intent action for starting a web search provider's settings activity. * Web search providers should handle this intent if they have provider-specific * settings to implement. * * @hide Pending API council approval. */ public final static String INTENT_ACTION_WEB_SEARCH_SETTINGS = "android.search.action.WEB_SEARCH_SETTINGS"; /** * Intent action broadcasted to inform that the searchables list or default have changed. * Components should handle this intent if they cache any searchable data and wish to stay * up to date on changes. * * @hide Pending API council approval. */ public final static String INTENT_ACTION_SEARCHABLES_CHANGED = "android.search.action.SEARCHABLES_CHANGED"; /** * If a suggestion has this value in {@link #SUGGEST_COLUMN_INTENT_ACTION}, * the search dialog will take no action. * * @hide */ public final static String INTENT_ACTION_NONE = "android.search.action.ZILCH"; /** * Reference to the shared system search service. */ private static ISearchManager mService; private final Context mContext; private int mIdent; // package private since they are used by the inner class SearchManagerCallback /* package */ boolean mIsShowing = false; /* package */ final Handler mHandler; /* package */ OnDismissListener mDismissListener = null; /* package */ OnCancelListener mCancelListener = null; private final SearchManagerCallback mSearchManagerCallback = new SearchManagerCallback(); /*package*/ SearchManager(Context context, Handler handler) { mContext = context; mHandler = handler; mService = ISearchManager.Stub.asInterface( ServiceManager.getService(Context.SEARCH_SERVICE)); } /*package*/ void setIdent(int ident) { if (mIdent != 0) { throw new IllegalStateException("mIdent already set"); } mIdent = ident; } /** * Launch search UI. * *

The search manager will open a search widget in an overlapping * window, and the underlying activity may be obscured. The search * entry state will remain in effect until one of the following events: *