Use system default web search & suggestions providers.
- Web search was hard coded to Google and suggestions in the search dialog were hard coded to the GoogleSearch suggest provider package. Both now point to the system default web search/suggest provider which can include third party search engines.
- I also removed the intent filter to handle action.WEB_SEARCH from the browser because it should no longer provide web search functionality for other apps, that feature is provided by the recently added WebSearchProvider package. Removing this intent filter also removes the browser from the list of web search providers in the system settings.
- As part of this change I had to factor out the search shortcut code to a separate function, add a couple of new functions and modify the browser provider code to access cursor fields in a safe manner.
diff --git a/src/com/android/browser/BrowserActivity.java b/src/com/android/browser/BrowserActivity.java
index cf3528e..564620e 100644
--- a/src/com/android/browser/BrowserActivity.java
+++ b/src/com/android/browser/BrowserActivity.java
@@ -162,6 +162,13 @@
private SensorManager mSensorManager = null;
+ // These are single-character shortcuts for searching popular sources.
+ private static final int SHORTCUT_INVALID = 0;
+ private static final int SHORTCUT_GOOGLE_SEARCH = 1;
+ private static final int SHORTCUT_WIKIPEDIA_SEARCH = 2;
+ private static final int SHORTCUT_DICTIONARY_SEARCH = 3;
+ private static final int SHORTCUT_GOOGLE_MOBILE_LOCAL_SEARCH = 4;
+
/* Whitelisted webpages
private static HashSet<String> sWhiteList;
@@ -684,6 +691,12 @@
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Browser");
+ // If this was a web search request, pass it on to the default web search provider.
+ if (handleWebSearchIntent(getIntent())) {
+ moveTaskToBack(true);
+ return;
+ }
+
if (!mTabControl.restoreState(icicle)) {
// clear up the thumbnail directory if we can't restore the state as
// none of the files in the directory are referenced any more.
@@ -782,6 +795,12 @@
|| Intent.ACTION_SEARCH.equals(action)
|| MediaStore.INTENT_ACTION_MEDIA_SEARCH.equals(action)
|| Intent.ACTION_WEB_SEARCH.equals(action)) {
+ // If this was a search request (e.g. search query directly typed into the address bar),
+ // pass it on to the default web search provider.
+ if (handleWebSearchIntent(intent)) {
+ return;
+ }
+
String url = getUrlFromIntent(intent);
if (url == null || url.length() == 0) {
url = mSettings.getHomePage();
@@ -845,6 +864,71 @@
}
}
+ private int parseUrlShortcut(String url) {
+ if (url == null) return SHORTCUT_INVALID;
+
+ // FIXME: quick search, need to be customized by setting
+ if (url.length() > 2 && url.charAt(1) == ' ') {
+ switch (url.charAt(0)) {
+ case 'g': return SHORTCUT_GOOGLE_SEARCH;
+ case 'w': return SHORTCUT_WIKIPEDIA_SEARCH;
+ case 'd': return SHORTCUT_DICTIONARY_SEARCH;
+ case 'l': return SHORTCUT_GOOGLE_MOBILE_LOCAL_SEARCH;
+ }
+ }
+ return SHORTCUT_INVALID;
+ }
+
+ /**
+ * Launches the default web search activity with the query parameters if the given intent's data
+ * are identified as plain search terms and not URLs/shortcuts.
+ * @return true if the intent was handled and web search activity was launched, false if not.
+ */
+ private boolean handleWebSearchIntent(Intent intent) {
+ if (intent == null) return false;
+
+ String url = null;
+ final String action = intent.getAction();
+ if (Intent.ACTION_VIEW.equals(action)) {
+ url = intent.getData().toString();
+ } else if (Intent.ACTION_SEARCH.equals(action)
+ || MediaStore.INTENT_ACTION_MEDIA_SEARCH.equals(action)
+ || Intent.ACTION_WEB_SEARCH.equals(action)) {
+ url = intent.getStringExtra(SearchManager.QUERY);
+ }
+ return handleWebSearchRequest(url);
+ }
+
+ /**
+ * Launches the default web search activity with the query parameters if the given url string
+ * was identified as plain search terms and not URL/shortcut.
+ * @return true if the request was handled and web search activity was launched, false if not.
+ */
+ private boolean handleWebSearchRequest(String inUrl) {
+ if (inUrl == null) return false;
+
+ // In general, we shouldn't modify URL from Intent.
+ // But currently, we get the user-typed URL from search box as well.
+ String url = fixUrl(inUrl).trim();
+
+ // URLs and site specific search shortcuts are handled by the regular flow of control, so
+ // return early.
+ if (Regex.WEB_URL_PATTERN.matcher(url).matches()
+ || parseUrlShortcut(url) != SHORTCUT_INVALID) {
+ return false;
+ }
+
+ Browser.updateVisitedHistory(mResolver, url, false);
+ Browser.addSearchUrl(mResolver, url);
+
+ Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
+ intent.addCategory(Intent.CATEGORY_DEFAULT);
+ intent.putExtra(SearchManager.QUERY, url);
+ startActivity(intent);
+
+ return true;
+ }
+
private String getUrlFromIntent(Intent intent) {
String url = null;
if (intent != null) {
@@ -4428,34 +4512,22 @@
return inUrl;
}
if (hasSpace) {
- // FIXME: quick search, need to be customized by setting
- if (inUrl.length() > 2 && inUrl.charAt(1) == ' ') {
- // FIXME: Is this the correct place to add to searches?
- // what if someone else calls this function?
- char char0 = inUrl.charAt(0);
-
- if (char0 == 'g') {
- Browser.addSearchUrl(mResolver, inUrl);
- return composeSearchUrl(inUrl.substring(2));
-
- } else if (char0 == 'w') {
- Browser.addSearchUrl(mResolver, inUrl);
- return URLUtil.composeSearchUrl(inUrl.substring(2),
- QuickSearch_W,
- QUERY_PLACE_HOLDER);
-
- } else if (char0 == 'd') {
- Browser.addSearchUrl(mResolver, inUrl);
- return URLUtil.composeSearchUrl(inUrl.substring(2),
- QuickSearch_D,
- QUERY_PLACE_HOLDER);
-
- } else if (char0 == 'l') {
- Browser.addSearchUrl(mResolver, inUrl);
+ // FIXME: Is this the correct place to add to searches?
+ // what if someone else calls this function?
+ int shortcut = parseUrlShortcut(inUrl);
+ if (shortcut != SHORTCUT_INVALID) {
+ Browser.addSearchUrl(mResolver, inUrl);
+ String query = inUrl.substring(2);
+ switch (shortcut) {
+ case SHORTCUT_GOOGLE_SEARCH:
+ return composeSearchUrl(query);
+ case SHORTCUT_WIKIPEDIA_SEARCH:
+ return URLUtil.composeSearchUrl(query, QuickSearch_W, QUERY_PLACE_HOLDER);
+ case SHORTCUT_DICTIONARY_SEARCH:
+ return URLUtil.composeSearchUrl(query, QuickSearch_D, QUERY_PLACE_HOLDER);
+ case SHORTCUT_GOOGLE_MOBILE_LOCAL_SEARCH:
// FIXME: we need location in this case
- return URLUtil.composeSearchUrl(inUrl.substring(2),
- QuickSearch_L,
- QUERY_PLACE_HOLDER);
+ return URLUtil.composeSearchUrl(query, QuickSearch_L, QUERY_PLACE_HOLDER);
}
}
} else {
diff --git a/src/com/android/browser/BrowserProvider.java b/src/com/android/browser/BrowserProvider.java
index f8051a4..d2f4a0a 100644
--- a/src/com/android/browser/BrowserProvider.java
+++ b/src/com/android/browser/BrowserProvider.java
@@ -28,6 +28,8 @@
import android.content.SharedPreferences;
import android.content.UriMatcher;
import android.content.SharedPreferences.Editor;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.database.AbstractCursor;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
@@ -62,7 +64,7 @@
private static final String[] SUGGEST_PROJECTION = new String[] {
"_id", "url", "title", "bookmark"
};
- private static final String SUGGEST_SELECTION =
+ private static final String SUGGEST_SELECTION =
"url LIKE ? OR url LIKE ? OR url LIKE ? OR url LIKE ?";
private String[] SUGGEST_ARGS = new String[4];
@@ -138,11 +140,11 @@
// 17 -> 18 Added favicon in bookmarks table for Home shortcuts
// 18 -> 19 Remove labels table
private static final int DATABASE_VERSION = 19;
-
+
// Regular expression which matches http://, followed by some stuff, followed by
// optionally a trailing slash, all matched as separate groups.
private static final Pattern STRIP_URL_PATTERN = Pattern.compile("^(http://)(.*?)(/$)?");
-
+
// The hex color string to be applied to urls of website suggestions, as derived from
// the current theme. This is not set until/unless beautifyUrl is called, at which point
// this variable caches the color value.
@@ -150,12 +152,12 @@
public BrowserProvider() {
}
-
+
private static CharSequence replaceSystemPropertyInString(Context context, CharSequence srcString) {
StringBuffer sb = new StringBuffer();
int lastCharLoc = 0;
-
+
final String client_id = Partner.getString(context.getContentResolver(), Partner.CLIENT_ID);
for (int i = 0; i < srcString.length(); ++i) {
@@ -217,7 +219,7 @@
CharSequence bookmarkDestination = replaceSystemPropertyInString(mContext, bookmarks[i + 1]);
db.execSQL("INSERT INTO bookmarks (title, url, visits, " +
"date, created, bookmark)" + " VALUES('" +
- bookmarks[i] + "', '" + bookmarkDestination +
+ bookmarks[i] + "', '" + bookmarkDestination +
"', 0, 0, 0, 1);");
}
} catch (ArrayIndexOutOfBoundsException e) {
@@ -248,7 +250,7 @@
public boolean onCreate() {
final Context context = getContext();
mOpenHelper = new DatabaseHelper(context);
- // we added "picasa web album" into default bookmarks for version 19.
+ // we added "picasa web album" into default bookmarks for version 19.
// To avoid erasing the bookmark table, we added it explicitly for
// version 18 and 19 as in the other cases, we will erase the table.
if (DATABASE_VERSION == 18 || DATABASE_VERSION == 19) {
@@ -289,9 +291,9 @@
* Subclass AbstractCursor so we can combine multiple Cursors and add
* "Google Search".
* Here are the rules.
- * 1. We only have MAX_SUGGESTION_LONG_ENTRIES in the list plus
+ * 1. We only have MAX_SUGGESTION_LONG_ENTRIES in the list plus
* "Google Search";
- * 2. If bookmark/history entries are less than
+ * 2. If bookmark/history entries are less than
* (MAX_SUGGESTION_SHORT_ENTRIES -1), we include Google suggest.
*/
private class MySuggestionCursor extends AbstractCursor {
@@ -301,6 +303,9 @@
private int mSuggestionCount;
private boolean mBeyondCursor;
private String mString;
+ private int mSuggestText1Id;
+ private int mSuggestText2Id;
+ private int mSuggestQueryId;
public MySuggestionCursor(Cursor hc, Cursor sc, String string) {
mHistoryCursor = hc;
@@ -312,6 +317,22 @@
}
mString = string;
mBeyondCursor = false;
+
+ // Some web suggest providers only give suggestions and have no description string for
+ // items. The order of the result columns may be different as well. So retrieve the
+ // column indices for the fields we need now and check before using below.
+ if (mSuggestCursor == null) {
+ mSuggestText1Id = -1;
+ mSuggestText2Id = -1;
+ mSuggestQueryId = -1;
+ } else {
+ mSuggestText1Id = mSuggestCursor.getColumnIndex(
+ SearchManager.SUGGEST_COLUMN_TEXT_1);
+ mSuggestText2Id = mSuggestCursor.getColumnIndex(
+ SearchManager.SUGGEST_COLUMN_TEXT_2);
+ mSuggestQueryId = mSuggestCursor.getColumnIndex(
+ SearchManager.SUGGEST_COLUMN_QUERY);
+ }
}
@Override
@@ -344,7 +365,7 @@
public String[] getColumnNames() {
return COLUMNS;
}
-
+
@Override
public String getString(int columnIndex) {
if ((mPos != -1 && mHistoryCursor != null)) {
@@ -367,7 +388,8 @@
if (mHistoryCount > mPos) {
return getHistoryTitle();
} else if (!mBeyondCursor) {
- return mSuggestCursor.getString(1);
+ if (mSuggestText1Id == -1) return null;
+ return mSuggestCursor.getString(mSuggestText1Id);
} else {
return mString;
}
@@ -376,7 +398,8 @@
if (mHistoryCount > mPos) {
return getHistorySubtitle();
} else if (!mBeyondCursor) {
- return mSuggestCursor.getString(2);
+ if (mSuggestText2Id == -1) return null;
+ return mSuggestCursor.getString(mSuggestText2Id);
} else {
return getContext().getString(R.string.search_the_web);
}
@@ -405,11 +428,12 @@
if (mHistoryCount > mPos) {
return null;
} else if (!mBeyondCursor) {
- return mSuggestCursor.getString(3);
+ if (mSuggestQueryId == -1) return null;
+ return mSuggestCursor.getString(mSuggestQueryId);
} else {
return mString;
}
-
+
case SUGGEST_COLUMN_FORMAT:
return "html";
}
@@ -478,11 +502,11 @@
mSuggestCursor = null;
}
}
-
+
/**
* Provides the title (text line 1) for a browser suggestion, which should be the
* webpage title. If the webpage title is empty, returns the stripped url instead.
- *
+ *
* @return the title string to use
*/
private String getHistoryTitle() {
@@ -492,12 +516,12 @@
}
return title;
}
-
+
/**
* Provides the subtitle (text line 2) for a browser suggestion, which should be the
* webpage url. If the webpage title is empty, then the url should go in the title
* instead, and the subtitle should be empty, so this would return null.
- *
+ *
* @return the subtitle string to use, or null if none
*/
private String getHistorySubtitle() {
@@ -508,7 +532,7 @@
return beautifyUrl(mHistoryCursor.getString(1 /* url */));
}
}
-
+
/**
* Strips "http://" from the beginning of a url and "/" from the end,
* and adds html formatting to make it green.
@@ -520,19 +544,19 @@
getContext().getTheme().resolveAttribute(
com.android.internal.R.attr.textColorSearchUrl, colorValue, true);
int color = getContext().getResources().getColor(colorValue.resourceId);
-
+
// Convert the int color value into a hex string, and strip the first two
// characters which will be the alpha transparency (html doesn't want this).
mSearchUrlColorHex = Integer.toHexString(color).substring(2);
}
-
+
return "<font color=\"#" + mSearchUrlColorHex + "\">" + stripUrl(url) + "</font>";
}
}
@Override
public Cursor query(Uri url, String[] projectionIn, String selection,
- String[] selectionArgs, String sortOrder)
+ String[] selectionArgs, String sortOrder)
throws IllegalStateException {
SQLiteDatabase db = mOpenHelper.getReadableDatabase();
@@ -575,15 +599,20 @@
// get Google suggest if there is still space in the list
if (myArgs != null && myArgs.length > 1
&& c.getCount() < (MAX_SUGGESTION_SHORT_ENTRIES - 1)) {
- // TODO: This shouldn't be hard-coded. Instead, it should use the
- // default web search provider. But the API for that is not implemented yet.
- ComponentName googleSearchComponent =
- new ComponentName("com.android.googlesearch",
- "com.android.googlesearch.GoogleSearch");
- SearchableInfo si =
- SearchManager.getSearchableInfo(googleSearchComponent, false);
- Cursor sc = SearchManager.getSuggestions(getContext(), si, selectionArgs[0]);
- return new MySuggestionCursor(c, sc, selectionArgs[0]);
+ Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
+ intent.addCategory(Intent.CATEGORY_DEFAULT);
+ ResolveInfo info = getContext().getPackageManager().resolveActivity(
+ intent, PackageManager.MATCH_DEFAULT_ONLY);
+ if (info != null) {
+ ComponentName googleSearchComponent =
+ new ComponentName(info.activityInfo.packageName,
+ info.activityInfo.name);
+ SearchableInfo si =
+ SearchManager.getSearchableInfo(googleSearchComponent, false);
+ Cursor sc = SearchManager.getSuggestions(
+ getContext(), si, selectionArgs[0]);
+ return new MySuggestionCursor(c, sc, selectionArgs[0]);
+ }
}
return new MySuggestionCursor(c, null, selectionArgs[0]);
}
@@ -735,14 +764,14 @@
getContext().getContentResolver().notifyChange(url, null);
return ret;
}
-
+
/**
* Strips the provided url of preceding "http://" and any trailing "/". Does not
* strip "https://". If the provided string cannot be stripped, the original string
* is returned.
- *
+ *
* TODO: Put this in TextUtils to be used by other packages doing something similar.
- *
+ *
* @param url a url to strip, like "http://www.google.com/"
* @return a stripped url like "www.google.com", or the original string if it could
* not be stripped