Quick Search Box is a feature introduced in Android 1.6 that I lauded in a previous post. If you are an Android developer, the good news is you can make QSB suggest results from within your applications.
The purpose of this post is to provide a human-understandable overview of the process that will allow you to integrate your application with QSB quickly and painlessly.
To get the job done you need to supply four things: an xml/searchable.xml file, a few entries in your manifest, a content provider that QSB will query for the data, and an activity to handle the taps on your suggestions. In addition to that you'll need to somehow communicate to your users that your application is searchable so that they enable it for search on their phones.
searchable.xml
This file, usually named as above and found in res/xml directory, seems to be the logical beginning of the story. It should look similar to this one:
<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android" android:label="@string/search_label"
android:searchSettingsDescription="@string/search_settings_description" android:includeInGlobalSearch="true"
android:searchSuggestIntentData="item" android:searchSuggestAuthority="com.burnayev.actioncomplete.search">
</searchable>
android:label defines the text that a user will see as the name of your application under More results... when QSB spews out its suggestions.
android:searchSettingsDescription is used as the description of your searchable items in Settings/Search/Searchable items (this is the place where your users will need to navigate to enable your app for QSB suggestions). This is what goes into the second line of the row. What goes into the first line (i.e. denotes the name of your application) you'll know in a moment.
android:includeInGlobalSearch="true" is the magic word that makes QSB care about your efforts.
android:searchSuggestAuthority tells "the system" what content provider to look for to get the search results from your application.
The Manifest Magic
You application's manifest has to declare two things: the content provider and the activity that's going to handle the taps on your suggestions in QSB.
The provider declaration looks like this:
<provider android:name=".SearchProvider" android:authorities="com.burnayev.actioncomplete.search"
android:syncable="false" />
and defines the provider class name (android:name) as well as the "authority" (android:authorities), which is an identifier that the system uses to figure out who provides what kind of data.
<activity android:name=".activity.GlobalSearchHandler" android:label="@string/app_name">
<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>
The snippet above says that GlobalSearchHandler (android:name) is going to handle QSB suggestion taps (intent filter android.intent.action.SEARCH).
Now the drum roll, please... android:label defines the name of your application in Settings/Search/Searchable items. And you thought it wasn't straightforward...
The Content Provider
This is the centerpiece of the story. It goes like this:
public class SearchProvider extends ContentProvider {
public static final String AUTHORITY = "com.burnayev.actioncomplete.search";
private static final String[] COLUMN_NAMES = new String[] { "_id", SearchManager.SUGGEST_COLUMN_TEXT_1,
SearchManager.SUGGEST_COLUMN_TEXT_2, SearchManager.SUGGEST_COLUMN_INTENT_ACTION,
SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID };
private static final int SEARCH_SUGGEST = 0;
private static UriMatcher uriMatcher;
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY, SEARCH_SUGGEST);
}
@Override
public boolean onCreate() {
searchDAO = new SearchDAO(getContext());
return true;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
String searchString = uri.getLastPathSegment();
MatrixCursor cursor = new MatrixCursor(COLUMN_NAMES);
List<Action> actions = // get your stuff, e.g. query a database or something
for (Action action : actions) {
Object[] rowObject = new Object[] { action.getId(), action.getName(), action.getProjectName(),
Constants.ACTION_ACTION_DETAILS, action.getId() };
cursor.addRow(rowObject);
}
return cursor;
}
@Override
public String getType(Uri uri) {
switch (uriMatcher.match(uri)) {
case SEARCH_SUGGEST:
return SearchManager.SUGGEST_MIME_TYPE;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
}
@Override
public Uri insert(Uri uri, ContentValues values) {
throw new UnsupportedOperationException();
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
throw new UnsupportedOperationException();
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
throw new UnsupportedOperationException();
}
}
Notice the AUTHORITY constant. QSB looks at authorities in searchable.xml and queries a matching provider.
COLUMN_NAMES array is used to define both the way your search suggestions are presented in QSB (SUGGEST_COLUMN_TEXT_1, SUGGEST_COLUMN_TEXT_2) and the manner a suggestion tap is processed by your application (SUGGEST_COLUMN_INTENT_ACTION, SUGGEST_COLUMN_INTENT_DATA_ID).
The query method is where you perform the real search within your application, build a Cursor, and return it for further processing by QSB.
You gotta decide on a communication mechanism between your search provider and suggestion taps handler. In this example I use SUGGEST_COLUMN_INTENT_ACTION to specify the intent and SUGGEST_COLUMN_INTENT_DATA_ID to specify the particular item id.
Handling Suggestion Taps
Last but not least you have to decide what to do with the taps on the QSB suggestions for your application. A possible approach is illustrated below:
public class GlobalSearchHandler extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
String intentAction = intent.getAction();
Uri intentData = intent.getData();
Long id = null;
try {
id = intentData.getLastPathSegment() != null ? Long.valueOf(intentData.getLastPathSegment()) : null;
} catch (NumberFormatException e) {
// null id may be just fine or it may be not
}
if (id != null) {
Intent targetIntent = new Intent(intentAction);
// configure the intent based on the retrieved id
startActivity(targetIntent);
}
finish();
}
}
The noteworthy part of the code snippet above is this:
String intentAction = intent.getAction();
Uri intentData = intent.getData();
Both intent action and intent data are based on the cursor fields you set in your search provider. They allow you to properly delegate further processing to the appropriate part of your application.
By now you should have a working prototype of your application nicely integrated with Android Quick Search Box. It's time to tell your users to go to Settings/Search/Searchable items and enable your application for QSB search suggestions.
0 comments:
Post a Comment