OSDN Git Service

Remove "Show web suggestions" from browser settings
[android-x86/packages-apps-Browser.git] / src / com / android / browser / BrowserProvider.java
index 4db1ebc..6c9a769 100644 (file)
 
 package com.android.browser;
 
-import com.google.android.providers.GoogleSettings.Partner;
+import com.android.browser.search.SearchEngine;
 
 import android.app.SearchManager;
-import android.backup.BackupManager;
-import android.content.ComponentName;
+import android.app.backup.BackupManager;
 import android.content.ContentProvider;
 import android.content.ContentResolver;
 import android.content.ContentUris;
@@ -28,27 +27,25 @@ import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
 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.content.UriMatcher;
 import android.database.AbstractCursor;
-import android.database.ContentObserver;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteOpenHelper;
 import android.net.Uri;
-import android.os.Handler;
+import android.os.Process;
 import android.preference.PreferenceManager;
 import android.provider.Browser;
-import android.provider.Settings;
 import android.provider.Browser.BookmarkColumns;
-import android.server.search.SearchableInfo;
+import android.speech.RecognizerResultsIntent;
 import android.text.TextUtils;
-import android.text.util.Regex;
 import android.util.Log;
-import android.util.TypedValue;
+import android.util.Patterns;
 
+import java.io.File;
+import java.io.FilenameFilter;
+import java.util.ArrayList;
 import java.util.Date;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -69,11 +66,11 @@ public class BrowserProvider extends ContentProvider {
         "bookmarks", "searches"
     };
     private static final String[] SUGGEST_PROJECTION = new String[] {
-            "_id", "url", "title", "bookmark"
+            "_id", "url", "title", "bookmark", "user_entered"
     };
     private static final String SUGGEST_SELECTION =
-            "url LIKE ? OR url LIKE ? OR url LIKE ? OR url LIKE ?"
-                + " OR title LIKE ?";
+            "(url LIKE ? OR url LIKE ? OR url LIKE ? OR url LIKE ?"
+                + " OR title LIKE ?) AND (bookmark = 1 OR user_entered = 1)";
     private String[] SUGGEST_ARGS = new String[5];
 
     // shared suggestion array index, make sure to match COLUMNS
@@ -81,10 +78,11 @@ public class BrowserProvider extends ContentProvider {
     private static final int SUGGEST_COLUMN_INTENT_DATA_ID = 2;
     private static final int SUGGEST_COLUMN_TEXT_1_ID = 3;
     private static final int SUGGEST_COLUMN_TEXT_2_ID = 4;
-    private static final int SUGGEST_COLUMN_ICON_1_ID = 5;
-    private static final int SUGGEST_COLUMN_ICON_2_ID = 6;
-    private static final int SUGGEST_COLUMN_QUERY_ID = 7;
-    private static final int SUGGEST_COLUMN_FORMAT = 8;
+    private static final int SUGGEST_COLUMN_TEXT_2_URL_ID = 5;
+    private static final int SUGGEST_COLUMN_ICON_1_ID = 6;
+    private static final int SUGGEST_COLUMN_ICON_2_ID = 7;
+    private static final int SUGGEST_COLUMN_QUERY_ID = 8;
+    private static final int SUGGEST_COLUMN_INTENT_EXTRA_DATA = 9;
 
     // shared suggestion columns
     private static final String[] COLUMNS = new String[] {
@@ -93,10 +91,11 @@ public class BrowserProvider extends ContentProvider {
             SearchManager.SUGGEST_COLUMN_INTENT_DATA,
             SearchManager.SUGGEST_COLUMN_TEXT_1,
             SearchManager.SUGGEST_COLUMN_TEXT_2,
+            SearchManager.SUGGEST_COLUMN_TEXT_2_URL,
             SearchManager.SUGGEST_COLUMN_ICON_1,
             SearchManager.SUGGEST_COLUMN_ICON_2,
             SearchManager.SUGGEST_COLUMN_QUERY,
-            SearchManager.SUGGEST_COLUMN_FORMAT};
+            SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA};
 
     private static final int MAX_SUGGESTION_SHORT_ENTRIES = 3;
     private static final int MAX_SUGGESTION_LONG_ENTRIES = 6;
@@ -152,29 +151,44 @@ public class BrowserProvider extends ContentProvider {
     // 19 -> 20 Added thumbnail
     // 20 -> 21 Added touch_icon
     // 21 -> 22 Remove "clientid"
-    private static final int DATABASE_VERSION = 22;
+    // 22 -> 23 Added user_entered
+    private static final int DATABASE_VERSION = 23;
 
     // 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://)(.*?)(/$)?");
 
-    private SearchManager mSearchManager;
-
-    // The ID of the ColorStateList 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.
-    private static String mSearchUrlColorId;
+    private BrowserSettings mSettings;
 
     public BrowserProvider() {
     }
 
+    // XXX: This is a major hack to remove our dependency on gsf constants and
+    // its content provider. http://b/issue?id=2425179
+    static String getClientId(ContentResolver cr) {
+        String ret = "android-google";
+        Cursor c = null;
+        try {
+            c = cr.query(Uri.parse("content://com.google.settings/partner"),
+                    new String[] { "value" }, "name='client_id'", null, null);
+            if (c != null && c.moveToNext()) {
+                ret = c.getString(0);
+            }
+        } catch (RuntimeException ex) {
+            // fall through to return the default
+        } finally {
+            if (c != null) {
+                c.close();
+            }
+        }
+        return ret;
+    }
 
     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, "android-google");
+        final String client_id = getClientId(context.getContentResolver());
 
         for (int i = 0; i < srcString.length(); ++i) {
             char c = srcString.charAt(i);
@@ -226,7 +240,8 @@ public class BrowserProvider extends ContentProvider {
                     "bookmark INTEGER," +
                     "favicon BLOB DEFAULT NULL," +
                     "thumbnail BLOB DEFAULT NULL," +
-                    "touch_icon BLOB DEFAULT NULL" +
+                    "touch_icon BLOB DEFAULT NULL," +
+                    "user_entered INTEGER" +
                     ");");
 
             final CharSequence[] bookmarks = mContext.getResources()
@@ -265,12 +280,64 @@ public class BrowserProvider extends ContentProvider {
             }
             if (oldVersion < 22) {
                 db.execSQL("DELETE FROM bookmarks WHERE (bookmark = 0 AND url LIKE \"%.google.%client=ms-%\")");
+                removeGears();
+            }
+            if (oldVersion < 23) {
+                db.execSQL("ALTER TABLE bookmarks ADD COLUMN user_entered INTEGER;");
             } else {
                 db.execSQL("DROP TABLE IF EXISTS bookmarks");
                 db.execSQL("DROP TABLE IF EXISTS searches");
                 onCreate(db);
             }
         }
+
+        private void removeGears() {
+            new Thread() {
+                @Override
+                public void run() {
+                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+                    String browserDataDirString = mContext.getApplicationInfo().dataDir;
+                    final String appPluginsDirString = "app_plugins";
+                    final String gearsPrefix = "gears";
+                    File appPluginsDir = new File(browserDataDirString + File.separator
+                            + appPluginsDirString);
+                    if (!appPluginsDir.exists()) {
+                        return;
+                    }
+                    // Delete the Gears plugin files
+                    File[] gearsFiles = appPluginsDir.listFiles(new FilenameFilter() {
+                        public boolean accept(File dir, String filename) {
+                            return filename.startsWith(gearsPrefix);
+                        }
+                    });
+                    for (int i = 0; i < gearsFiles.length; ++i) {
+                        if (gearsFiles[i].isDirectory()) {
+                            deleteDirectory(gearsFiles[i]);
+                        } else {
+                            gearsFiles[i].delete();
+                        }
+                    }
+                    // Delete the Gears data files
+                    File gearsDataDir = new File(browserDataDirString + File.separator
+                            + gearsPrefix);
+                    if (!gearsDataDir.exists()) {
+                        return;
+                    }
+                    deleteDirectory(gearsDataDir);
+                }
+
+                private void deleteDirectory(File currentDir) {
+                    File[] files = currentDir.listFiles();
+                    for (int i = 0; i < files.length; ++i) {
+                        if (files[i].isDirectory()) {
+                            deleteDirectory(files[i]);
+                        }
+                        files[i].delete();
+                    }
+                    currentDir.delete();
+                }
+            }.start();
+        }
     }
 
     @Override
@@ -292,66 +359,10 @@ public class BrowserProvider extends ContentProvider {
                 ed.commit();
             }
         }
-        mSearchManager = (SearchManager) context.getSystemService(Context.SEARCH_SERVICE);
-        mShowWebSuggestionsSettingChangeObserver
-            = new ShowWebSuggestionsSettingChangeObserver();
-        context.getContentResolver().registerContentObserver(
-                Settings.System.getUriFor(
-                        Settings.System.SHOW_WEB_SUGGESTIONS),
-                true, mShowWebSuggestionsSettingChangeObserver);
-        updateShowWebSuggestions();
+        mSettings = BrowserSettings.getInstance();
         return true;
     }
 
-    /**
-     * This Observer will ensure that if the user changes the system
-     * setting of whether to display web suggestions, we will
-     * change accordingly.
-     */
-    /* package */ class ShowWebSuggestionsSettingChangeObserver
-            extends ContentObserver {
-        public ShowWebSuggestionsSettingChangeObserver() {
-            super(new Handler());
-        }
-
-        @Override
-        public void onChange(boolean selfChange) {
-            updateShowWebSuggestions();
-        }
-    }
-
-    private ShowWebSuggestionsSettingChangeObserver
-            mShowWebSuggestionsSettingChangeObserver;
-
-    // If non-null, then the system is set to show web suggestions,
-    // and this is the SearchableInfo to use to get them.
-    private SearchableInfo mSearchableInfo;
-
-    /**
-     * Check the system settings to see whether web suggestions are
-     * allowed.  If so, store the SearchableInfo to grab suggestions
-     * while the user is typing.
-     */
-    private void updateShowWebSuggestions() {
-        mSearchableInfo = null;
-        Context context = getContext();
-        if (Settings.System.getInt(context.getContentResolver(),
-                Settings.System.SHOW_WEB_SUGGESTIONS,
-                1 /* default on */) == 1) {
-            Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
-            intent.addCategory(Intent.CATEGORY_DEFAULT);
-            ResolveInfo info = context.getPackageManager().resolveActivity(
-                    intent, PackageManager.MATCH_DEFAULT_ONLY);
-            if (info != null) {
-                ComponentName googleSearchComponent =
-                        new ComponentName(info.activityInfo.packageName,
-                                info.activityInfo.name);
-                mSearchableInfo = mSearchManager.getSearchableInfo(
-                        googleSearchComponent, false);
-            }
-        }
-    }
-
     private void fixPicasaBookmark() {
         SQLiteDatabase db = mOpenHelper.getWritableDatabase();
         Cursor cursor = db.rawQuery("SELECT _id FROM bookmarks WHERE " +
@@ -374,23 +385,26 @@ public class BrowserProvider extends ContentProvider {
 
     /*
      * Subclass AbstractCursor so we can combine multiple Cursors and add
-     * "Google Search".
+     * "Search the web".
      * Here are the rules.
      * 1. We only have MAX_SUGGESTION_LONG_ENTRIES in the list plus
-     *      "Google Search";
-     * 2. If bookmark/history entries are less than
-     *      (MAX_SUGGESTION_SHORT_ENTRIES -1), we include Google suggest.
+     *      "Search the web";
+     * 2. If bookmark/history entries has a match, "Search the web" shows up at
+     *      the second place. Otherwise, "Search the web" shows up at the first
+     *      place.
      */
     private class MySuggestionCursor extends AbstractCursor {
         private Cursor  mHistoryCursor;
         private Cursor  mSuggestCursor;
         private int     mHistoryCount;
         private int     mSuggestionCount;
-        private boolean mBeyondCursor;
+        private boolean mIncludeWebSearch;
         private String  mString;
         private int     mSuggestText1Id;
         private int     mSuggestText2Id;
+        private int     mSuggestText2UrlId;
         private int     mSuggestQueryId;
+        private int     mSuggestIntentExtraDataId;
 
         public MySuggestionCursor(Cursor hc, Cursor sc, String string) {
             mHistoryCursor = hc;
@@ -401,7 +415,7 @@ public class BrowserProvider extends ContentProvider {
                 mSuggestionCount = MAX_SUGGESTION_LONG_ENTRIES - mHistoryCount;
             }
             mString = string;
-            mBeyondCursor = false;
+            mIncludeWebSearch = string.length() > 0;
 
             // 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
@@ -409,14 +423,20 @@ public class BrowserProvider extends ContentProvider {
             if (mSuggestCursor == null) {
                 mSuggestText1Id = -1;
                 mSuggestText2Id = -1;
+                mSuggestText2UrlId = -1;
                 mSuggestQueryId = -1;
+                mSuggestIntentExtraDataId = -1;
             } else {
                 mSuggestText1Id = mSuggestCursor.getColumnIndex(
                                 SearchManager.SUGGEST_COLUMN_TEXT_1);
                 mSuggestText2Id = mSuggestCursor.getColumnIndex(
                                 SearchManager.SUGGEST_COLUMN_TEXT_2);
+                mSuggestText2UrlId = mSuggestCursor.getColumnIndex(
+                        SearchManager.SUGGEST_COLUMN_TEXT_2_URL);
                 mSuggestQueryId = mSuggestCursor.getColumnIndex(
                                 SearchManager.SUGGEST_COLUMN_QUERY);
+                mSuggestIntentExtraDataId = mSuggestCursor.getColumnIndex(
+                                SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA);
             }
         }
 
@@ -425,21 +445,30 @@ public class BrowserProvider extends ContentProvider {
             if (mHistoryCursor == null) {
                 return false;
             }
+            if (mIncludeWebSearch) {
+                if (mHistoryCount == 0 && newPosition == 0) {
+                    return true;
+                } else if (mHistoryCount > 0) {
+                    if (newPosition == 0) {
+                        mHistoryCursor.moveToPosition(0);
+                        return true;
+                    } else if (newPosition == 1) {
+                        return true;
+                    }
+                }
+                newPosition--;
+            }
             if (mHistoryCount > newPosition) {
                 mHistoryCursor.moveToPosition(newPosition);
-                mBeyondCursor = false;
-            } else if (mHistoryCount + mSuggestionCount > newPosition) {
-                mSuggestCursor.moveToPosition(newPosition - mHistoryCount);
-                mBeyondCursor = false;
             } else {
-                mBeyondCursor = true;
+                mSuggestCursor.moveToPosition(newPosition - mHistoryCount);
             }
             return true;
         }
 
         @Override
         public int getCount() {
-            if (mString.length() > 0) {
+            if (mIncludeWebSearch) {
                 return mHistoryCount + mSuggestionCount + 1;
             } else {
                 return mHistoryCount + mSuggestionCount;
@@ -454,43 +483,69 @@ public class BrowserProvider extends ContentProvider {
         @Override
         public String getString(int columnIndex) {
             if ((mPos != -1 && mHistoryCursor != null)) {
+                int type = -1; // 0: web search; 1: history; 2: suggestion
+                if (mIncludeWebSearch) {
+                    if (mHistoryCount == 0 && mPos == 0) {
+                        type = 0;
+                    } else if (mHistoryCount > 0) {
+                        if (mPos == 0) {
+                            type = 1;
+                        } else if (mPos == 1) {
+                            type = 0;
+                        }
+                    }
+                    if (type == -1) type = (mPos - 1) < mHistoryCount ? 1 : 2;
+                } else {
+                    type = mPos < mHistoryCount ? 1 : 2;
+                }
+
                 switch(columnIndex) {
                     case SUGGEST_COLUMN_INTENT_ACTION_ID:
-                        if (mHistoryCount > mPos) {
+                        if (type == 1) {
                             return Intent.ACTION_VIEW;
                         } else {
                             return Intent.ACTION_SEARCH;
                         }
 
                     case SUGGEST_COLUMN_INTENT_DATA_ID:
-                        if (mHistoryCount > mPos) {
+                        if (type == 1) {
                             return mHistoryCursor.getString(1);
                         } else {
                             return null;
                         }
 
                     case SUGGEST_COLUMN_TEXT_1_ID:
-                        if (mHistoryCount > mPos) {
+                        if (type == 0) {
+                            return mString;
+                        } else if (type == 1) {
                             return getHistoryTitle();
-                        } else if (!mBeyondCursor) {
+                        } else {
                             if (mSuggestText1Id == -1) return null;
                             return mSuggestCursor.getString(mSuggestText1Id);
-                        } else {
-                            return mString;
                         }
 
                     case SUGGEST_COLUMN_TEXT_2_ID:
-                        if (mHistoryCount > mPos) {
-                            return getHistorySubtitle();
-                        } else if (!mBeyondCursor) {
+                        if (type == 0) {
+                            return getContext().getString(R.string.search_the_web);
+                        } else if (type == 1) {
+                            return null;  // Use TEXT_2_URL instead
+                        } else {
                             if (mSuggestText2Id == -1) return null;
                             return mSuggestCursor.getString(mSuggestText2Id);
+                        }
+
+                    case SUGGEST_COLUMN_TEXT_2_URL_ID:
+                        if (type == 0) {
+                            return null;
+                        } else if (type == 1) {
+                            return getHistoryUrl();
                         } else {
-                            return getContext().getString(R.string.search_the_web);
+                            if (mSuggestText2UrlId == -1) return null;
+                            return mSuggestCursor.getString(mSuggestText2UrlId);
                         }
 
                     case SUGGEST_COLUMN_ICON_1_ID:
-                        if (mHistoryCount > mPos) {
+                        if (type == 1) {
                             if (mHistoryCursor.getInt(3) == 1) {
                                 return Integer.valueOf(
                                         R.drawable.ic_search_category_bookmark)
@@ -510,21 +565,28 @@ public class BrowserProvider extends ContentProvider {
                         return "0";
 
                     case SUGGEST_COLUMN_QUERY_ID:
-                        if (mHistoryCount > mPos) {
+                        if (type == 0) {
+                            return mString;
+                        } else if (type == 1) {
                             // Return the url in the intent query column. This is ignored
                             // within the browser because our searchable is set to
                             // android:searchMode="queryRewriteFromData", but it is used by
                             // global search for query rewriting.
                             return mHistoryCursor.getString(1);
-                        } else if (!mBeyondCursor) {
+                        } else {
                             if (mSuggestQueryId == -1) return null;
                             return mSuggestCursor.getString(mSuggestQueryId);
-                        } else {
-                            return mString;
                         }
 
-                    case SUGGEST_COLUMN_FORMAT:
-                        return "html";
+                    case SUGGEST_COLUMN_INTENT_EXTRA_DATA:
+                        if (type == 0) {
+                            return null;
+                        } else if (type == 1) {
+                            return null;
+                        } else {
+                            if (mSuggestIntentExtraDataId == -1) return null;
+                            return mSuggestCursor.getString(mSuggestIntentExtraDataId);
+                        }
                 }
             }
             return null;
@@ -601,7 +663,7 @@ public class BrowserProvider extends ContentProvider {
         private String getHistoryTitle() {
             String title = mHistoryCursor.getString(2 /* webpage title */);
             if (TextUtils.isEmpty(title) || TextUtils.getTrimmedLength(title) == 0) {
-                title = beautifyUrl(mHistoryCursor.getString(1 /* url */));
+                title = stripUrl(mHistoryCursor.getString(1 /* url */));
             }
             return title;
         }
@@ -613,29 +675,99 @@ public class BrowserProvider extends ContentProvider {
          *
          * @return the subtitle string to use, or null if none
          */
-        private String getHistorySubtitle() {
+        private String getHistoryUrl() {
             String title = mHistoryCursor.getString(2 /* webpage title */);
             if (TextUtils.isEmpty(title) || TextUtils.getTrimmedLength(title) == 0) {
                 return null;
             } else {
-                return beautifyUrl(mHistoryCursor.getString(1 /* url */));
+                return stripUrl(mHistoryCursor.getString(1 /* url */));
             }
         }
 
-        /**
-         * Strips "http://" from the beginning of a url and "/" from the end,
-         * and adds html formatting to make it green.
-         */
-        private String beautifyUrl(String url) {
-            if (mSearchUrlColorId == null) {
-                // Get the color used for this purpose from the current theme.
-                TypedValue colorValue = new TypedValue();
-                getContext().getTheme().resolveAttribute(
-                        com.android.internal.R.attr.textColorSearchUrl, colorValue, true);
-                mSearchUrlColorId = Integer.toString(colorValue.resourceId);
+    }
+
+    private static class ResultsCursor extends AbstractCursor {
+        // Array indices for RESULTS_COLUMNS
+        private static final int RESULT_ACTION_ID = 1;
+        private static final int RESULT_DATA_ID = 2;
+        private static final int RESULT_TEXT_ID = 3;
+        private static final int RESULT_ICON_ID = 4;
+        private static final int RESULT_EXTRA_ID = 5;
+
+        private static final String[] RESULTS_COLUMNS = new String[] {
+                "_id",
+                SearchManager.SUGGEST_COLUMN_INTENT_ACTION,
+                SearchManager.SUGGEST_COLUMN_INTENT_DATA,
+                SearchManager.SUGGEST_COLUMN_TEXT_1,
+                SearchManager.SUGGEST_COLUMN_ICON_1,
+                SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA
+        };
+        private final ArrayList<String> mResults;
+        public ResultsCursor(ArrayList<String> results) {
+            mResults = results;
+        }
+        public int getCount() { return mResults.size(); }
+
+        public String[] getColumnNames() {
+            return RESULTS_COLUMNS;
+        }
+
+        public String getString(int column) {
+            switch (column) {
+                case RESULT_ACTION_ID:
+                    return RecognizerResultsIntent.ACTION_VOICE_SEARCH_RESULTS;
+                case RESULT_TEXT_ID:
+                // The data is used when the phone is in landscape mode.  We
+                // still want to show the result string.
+                case RESULT_DATA_ID:
+                    return mResults.get(mPos);
+                case RESULT_EXTRA_ID:
+                    // The Intent's extra data will store the index into
+                    // mResults so the BrowserActivity will know which result to
+                    // use.
+                    return Integer.toString(mPos);
+                case RESULT_ICON_ID:
+                    return Integer.valueOf(R.drawable.magnifying_glass)
+                            .toString();
+                default:
+                    return null;
+            }
+        }
+        public short getShort(int column) {
+            throw new UnsupportedOperationException();
+        }
+        public int getInt(int column) {
+            throw new UnsupportedOperationException();
+        }
+        public long getLong(int column) {
+            if ((mPos != -1) && column == 0) {
+                return mPos;        // use row# as the _id
             }
+            throw new UnsupportedOperationException();
+        }
+        public float getFloat(int column) {
+            throw new UnsupportedOperationException();
+        }
+        public double getDouble(int column) {
+            throw new UnsupportedOperationException();
+        }
+        public boolean isNull(int column) {
+            throw new UnsupportedOperationException();
+        }
+    }
 
-            return "<font color=\"@" + mSearchUrlColorId + "\">" + stripUrl(url) + "</font>";
+    private ResultsCursor mResultsCursor;
+
+    /**
+     * Provide a set of results to be returned to query, intended to be used
+     * by the SearchDialog when the BrowserActivity is in voice search mode.
+     * @param results Strings to display in the dropdown from the SearchDialog
+     */
+    /* package */ void setQueryResults(ArrayList<String> results) {
+        if (results == null) {
+            mResultsCursor = null;
+        } else {
+            mResultsCursor = new ResultsCursor(results);
         }
     }
 
@@ -643,12 +775,16 @@ public class BrowserProvider extends ContentProvider {
     public Cursor query(Uri url, String[] projectionIn, String selection,
             String[] selectionArgs, String sortOrder)
             throws IllegalStateException {
-        SQLiteDatabase db = mOpenHelper.getReadableDatabase();
-
         int match = URI_MATCHER.match(url);
         if (match == -1) {
             throw new IllegalArgumentException("Unknown URL");
         }
+        if (match == URI_MATCH_SUGGEST && mResultsCursor != null) {
+            Cursor results = mResultsCursor;
+            mResultsCursor = null;
+            return results;
+        }
+        SQLiteDatabase db = mOpenHelper.getReadableDatabase();
 
         if (match == URI_MATCH_SUGGEST || match == URI_MATCH_BOOKMARKS_SUGGEST) {
             String suggestSelection;
@@ -680,15 +816,17 @@ public class BrowserProvider extends ContentProvider {
                     ORDER_BY, MAX_SUGGESTION_LONG_ENTRIES_STRING);
 
             if (match == URI_MATCH_BOOKMARKS_SUGGEST
-                    || Regex.WEB_URL_PATTERN.matcher(selectionArgs[0]).matches()) {
+                    || Patterns.WEB_URL.matcher(selectionArgs[0]).matches()) {
                 return new MySuggestionCursor(c, null, "");
             } else {
-                // get Google suggest if there is still space in the list
+                // get search suggestions if there is still space in the list
                 if (myArgs != null && myArgs.length > 1
-                        && mSearchableInfo != null
                         && c.getCount() < (MAX_SUGGESTION_SHORT_ENTRIES - 1)) {
-                    Cursor sc = mSearchManager.getSuggestions(mSearchableInfo, selectionArgs[0]);
-                    return new MySuggestionCursor(c, sc, selectionArgs[0]);
+                    SearchEngine searchEngine = mSettings.getSearchEngine();
+                    if (searchEngine != null && searchEngine.supportsSuggestions()) {
+                        Cursor sc = searchEngine.getSuggestions(getContext(), selectionArgs[0]);
+                        return new MySuggestionCursor(c, sc, selectionArgs[0]);
+                    }
                 }
                 return new MySuggestionCursor(c, null, selectionArgs[0]);
             }
@@ -856,18 +994,14 @@ public class BrowserProvider extends ContentProvider {
             throw new IllegalArgumentException("Unknown URL");
         }
 
-        String id = null;
-        boolean isBookmarkTable = (match == URI_MATCH_BOOKMARKS_ID);
-        boolean changingBookmarks = false;
-
-        if (isBookmarkTable || match == URI_MATCH_SEARCHES_ID) {
+        if (match == URI_MATCH_BOOKMARKS_ID || match == URI_MATCH_SEARCHES_ID) {
             StringBuilder sb = new StringBuilder();
             if (where != null && where.length() > 0) {
                 sb.append("( ");
                 sb.append(where);
                 sb.append(" ) AND ");
             }
-            id = url.getPathSegments().get(1);
+            String id = url.getPathSegments().get(1);
             sb.append("_id = ");
             sb.append(id);
             where = sb.toString();
@@ -878,23 +1012,23 @@ public class BrowserProvider extends ContentProvider {
         // Not all bookmark-table updates should be backed up.  Look to see
         // whether we changed the title, url, or "is a bookmark" state, and
         // request a backup if so.
-        if (isBookmarkTable) {
+        if (match == URI_MATCH_BOOKMARKS_ID || match == URI_MATCH_BOOKMARKS) {
+            boolean changingBookmarks = false;
             // Alterations to the bookmark field inherently change the bookmark
             // set, so we don't need to query the record; we know a priori that
             // we will need to back up this change.
             if (values.containsKey(BookmarkColumns.BOOKMARK)) {
                 changingBookmarks = true;
-            }
-            // changing the title or URL of a bookmark record requires a backup,
-            // but we don't know wether such an update is on a bookmark without
-            // querying the record
-            if (!changingBookmarks &&
-                    (values.containsKey(BookmarkColumns.TITLE)
-                     || values.containsKey(BookmarkColumns.URL))) {
-                // when isBookmarkTable is true, the 'id' var was assigned above
+            } else if ((values.containsKey(BookmarkColumns.TITLE)
+                     || values.containsKey(BookmarkColumns.URL))
+                     && values.containsKey(BookmarkColumns._ID)) {
+                // If a title or URL has been changed, check to see if it is to
+                // a bookmark.  The ID should have been included in the update,
+                // so use it.
                 Cursor cursor = cr.query(Browser.BOOKMARKS_URI,
                         new String[] { BookmarkColumns.BOOKMARK },
-                        "_id = " + id, null, null);
+                        BookmarkColumns._ID + " = "
+                        + values.getAsString(BookmarkColumns._ID), null, null);
                 if (cursor.moveToNext()) {
                     changingBookmarks = (cursor.getInt(0) != 0);
                 }