resolved conflicts for merge of 565505 to master
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index d95877e..aec23a2 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -53,9 +53,9 @@
              (android.test) library. -->
         <uses-library android:name="android.test.runner" />
 
-        <provider android:name="BrowserProvider" 
-                  android:authorities="browser" 
-                  android:multiprocess="true" 
+        <provider android:name="BrowserProvider"
+                  android:authorities="browser"
+                  android:multiprocess="true"
                   android:readPermission="com.android.browser.permission.READ_HISTORY_BOOKMARKS"
                   android:writePermission="com.android.browser.permission.WRITE_HISTORY_BOOKMARKS"/>
         <activity android:name="BrowserActivity"
@@ -112,7 +112,6 @@
                 <data android:scheme="https" />
             </intent-filter>
             <intent-filter>
-                <action android:name="android.intent.action.WEB_SEARCH" />
                 <action android:name="android.intent.action.MEDIA_SEARCH" />
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
@@ -149,7 +148,7 @@
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
 
-        </activity-alias>        
+        </activity-alias>
 
         <activity android:name="BrowserDownloadPage" android:label=""
                   android:configChanges="orientation|keyboardHidden">
diff --git a/src/com/android/browser/BrowserActivity.java b/src/com/android/browser/BrowserActivity.java
index 7d6be9e..b5864e6 100644
--- a/src/com/android/browser/BrowserActivity.java
+++ b/src/com/android/browser/BrowserActivity.java
@@ -170,6 +170,13 @@
 
     private WebStorage.QuotaUpdater mWebStorageQuotaUpdater = 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;
 
@@ -687,6 +694,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.
@@ -836,6 +849,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();
@@ -904,6 +923,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) {
@@ -4581,34 +4665,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 df52d80..8ed889e 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 ?"
                 + " OR title LIKE ?";
     private String[] SUGGEST_ARGS = new String[5];
@@ -141,11 +143,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.
@@ -153,12 +155,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) {
@@ -220,7 +222,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) {
@@ -251,7 +253,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) {
@@ -292,9 +294,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 {
@@ -304,6 +306,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;
@@ -315,6 +320,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
@@ -347,7 +368,7 @@
         public String[] getColumnNames() {
             return COLUMNS;
         }
-        
+
         @Override
         public String getString(int columnIndex) {
             if ((mPos != -1 && mHistoryCursor != null)) {
@@ -370,7 +391,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;
                         }
@@ -379,7 +401,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);
                         }
@@ -408,11 +431,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";
                 }
@@ -481,11 +505,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() {
@@ -495,12 +519,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() {
@@ -511,7 +535,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.
@@ -523,19 +547,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();
 
@@ -580,15 +604,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]);
             }
@@ -740,14 +769,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