Custom Suggestion Provider Code Snippets
satya - Sun Dec 09 2012 16:23:16 GMT-0500 (Eastern Standard Time)
Listing 12-5. Building a UriMatcher with SearchManager Suggest URI Definitions
private static UriMatcher buildUriMatcher(String AUTHORITY)
{
UriMatcher matcher =
new UriMatcher(UriMatcher.NO_MATCH);
matcher.addURI(AUTHORITY,
SearchManager.SUGGEST_URI_PATH_QUERY, SEARCH_SUGGEST);
matcher.addURI(AUTHORITY,
SearchManager.SUGGEST_URI_PATH_QUERY + "/*", SEARCH_SUGGEST);
matcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_SHORTCUT,
SHORTCUT_REFRESH);
matcher.addURI(AUTHORITY,
SearchManager.SUGGEST_URI_PATH_SHORTCUT + "/*",
SHORTCUT_REFRESH);
return matcher;
}
satya - Sun Dec 09 2012 16:27:05 GMT-0500 (Eastern Standard Time)
Listing 12-6. Outline of query() method using a UriMatcher
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder)
{
...other stuff
switch (sURIMatcher.match(uri))
{
case SEARCH_SUGGEST:
//Return a series of suggestions
case SHORTCUT_REFRESH:
//Return the updated suggestion
}//eof-switch
....other stuff
}
satya - Sun Dec 09 2012 16:28:31 GMT-0500 (Eastern Standard Time)
Listing 12-7. Implementing getType() for SuggestUrlProvider
?.other stuff
//Initialize the object below first
private static URIMatcher sURIMatcher;
?.otherstuff
public String getType(Uri uri) {
switch (sURIMatcher.match(uri)) {
case SEARCH_SUGGEST:
return SearchManager.SUGGEST_MIME_TYPE;
case SHORTCUT_REFRESH:
return SearchManager.SHORTCUT_MIME_TYPE;
default:
throw new IllegalArgumentException("Unknown URL " + uri);
}
}
satya - Sun Dec 09 2012 16:29:02 GMT-0500 (Eastern Standard Time)
Listing 12-8. SearchManager Mime Type Constants
SearchManager.SUGGEST_MIME_TYPE
SearchManager.SHORTCUT_MIME_TYPE
satya - Sun Dec 09 2012 16:29:26 GMT-0500 (Eastern Standard Time)
Listing 12-9. SearchManager Mime Type Constant Values
vnd.android.cursor.dir/vnd.android.search.suggest
vnd.android.cursor.item/vnd.android.search.suggest
satya - Sun Dec 09 2012 16:31:29 GMT-0500 (Eastern Standard Time)
Listing 12-10. Implementing query() method for SuggestUrlProvider
public Cursor query(Uri uri, String[] projection,
String selection,
String[] selectionArgs, String sortOrder)
{
Log.d(tag,"query called with uri:" + uri);
Log.d(tag,"selection:" + selection);
String query = selectionArgs[0];
Log.d(tag,"query:" + query);
switch (sURIMatcher.match(uri)) {
case SEARCH_SUGGEST:
Log.d(tag,"search suggest called");
return getSuggestions(query);
case SHORTCUT_REFRESH:
Log.d(tag,"shortcut refresh called");
return null;
default:
throw new IllegalArgumentException("Unknown URL " + uri);
}
}
satya - Sun Dec 09 2012 16:32:10 GMT-0500 (Eastern Standard Time)
Listing 12-11. CustomSuggestionProvider Searchableinfo Metadata XML file
//xml/searchable.xml
<searchable
xmlns:android="http://schemas.android.com/apk/res/android"
android:label="@string/search_label"
android:hint="@string/search_hint"
android:searchMode="showSearchLabelAsBadge"
android:searchSettingsDescription="suggests urls"
android:includeInGlobalSearch="true"
android:queryAfterZeroResults="true"
android:searchSuggestAuthority=
"com.androidbook.search.custom.suggesturlprovider"
android:searchSuggestIntentAction=
"android.intent.action.VIEW"
android:searchSuggestSelection=" ? "
/>
satya - Sun Dec 09 2012 16:33:18 GMT-0500 (Eastern Standard Time)
Listing 12-12. Defining Suggestion Cursor Columns for SuggestUrlProvider
private static final String[] COLUMNS = {
"_id", // must include this column
SearchManager.SUGGEST_COLUMN_TEXT_1,
SearchManager.SUGGEST_COLUMN_TEXT_2,
SearchManager.SUGGEST_COLUMN_INTENT_DATA,
SearchManager.SUGGEST_COLUMN_INTENT_ACTION,
SearchManager.SUGGEST_COLUMN_SHORTCUT_ID
}
satya - Sun Dec 09 2012 16:33:55 GMT-0500 (Eastern Standard Time)
Listing 12-13. Using a MatrixCursor
MatrixCursor cursor = new MatrixCursor(COLUMNS);
string[] rowData;
//insert values for each column in rowData
cursor.addRow(rowData);
satya - Sun Dec 09 2012 16:40:08 GMT-0500 (Eastern Standard Time)
Listing 12-14. Custom Suggestion Provider Full Source Code
public class SuggestUrlProvider extends ContentProvider
{
private static final String tag = "SuggestUrlProvider";
public static String AUTHORITY =
"com.androidbook.search.custom.suggesturlprovider";
private static final int SEARCH_SUGGEST = 0;
private static final int SHORTCUT_REFRESH = 1;
private static final UriMatcher sURIMatcher = buildUriMatcher();
private static final String[] COLUMNS = {
"_id", // must include this column
SearchManager.SUGGEST_COLUMN_TEXT_1,
SearchManager.SUGGEST_COLUMN_TEXT_2,
SearchManager.SUGGEST_COLUMN_INTENT_DATA,
SearchManager.SUGGEST_COLUMN_INTENT_ACTION,
SearchManager.SUGGEST_COLUMN_SHORTCUT_ID
};
private static UriMatcher buildUriMatcher()
{
UriMatcher matcher =
new UriMatcher(UriMatcher.NO_MATCH);
matcher.addURI(AUTHORITY,
SearchManager.SUGGEST_URI_PATH_QUERY,
SEARCH_SUGGEST);
matcher.addURI(AUTHORITY,
SearchManager.SUGGEST_URI_PATH_QUERY +
"/*",
SEARCH_SUGGEST);
matcher.addURI(AUTHORITY,
SearchManager.SUGGEST_URI_PATH_SHORTCUT,
SHORTCUT_REFRESH);
matcher.addURI(AUTHORITY,
SearchManager.SUGGEST_URI_PATH_SHORTCUT +
"/*",
SHORTCUT_REFRESH);
return matcher;
}
@Override
public boolean onCreate() {
//lets not do anything in particular
Log.d(tag,"onCreate called");
return true;
}
@Override
public Cursor query(Uri uri, String[] projection,
String selection, String[] selectionArgs,
String sortOrder)
{
Log.d(tag,"query called with uri:" + uri);
Log.d(tag,"selection:" + selection);
String query = selectionArgs[0];
Log.d(tag,"query:" + query);
switch (sURIMatcher.match(uri)) {
case SEARCH_SUGGEST:
Log.d(tag,"search suggest called");
return getSuggestions(query);
case SHORTCUT_REFRESH:
Log.d(tag,"shortcut refresh called");
return null;
default:
throw new IllegalArgumentException("Unknown URL " + uri);
}
}
private Cursor getSuggestions(String query)
{
if (query == null) return null;
String word = getWord(query);
if (word == null)
return null;
Log.d(tag,"query is longer than 3 letters");
MatrixCursor cursor = new MatrixCursor(COLUMNS);
cursor.addRow(createRow1(word));
cursor.addRow(createRow2(word));
return cursor;
}
private Object[] createRow1(String query)
{
return columnValuesOfQuery(query,
"android.intent.action.VIEW",
"http://www.thefreedictionary.com/" + query,
"Look up in freedictionary.com for",
query);
}
private Object[] createRow2(String query)
{
return columnValuesOfQuery(query,
"android.intent.action.VIEW",
"http://www.google.com/search?hl=en&source=hp&q=define%3A/"
+ query,
"Look up in google.com for",
query);
}
private Object[] columnValuesOfQuery(String query,
String intentAction,
String url,
String text1,
String text2)
{
return new String[] {
query, // _id
text1, // text1
text2, // text2
url,
// intent_data (included when clicking on item)
intentAction, //action
SearchManager.SUGGEST_NEVER_MAKE_SHORTCUT
};
}
private Cursor refreshShortcut(String shortcutId,
String[] projection) {
return null;
}
public String getType(Uri uri) {
switch (sURIMatcher.match(uri)) {
case SEARCH_SUGGEST:
return SearchManager.SUGGEST_MIME_TYPE;
case SHORTCUT_REFRESH:
return SearchManager.SHORTCUT_MIME_TYPE;
default:
throw
new IllegalArgumentException("Unknown URL " + uri);
}
}
public Uri insert(Uri uri, ContentValues values) {
throw new UnsupportedOperationException();
}
public int delete(Uri uri, String selection,
String[] selectionArgs) {
throw new UnsupportedOperationException();
}
public int update(Uri uri, ContentValues values,
String selection,
String[] selectionArgs) {
throw new UnsupportedOperationException();
}
private String getWord(String query)
{
int dotIndex = query.indexOf('.');
if (dotIndex < 0)
return null;
return query.substring(0,dotIndex);
}
}
satya - Sun Dec 09 2012 16:41:22 GMT-0500 (Eastern Standard Time)
Listing 12-16. Responding to Action_View and Action_Search
//Body of onCreate
// get and process search query here
final Intent queryIntent = getIntent();
//query action
final String queryAction = queryIntent.getAction();
if (Intent.ACTION_SEARCH.equals(queryAction))
{
this.doSearchQuery(queryIntent);
}
else if (Intent.ACTION_VIEW.equals(queryAction))
{
this.doView(queryIntent);
}
else {
Log.d(tag,"Create intent NOT from search");
}
satya - Sun Dec 09 2012 16:42:18 GMT-0500 (Eastern Standard Time)
Listing 12-17. Full source code of SearchActivity
//file: SearchActivity.java
public class SearchActivity extends Activity
{
private final static String tag ="SearchActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(tag,"I am being created");
setContentView(R.layout.layout_test_search_activity);
// get and process search query here
final Intent queryIntent = getIntent();
//query action
final String queryAction = queryIntent.getAction();
Log.d(tag,"Create Intent action:"+queryAction);
final String queryString =
queryIntent.getStringExtra(SearchManager.QUERY);
Log.d(tag,"Create Intent query:"+queryString);
if (Intent.ACTION_SEARCH.equals(queryAction))
{
this.doSearchQuery(queryIntent);
}
else if (Intent.ACTION_VIEW.equals(queryAction))
{
this.doView(queryIntent);
}
else {
Log.d(tag,"Create intent NOT from search");
}
return;
}
@Override
public void onNewIntent(final Intent newIntent)
{
super.onNewIntent(newIntent);
Log.d(tag,"new intent calling me");
// get and process search query here
final Intent queryIntent = newIntent;
//query action
final String queryAction = queryIntent.getAction();
Log.d(tag,"New Intent action:"+queryAction);
final String queryString =
queryIntent.getStringExtra(SearchManager.QUERY);
Log.d(tag,"New Intent query:"+queryString);
if (Intent.ACTION_SEARCH.equals(queryAction))
{
this.doSearchQuery(queryIntent);
}
else if (Intent.ACTION_VIEW.equals(queryAction))
{
this.doView(queryIntent);
}
else {
Log.d(tag,"New intent NOT from search");
}
return;
}
private void doSearchQuery(final Intent queryIntent)
{
final String queryString =
queryIntent.getStringExtra(SearchManager.QUERY);
appendText("You are searching for:" + queryString);
}
private void appendText(String msg)
{
TextView tv = (TextView)this.findViewById(R.id.text1);
tv.setText(tv.getText() + "\n" + msg);
}
private void doView(final Intent queryIntent)
{
Uri uri = queryIntent.getData();
String action = queryIntent.getAction();
Intent i = new Intent(action);
i.setData(uri);
startActivity(i);
this.finish();
}
}
satya - Sun Dec 09 2012 16:42:43 GMT-0500 (Eastern Standard Time)
The intent that gets fired to invoke the search activity
launching Intent {
act=android.intent.action.VIEW
dat=http://www.google.com
flg=0x10000000
cmp=com.androidbook.search.custom/.SearchActivity (has extras)
}
satya - Sun Dec 09 2012 16:43:39 GMT-0500 (Eastern Standard Time)
Listing 12-18. Responding to Action_Search and Action_View
if (Intent.ACTION_SEARCH.equals(queryAction))
{
this.doSearchQuery(queryIntent);
}
else if (Intent.ACTION_VIEW.equals(queryAction))
{
this.doView(queryIntent);
}
satya - Sun Dec 09 2012 16:45:37 GMT-0500 (Eastern Standard Time)
Listing 12-20. Finishing the Search Activity
private void doView(final Intent queryIntent)
{
Uri uri = queryIntent.getData();
String action = queryIntent.getAction();
Intent i = new Intent(action);
i.setData(uri);
startActivity(i);
this.finish();
}
satya - Sun Dec 09 2012 16:46:12 GMT-0500 (Eastern Standard Time)
Listing 12-21. Custom Suggestion Provider Manifest File
//file:AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.androidbook.search.custom"
android:versionCode="1"
android:versionName="1.0.0">
<application android:icon="@drawable/icon"
android:label="Custom Suggestions Provider">
<!--
****************************************************************
* Search related code: search activity
****************************************************************
-->
<activity android:name=".SearchActivity"
android:label="Search Activity Label"
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>
<!-- Declare default search -->
<meta-data android:name="android.app.default_searchable"
android:value=".SearchActivity" />
<!-- Declare Suggestion Provider -->
<provider android:name="SuggestUrlProvider"
android:authorities=
"com.androidbook.search.custom.suggesturlprovider" />
</application>
<uses-sdk android:minSdkVersion="4" />
</manifest>
satya - Sun Dec 09 2012 16:46:52 GMT-0500 (Eastern Standard Time)
Listing 12-22. List of Action Key Codes
keycode_dpad_up
keycode_dpad_down
keycode_dpad_left
keycode_dpad_right
keycode_dpad_center
keycode_back
keycode_call
keycode_camera
keycode_clear
kecode_endcall
keycode_home
keycode_menu
keycode_mute
keycode_power
keycode_search
keycode_volume_up
keycode_volume_down
satya - Sun Dec 09 2012 16:47:08 GMT-0500 (Eastern Standard Time)
More on key codes
satya - Sun Dec 09 2012 16:47:39 GMT-0500 (Eastern Standard Time)
Listing 12-23. Action Key Definition Example
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
android:label="@string/search_label"
android:hint="@string/search_hint"
android:searchMode="showSearchLabelAsBadge"
android:includeInGlobalSearch="true"
android:searchSuggestAuthority=
"com.androidbook.search.simplesp.SimpleSuggestionProvider"
android:searchSuggestSelection=" ? "
>
<actionkey
android:keycode="KEYCODE_CALL"
android:queryActionMsg="call"
android:suggestActionMsg="call"
android:suggestActionMsgColumn="call_column" />
<actionkey
android:keycode="KEYCODE_DPAD_CENTER"
android:queryActionMsg="doquery"
android:suggestActionMsg="dosuggest"
android:suggestActionMsgColumn="my_column" />
.....
</searchable>
satya - Sun Dec 09 2012 16:48:20 GMT-0500 (Eastern Standard Time)
More info on action keys at google
satya - Sun Dec 09 2012 16:49:56 GMT-0500 (Eastern Standard Time)
Listing 12-24. Example of Action Key Columns in the Suggestion Cursor
private static final String[] COLUMNS = {
"_id", // must include this column
SearchManager.SUGGEST_COLUMN_TEXT_1,
SearchManager.SUGGEST_COLUMN_TEXT_2,
SearchManager.SUGGEST_COLUMN_INTENT_DATA,
SearchManager.SUGGEST_COLUMN_INTENT_ACTION,
SearchManager.SUGGEST_COLUMN_SHORTCUT_ID,
"call_column",
"my_column"
};
satya - Sun Dec 09 2012 16:50:19 GMT-0500 (Eastern Standard Time)
Listing 12-25. Passing Additional Application data to Search Activity
public boolean onSearchRequested()
{
Bundle applicationData = new Bundle();
applicationData.putString("string_key","some string value");
applicationData.putLong("long_key",290904);
applicationData.putFloat("float_key",2.0f);
startSearch(null, // Initial Search search query string
false, // don't "select initial query"
applicationData, // extra data
false // don't force a global search
);
return true;
}
satya - Sun Dec 09 2012 16:51:36 GMT-0500 (Eastern Standard Time)
Listing 12-26. Retrieving Additional Application Data Context
Bundle applicationData =
queryIntent.getBundleExtra(SearchManager.APP_DATA);
if (applicationData != null)
{
String s = applicationData.getString("string_key");
long l = applicationData.getLong("long_key");
float f = applicationData.getFloat("float_key");
}