Merge commit 'remotes/korg/cupcake' into merge

Conflicts:
	src/com/android/browser/BrowserActivity.java
diff --git a/src/com/android/browser/AddBookmarkPage.java b/src/com/android/browser/AddBookmarkPage.java
index ea65a46..cf3fe70 100644
--- a/src/com/android/browser/AddBookmarkPage.java
+++ b/src/com/android/browser/AddBookmarkPage.java
@@ -48,8 +48,8 @@
     private Bundle      mMap;
     
     private static final String[]   mProjection = 
-        { "_id", "url", "bookmark", "created", "title" };
-    private static final String     WHERE_CLAUSE = "url = ? AND bookmark = 0";
+        { "_id", "url", "bookmark", "created", "title", "visits" };
+    private static final String     WHERE_CLAUSE = "url = ?";
     private final String[]          SELECTION_ARGS = new String[1];
 
     private View.OnClickListener mSaveBookmark = new View.OnClickListener() {
@@ -119,16 +119,13 @@
         boolean emptyTitle = title.length() == 0;
         boolean emptyUrl = unfilteredUrl.trim().length() == 0;
         Resources r = getResources();
-        if (emptyTitle) {
+        if (emptyTitle || emptyUrl) {
+            if (emptyTitle) {
+                mTitle.setError(r.getText(R.string.bookmark_needs_title));
+            }
             if (emptyUrl) {
-                setTitle(r.getText(R.string.empty_bookmark));
-                return false;
-            }    
-            setTitle(r.getText(R.string.bookmark_needs_title));
-            return false;
-        }
-        if (emptyUrl) {
-            setTitle(r.getText(R.string.bookmark_needs_url));
+                mAddress.setError(r.getText(R.string.bookmark_needs_url));
+            }
             return false;
         }
         String url = unfilteredUrl;
@@ -138,11 +135,11 @@
             try {
                 address = new WebAddress(unfilteredUrl);
             } catch (ParseException e) {
-                setTitle(r.getText(R.string.bookmark_url_not_valid));
+                mAddress.setError(r.getText(R.string.bookmark_url_not_valid));
                 return false;
             }
             if (address.mHost.length() == 0) {
-                setTitle(r.getText(R.string.bookmark_url_not_valid));
+                mAddress.setError(r.getText(R.string.bookmark_url_not_valid));
                 return false;
             }
             url = address.toString();
@@ -163,25 +160,58 @@
                         WHERE_CLAUSE,
                         SELECTION_ARGS,
                         null);
-                if (c.moveToFirst()) {
-                    // This means we have been to this site, so convert the 
-                    // history item to a bookmark.
-                    ContentValues map = new ContentValues();
+                ContentValues map = new ContentValues();
+                if (c.moveToFirst() && c.getInt(c.getColumnIndexOrThrow(
+                        Browser.BookmarkColumns.BOOKMARK)) == 0) {
+                    // This means we have been to this site but not bookmarked
+                    // it, so convert the history item to a bookmark                    
                     map.put(Browser.BookmarkColumns.CREATED, creationTime);
                     map.put(Browser.BookmarkColumns.TITLE, title);
                     map.put(Browser.BookmarkColumns.BOOKMARK, 1);
                     cr.update(Browser.BOOKMARKS_URI, map, 
                             "_id = " + c.getInt(0), null);
                 } else {
-                    // Adding a bookmark for a site the user has not been to.
-                    ContentValues map = new ContentValues();
-                    map.put(Browser.BookmarkColumns.TITLE, title);
-                    map.put(Browser.BookmarkColumns.URL, url);
-                    map.put(Browser.BookmarkColumns.CREATED, creationTime);
-                    map.put(Browser.BookmarkColumns.BOOKMARK, 1);
-                    map.put(Browser.BookmarkColumns.DATE, 0);
-                    map.put(Browser.BookmarkColumns.VISITS, 0);
-                    cr.insert(Browser.BOOKMARKS_URI, map);
+                    int count = c.getCount();
+                    boolean matchedTitle = false;
+                    for (int i = 0; i < count; i++) {
+                        // One or more bookmarks already exist for this site.
+                        // Check the names of each
+                        c.moveToPosition(i);
+                        if (c.getString(c.getColumnIndexOrThrow(
+                                Browser.BookmarkColumns.TITLE)).equals(title)) {
+                            // The old bookmark has the same name.
+                            // Update its creation time.
+                            map.put(Browser.BookmarkColumns.CREATED,
+                                    creationTime);
+                            cr.update(Browser.BOOKMARKS_URI, map, 
+                                    "_id = " + c.getInt(0), null);
+                            matchedTitle = true;
+                        }
+                    }
+                    if (!matchedTitle) {
+                        // Adding a bookmark for a site the user has visited,
+                        // or a new bookmark (with a different name) for a site
+                        // the user has visited
+                        map.put(Browser.BookmarkColumns.TITLE, title);
+                        map.put(Browser.BookmarkColumns.URL, url);
+                        map.put(Browser.BookmarkColumns.CREATED, creationTime);
+                        map.put(Browser.BookmarkColumns.BOOKMARK, 1);
+                        map.put(Browser.BookmarkColumns.DATE, 0);
+                        int visits = 0;
+                        if (count > 0) {
+                            // The user has already bookmarked, and possibly
+                            // visited this site.  However, they are creating
+                            // a new bookmark with the same url but a different
+                            // name.  The new bookmark should have the same
+                            // number of visits as the already created bookmark.
+                            visits = c.getInt(c.getColumnIndexOrThrow(
+                                    Browser.BookmarkColumns.VISITS));
+                        }
+                        // Bookmark starts with 3 extra visits so that it will
+                        // bubble up in the most visited and goto search box
+                        map.put(Browser.BookmarkColumns.VISITS, visits + 3);
+                        cr.insert(Browser.BOOKMARKS_URI, map);
+                    }
                 }
                 WebIconDatabase.getInstance().retainIconForPageUrl(url);
                 c.deactivate();
diff --git a/src/com/android/browser/AddNewBookmark.java b/src/com/android/browser/AddNewBookmark.java
index 748c9c2..a75d002 100644
--- a/src/com/android/browser/AddNewBookmark.java
+++ b/src/com/android/browser/AddNewBookmark.java
@@ -63,10 +63,6 @@
      *  @param url  The new url for the bookmark item.
      */
     /* package */ void setUrl(String url) {
-        if (url.length() > BrowserSettings.MAX_TEXTVIEW_LEN) {
-            mUrlText.setText(url.substring(0, BrowserSettings.MAX_TEXTVIEW_LEN));
-        } else {
-            mUrlText.setText(url);
-        }
+        mUrlText.setText(url);
     }
 }
diff --git a/src/com/android/browser/BookmarkItem.java b/src/com/android/browser/BookmarkItem.java
index 0015eaf..a70dd4f 100644
--- a/src/com/android/browser/BookmarkItem.java
+++ b/src/com/android/browser/BookmarkItem.java
@@ -19,19 +19,20 @@
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.view.LayoutInflater;
+import android.view.View;
 import android.widget.ImageView;
-import android.widget.RelativeLayout;
+import android.widget.LinearLayout;
 import android.widget.TextView;
 
 /**
  *  Custom layout for an item representing a bookmark in the browser.
  */
-class BookmarkItem extends RelativeLayout {
+class BookmarkItem extends LinearLayout {
 
-    private TextView    mTextView;
-    private TextView    mUrlText;
-    private ImageView   mImageView;
-    private LayoutInflater mFactory;
+    protected TextView    mTextView;
+    protected TextView    mUrlText;
+    protected ImageView   mImageView;
+    protected String      mUrl;
 
     /**
      *  Instantiate a bookmark item, including a default favicon.
@@ -41,11 +42,13 @@
     BookmarkItem(Context context) {
         super(context);
 
-        mFactory = LayoutInflater.from(context);
-        mFactory.inflate(R.layout.bookmark_item, this);
+        LayoutInflater factory = LayoutInflater.from(context);
+        factory.inflate(R.layout.history_item, this);
         mTextView = (TextView) findViewById(R.id.title);
         mUrlText = (TextView) findViewById(R.id.url);
         mImageView = (ImageView) findViewById(R.id.favicon);
+        View star = findViewById(R.id.star);
+        star.setVisibility(View.GONE);
     }
 
     /**
@@ -61,8 +64,8 @@
     /**
      * Return the name assigned to this bookmark item.
      */
-    /* package */ CharSequence getName() {
-        return mTextView.getText();
+    /* package */ String getName() {
+        return mTextView.getText().toString();
     }
 
     /**
@@ -72,6 +75,10 @@
         return mTextView;
     }
 
+    /* package */ String getUrl() {
+        return mUrl;
+    }
+
     /**
      *  Set the favicon for this item.
      *
@@ -101,5 +108,6 @@
      */
     /* package */ void setUrl(String url) {
         mUrlText.setText(url);
+        mUrl = url;
     }
 }
diff --git a/src/com/android/browser/BrowserActivity.java b/src/com/android/browser/BrowserActivity.java
index 789babe..e018eb4 100644
--- a/src/com/android/browser/BrowserActivity.java
+++ b/src/com/android/browser/BrowserActivity.java
@@ -16,26 +16,28 @@
 
 package com.android.browser;
 
+import com.google.android.googleapps.IGoogleLoginService;
+import com.google.android.googlelogin.GoogleLoginServiceConstants;
+
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.AlertDialog;
-import android.app.SearchManager;
 import android.app.ProgressDialog;
+import android.app.SearchManager;
 import android.content.ActivityNotFoundException;
-import android.content.res.AssetManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
-import android.content.DialogInterface.OnCancelListener;
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
-import android.content.pm.ActivityInfo;
+import android.content.DialogInterface.OnCancelListener;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.content.res.AssetManager;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.database.Cursor;
@@ -54,10 +56,10 @@
 import android.graphics.drawable.PaintDrawable;
 import android.hardware.SensorListener;
 import android.hardware.SensorManager;
+import android.net.ConnectivityManager;
 import android.net.Uri;
 import android.net.WebAddress;
 import android.net.http.EventHandler;
-import android.net.http.RequestQueue;
 import android.net.http.SslCertificate;
 import android.net.http.SslError;
 import android.os.AsyncTask;
@@ -75,10 +77,10 @@
 import android.os.SystemProperties;
 import android.preference.PreferenceManager;
 import android.provider.Browser;
-import android.provider.Contacts.Intents.Insert;
 import android.provider.Contacts;
 import android.provider.Downloads;
 import android.provider.MediaStore;
+import android.provider.Contacts.Intents.Insert;
 import android.text.IClipboard;
 import android.text.TextUtils;
 import android.text.format.DateFormat;
@@ -86,8 +88,6 @@
 import android.util.Config;
 import android.util.Log;
 import android.view.ContextMenu;
-import android.view.ContextMenu.ContextMenuInfo;
-import android.view.MenuItem.OnMenuItemClickListener;
 import android.view.Gravity;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
@@ -97,6 +97,9 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.Window;
+import android.view.WindowManager;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.MenuItem.OnMenuItemClickListener;
 import android.view.animation.AlphaAnimation;
 import android.view.animation.Animation;
 import android.view.animation.AnimationSet;
@@ -120,16 +123,14 @@
 import android.widget.TextView;
 import android.widget.Toast;
 
-import com.google.android.googleapps.IGoogleLoginService;
-import com.google.android.googlelogin.GoogleLoginServiceConstants;
-
 import java.io.BufferedOutputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
-import java.io.InputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.net.MalformedURLException;
+import java.net.URI;
 import java.net.URL;
 import java.net.URLEncoder;
 import java.text.ParseException;
@@ -138,9 +139,9 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
+import java.util.Vector;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
-import java.util.Vector;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
 
@@ -202,7 +203,7 @@
                     if (googleUser == null || !hostedUser.equals(googleUser)) {
                         String domain = hostedUser.substring(hostedUser.lastIndexOf('@')+1);
                         homepage = "http://www.google.com/m/a/" + domain + "?client=ms-" +
-                            SystemProperties.get("ro.com.google.clientid", "unknown");
+                            SystemProperties.get("persist.sys.com.google.clientid", "unknown");
                     }
                 } catch (RemoteException ignore) {
                     // Login service died; carry on
@@ -685,9 +686,11 @@
             // If the intent is ACTION_VIEW and data is not null, the Browser is
             // invoked to view the content by another application. In this case,
             // the tab will be close when exit.
+            String url = getUrlFromIntent(intent);
             final TabControl.Tab t = mTabControl.createNewTab(
                     Intent.ACTION_VIEW.equals(intent.getAction()) &&
-                    intent.getData() != null);
+                    intent.getData() != null,
+                    intent.getStringExtra(Browser.EXTRA_APPLICATION_ID), url);
             mTabControl.setCurrentTab(t);
             // This is one of the only places we call attachTabToContentView
             // without animating from the tab picker.
@@ -709,7 +712,6 @@
             }
             copyPlugins(true);
 
-            String url = getUrlFromIntent(intent);
             if (url == null || url.length() == 0) {
                 if (mSettings.isLoginInitialized()) {
                     webView.loadUrl(mSettings.getHomePage());
@@ -730,19 +732,18 @@
            http stack */
         mNetworkStateChangedFilter = new IntentFilter();
         mNetworkStateChangedFilter.addAction(
-                RequestQueue.HTTP_NETWORK_STATE_CHANGED_INTENT);
+                ConnectivityManager.CONNECTIVITY_ACTION);
         mNetworkStateIntentReceiver = new BroadcastReceiver() {
                 @Override
                 public void onReceive(Context context, Intent intent) {
                     if (intent.getAction().equals(
-                            RequestQueue.HTTP_NETWORK_STATE_CHANGED_INTENT)) {
-                        Boolean up = (Boolean)intent.getExtra(
-                                RequestQueue.HTTP_NETWORK_STATE_UP);
-                        onNetworkToggle(up);
+                            ConnectivityManager.CONNECTIVITY_ACTION)) {
+                        boolean down = intent.getBooleanExtra(
+                                ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
+                        onNetworkToggle(!down);
                     }
                 }
             };
-        setRequestedOrientation(mSettings.getOrientation());
     }
 
     @Override
@@ -778,11 +779,44 @@
             }
             if (Intent.ACTION_VIEW.equals(action) &&
                     (flags & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) != 0) {
+                final String appId =
+                        intent.getStringExtra(Browser.EXTRA_APPLICATION_ID);
+                final TabControl.Tab appTab = mTabControl.getTabFromId(appId);
+                if (appTab != null) {
+                    Log.i(LOGTAG, "Reusing tab for " + appId);
+                    // Dismiss the subwindow if applicable.
+                    dismissSubWindow(appTab);
+                    // Since we might kill the WebView, remove it from the
+                    // content view first.
+                    removeTabFromContentView(appTab);
+                    // Recreate the main WebView after destroying the old one.
+                    // If the WebView has the same original url and is on that
+                    // page, it can be reused.
+                    boolean needsLoad =
+                            mTabControl.recreateWebView(appTab, url);
+                    if (current != appTab) {
+                        showTab(appTab, needsLoad ? url : null);
+                    } else {
+                        if (mTabOverview != null && mAnimationCount == 0) {
+                            sendAnimateFromOverview(appTab, false,
+                                    needsLoad ? url : null, TAB_OVERVIEW_DELAY,
+                                    null);
+                        } else {
+                            // If the tab was the current tab, we have to attach
+                            // it to the view system again.
+                            attachTabToContentView(appTab);
+                            if (needsLoad) {
+                                appTab.getWebView().loadUrl(url);
+                            }
+                        }
+                    }
+                    return;
+                }
                 // if FLAG_ACTIVITY_BROUGHT_TO_FRONT flag is on, the url will be
                 // opened in a new tab unless we have reached MAX_TABS. Then the
                 // url will be opened in the current tab. If a new tab is
                 // created, it will have "true" for exit on close.
-                openTabAndShow(url, null, true);
+                openTabAndShow(url, null, true, appId);
             } else {
                 if ("about:debug".equals(url)) {
                     mSettings.toggleDebugSettings();
@@ -1289,13 +1323,12 @@
     }
 
     /**
-     * Overriding this forces the search key to launch global search.  The difference
-     * is the final "true" which requests global search.
+     * Overriding this to insert a local information bundle
      */
     @Override
     public boolean onSearchRequested() {
         startSearch(null, false,
-                createGoogleSearchSourceBundle(GOOGLE_SEARCH_SOURCE_SEARCHKEY), true);
+                createGoogleSearchSourceBundle(GOOGLE_SEARCH_SOURCE_SEARCHKEY), false);
         return true;
     }
 
@@ -1324,19 +1357,13 @@
                 }
                 break;
 
-            case R.id.search_menu_id:
-                // launch using "global" search, which will bring up the Google search box
-                startSearch(null, false,
-                        createGoogleSearchSourceBundle(GOOGLE_SEARCH_SOURCE_SEARCHMENU), true);
-                break;
-
             case R.id.bookmarks_menu_id:
-                bookmarksPicker();
+                bookmarksOrHistoryPicker(false);
                 break;
 
             case R.id.windows_menu_id:
                 if (mTabControl.getTabCount() == 1) {
-                    openTabAndShow(mSettings.getHomePage(), null, false);
+                    openTabAndShow(mSettings.getHomePage(), null, false, null);
                 } else {
                     tabPicker(true, mTabControl.getCurrentIndex(), false);
                 }
@@ -1412,12 +1439,7 @@
                 break;
 
             case R.id.classic_history_menu_id:
-                loadHistory();
-                break;
-
-            case R.id.bookmark_page_menu_id:
-                Browser.saveBookmark(this, getTopWindow().getTitle(),
-                        getTopWindow().getUrl());
+                bookmarksOrHistoryPicker(true);
                 break;
 
             case R.id.share_page_menu_id:
@@ -1428,12 +1450,6 @@
                 getTopWindow().debugDump();
                 break;
 
-            case R.id.zoom_menu_id:
-                // FIXME: Can we move this out of WebView? How does this work
-                // for a subwindow?
-                getTopWindow().invokeZoomPicker();
-                break;
-
             case R.id.zoom_in_menu_id:
                 getTopWindow().zoomIn();
                 break;
@@ -1446,18 +1462,6 @@
                 viewDownloads(null);
                 break;
 
-            case R.id.flip_orientation_menu_id:
-                if (mSettings.getOrientation() !=
-                        ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
-                    mSettings.setOrientation(this,
-                            ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
-                } else {
-                    mSettings.setOrientation(this,
-                            ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
-                }
-                setRequestedOrientation(mSettings.getOrientation());
-                break;
-
             // -- Tab menu
             case R.id.view_tab_menu_id:
                 if (mTabListener != null && mTabOverview != null) {
@@ -1499,17 +1503,12 @@
                 }
                 break;
 
-            case R.id.history_tab_menu_id: {
-                    Intent i = new Intent(this, BrowserHistoryPage.class);
-                    i.putExtra("maxTabsOpen",
-                            mTabControl.getTabCount() >=
-                            TabControl.MAX_TABS);
-                    startActivityForResult(i, CLASSIC_HISTORY_PAGE);
-                }
+            case R.id.history_tab_menu_id:
+                bookmarksOrHistoryPicker(true);
                 break;
 
             case R.id.bookmarks_tab_menu_id:
-                bookmarksPicker();
+                bookmarksOrHistoryPicker(false);
                 break;
 
             case R.id.properties_tab_menu_id:
@@ -1570,6 +1569,7 @@
                 if (mCurrentMenuState != mMenuState) {
                     menu.setGroupVisible(R.id.MAIN_MENU, false);
                     menu.setGroupEnabled(R.id.MAIN_MENU, false);
+                    menu.setGroupEnabled(R.id.MAIN_SHORTCUT_MENU, false);
                     menu.setGroupVisible(R.id.TAB_MENU, true);
                     menu.setGroupEnabled(R.id.TAB_MENU, true);
                 }
@@ -1582,6 +1582,7 @@
                 if (mCurrentMenuState != mMenuState) {
                     menu.setGroupVisible(R.id.MAIN_MENU, false);
                     menu.setGroupEnabled(R.id.MAIN_MENU, false);
+                    menu.setGroupEnabled(R.id.MAIN_SHORTCUT_MENU, false);
                     menu.setGroupVisible(R.id.TAB_MENU, false);
                     menu.setGroupEnabled(R.id.TAB_MENU, false);
                 }
@@ -1590,47 +1591,34 @@
                 if (mCurrentMenuState != mMenuState) {
                     menu.setGroupVisible(R.id.MAIN_MENU, true);
                     menu.setGroupEnabled(R.id.MAIN_MENU, true);
+                    menu.setGroupEnabled(R.id.MAIN_SHORTCUT_MENU, true);
                     menu.setGroupVisible(R.id.TAB_MENU, false);
                     menu.setGroupEnabled(R.id.TAB_MENU, false);
                 }
                 final WebView w = getTopWindow();
-                boolean canGoBack = w.canGoBack();
+                boolean canGoBack = false;
+                boolean canGoForward = false;
+                boolean isHome = false;
+                if (w != null) {
+                    canGoBack = w.canGoBack();
+                    canGoForward = w.canGoForward();
+                    isHome = mSettings.getHomePage().equals(w.getUrl());
+                }
                 final MenuItem back = menu.findItem(R.id.back_menu_id);
-                back.setVisible(canGoBack);
                 back.setEnabled(canGoBack);
-                final MenuItem flip =
-                        menu.findItem(R.id.flip_orientation_menu_id);
-                boolean keyboardClosed =
-                        getResources().getConfiguration().hardKeyboardHidden ==
-                        Configuration.HARDKEYBOARDHIDDEN_YES;
-                flip.setEnabled(keyboardClosed);
 
-                boolean isHome = mSettings.getHomePage().equals(w.getUrl());
                 final MenuItem home = menu.findItem(R.id.homepage_menu_id);
-                home.setVisible(!isHome);
                 home.setEnabled(!isHome);
 
                 menu.findItem(R.id.forward_menu_id)
-                        .setEnabled(w.canGoForward());
-
-                menu.findItem(R.id.zoom_in_menu_id).setVisible(false);
-                menu.findItem(R.id.zoom_out_menu_id).setVisible(false);
+                        .setEnabled(canGoForward);
 
                 // decide whether to show the share link option
                 PackageManager pm = getPackageManager();
                 Intent send = new Intent(Intent.ACTION_SEND);
                 send.setType("text/plain");
-                List<ResolveInfo> list = pm.queryIntentActivities(send,
-                        PackageManager.MATCH_DEFAULT_ONLY);
-                menu.findItem(R.id.share_page_menu_id).setVisible(
-                        list.size() > 0);
-
-                // Hide the menu+<window number> items
-                // Can't set visibility in menu xml file b/c when a
-                // group is set visible, all items are set visible.
-                for (int i = 0; i < WINDOW_SHORTCUT_ID_ARRAY.length; i++) {
-                    menu.findItem(WINDOW_SHORTCUT_ID_ARRAY[i]).setVisible(false);
-                }
+                ResolveInfo ri = pm.resolveActivity(send, PackageManager.MATCH_DEFAULT_ONLY);
+                menu.findItem(R.id.share_page_menu_id).setVisible(ri != null);
 
                 // If there is only 1 window, the text will be "New window"
                 final MenuItem windows = menu.findItem(R.id.windows_menu_id);
@@ -1737,14 +1725,16 @@
                 PackageManager pm = getPackageManager();
                 Intent send = new Intent(Intent.ACTION_SEND);
                 send.setType("text/plain");
-                List<ResolveInfo> list = pm.queryIntentActivities(send,
-                        PackageManager.MATCH_DEFAULT_ONLY);
-                menu.findItem(R.id.share_link_context_menu_id).setVisible(
-                        list.size() > 0);
-                break;
-
+                ResolveInfo ri = pm.resolveActivity(send, PackageManager.MATCH_DEFAULT_ONLY);
+                menu.findItem(R.id.share_link_context_menu_id).setVisible(ri != null);
+                if (type == WebView.HitTestResult.SRC_ANCHOR_TYPE) {
+                    break;
+                }
+                // otherwise fall through to handle image part
             case WebView.HitTestResult.IMAGE_TYPE:
-                menu.setHeaderTitle(extra);
+                if (type == WebView.HitTestResult.IMAGE_TYPE) {
+                    menu.setHeaderTitle(extra);
+                }
                 menu.findItem(R.id.view_image_context_menu_id).setIntent(
                         new Intent(Intent.ACTION_VIEW, Uri.parse(extra)));
                 menu.findItem(R.id.download_context_menu_id).
@@ -1757,22 +1747,11 @@
         }
     }
 
-    // Used by attachTabToContentView for the WebView's ZoomControl widget.
-    private static final FrameLayout.LayoutParams ZOOM_PARAMS =
-            new FrameLayout.LayoutParams(
-                    ViewGroup.LayoutParams.FILL_PARENT,
-                    ViewGroup.LayoutParams.WRAP_CONTENT,
-                    Gravity.BOTTOM);
-
     // Attach the given tab to the content view.
     private void attachTabToContentView(TabControl.Tab t) {
         final WebView main = t.getWebView();
         // Attach the main WebView.
         mContentView.addView(main, COVER_SCREEN_PARAMS);
-        // Attach the Zoom control widget and hide it.
-        final View zoom = main.getZoomControls();
-        mContentView.addView(zoom, ZOOM_PARAMS);
-        zoom.setVisibility(View.GONE);
         // Attach the sub window if necessary
         attachSubWindow(t);
         // Request focus on the top window.
@@ -1792,8 +1771,7 @@
 
     // Remove the given tab from the content view.
     private void removeTabFromContentView(TabControl.Tab t) {
-        // Remove the Zoom widget and the main WebView.
-        mContentView.removeView(t.getWebView().getZoomControls());
+        // Remove the main WebView.
         mContentView.removeView(t.getWebView());
         // Remove the sub window if it exists.
         if (t.getSubWebView() != null) {
@@ -1838,7 +1816,13 @@
         // Send the animate message.
         final HashMap map = new HashMap();
         map.put("view", view);
-        map.put("url", url);
+        // Load the url after the AnimatingView has captured the picture. This
+        // prevents any bad layout or bad scale from being used during
+        // animation.
+        if (url != null) {
+            dismissSubWindow(tab);
+            tab.getWebView().loadUrl(url);
+        }
         map.put("msg", msg);
         mHandler.sendMessageDelayed(mHandler.obtainMessage(
                 ANIMATE_FROM_OVERVIEW, newTab ? 1 : 0, 0, map), delay);
@@ -1858,6 +1842,10 @@
 
     // Called by TabControl when a tab is requesting focus
     /* package */ void showTab(TabControl.Tab t) {
+        showTab(t, null);
+    }
+
+    private void showTab(TabControl.Tab t, String url) {
         // Disallow focus change during a tab animation.
         if (mAnimationCount > 0) {
             return;
@@ -1869,7 +1857,7 @@
             delay = TAB_ANIMATION_DURATION + TAB_OVERVIEW_DELAY;
             tabPicker(false, mTabControl.getTabIndex(t), false);
         }
-        sendAnimateFromOverview(t, false, null, delay, null);
+        sendAnimateFromOverview(t, false, url, delay, null);
     }
 
     // This method does a ton of stuff. It will attempt to create a new tab
@@ -1881,7 +1869,7 @@
     // method is called from TabListener.onClick(), the method will animate
     // away from the tab overview.
     private void openTabAndShow(String url, final Message msg,
-            boolean closeOnExit) {
+            boolean closeOnExit, String appId) {
         final boolean newTab = mTabControl.getTabCount() != TabControl.MAX_TABS;
         final TabControl.Tab currentTab = mTabControl.getCurrentTab();
         if (newTab) {
@@ -1909,8 +1897,9 @@
                 }
                 // Animate from the Tab overview after any animations have
                 // finished.
-                sendAnimateFromOverview(mTabControl.createNewTab(closeOnExit),
-                        true, url, delay, msg);
+                sendAnimateFromOverview(
+                        mTabControl.createNewTab(closeOnExit, appId, url), true,
+                        url, delay, msg);
             }
         } else if (url != null) {
             // We should not have a msg here.
@@ -1987,15 +1976,17 @@
                             public void run() {
                                 // Remove the AnimatingView.
                                 mContentView.removeView(view);
-                                // Make newIndex visible.
-                                mTabOverview.setCurrentIndex(newIndex);
-                                // Restore the listener.
-                                mTabOverview.setListener(mTabListener);
-                                // Change the menu to TAB_MENU if the
-                                // ImageGrid is interactive.
-                                if (mTabOverview.isLive()) {
-                                    mMenuState = R.id.TAB_MENU;
-                                    mTabOverview.requestFocus();
+                                if (mTabOverview != null) {
+                                    // Make newIndex visible.
+                                    mTabOverview.setCurrentIndex(newIndex);
+                                    // Restore the listener.
+                                    mTabOverview.setListener(mTabListener);
+                                    // Change the menu to TAB_MENU if the
+                                    // ImageGrid is interactive.
+                                    if (mTabOverview.isLive()) {
+                                        mMenuState = R.id.TAB_MENU;
+                                        mTabOverview.requestFocus();
+                                    }
                                 }
                                 // If a remove was requested, remove the tab.
                                 if (remove) {
@@ -2013,10 +2004,12 @@
                                     if (currentTab != tab) {
                                         mTabControl.setCurrentTab(currentTab);
                                     }
-                                    mTabOverview.remove(newIndex);
-                                    // Make the current tab visible.
-                                    mTabOverview.setCurrentIndex(
-                                            mTabControl.getCurrentIndex());
+                                    if (mTabOverview != null) {
+                                        mTabOverview.remove(newIndex);
+                                        // Make the current tab visible.
+                                        mTabOverview.setCurrentIndex(
+                                                mTabControl.getCurrentIndex());
+                                    }
                                 }
                             }
                         });
@@ -2041,7 +2034,7 @@
     // Animate from the tab picker. The index supplied is the index to animate
     // from.
     private void animateFromTabOverview(final AnimatingView view,
-            final boolean newTab, final String url, final Message msg) {
+            final boolean newTab, final Message msg) {
         // firstVisible is the first visible tab on the screen.  This helps
         // to know which corner of the screen the selected tab is.
         int firstVisible = mTabOverview.getFirstVisiblePosition();
@@ -2063,7 +2056,7 @@
         // Find the view at this location.
         final View v = mTabOverview.getChildAt(location);
 
-        // Wait until the animation completes to load the url.
+        // Wait until the animation completes to replace the AnimatingView.
         final Animation.AnimationListener l =
                 new Animation.AnimationListener() {
                     public void onAnimationStart(Animation a) {}
@@ -2078,15 +2071,9 @@
                                 dismissTabOverview(v == null);
                                 TabControl.Tab t =
                                         mTabControl.getCurrentTab();
-                                WebView w = t.getWebView();
-                                if (url != null) {
-                                    // Dismiss the subwindow if one exists.
-                                    dismissSubWindow(t);
-                                    w.loadUrl(url);
-                                }
                                 mMenuState = R.id.MAIN_MENU;
                                 // Resume regular updates.
-                                w.resumeTimers();
+                                t.getWebView().resumeTimers();
                                 // Dispatch the message after the animation
                                 // completes.
                                 if (msg != null) {
@@ -2111,7 +2098,7 @@
             // Make the view VISIBLE during the animation.
             view.setVisibility(View.VISIBLE);
         } else {
-            // Go ahead and load the url.
+            // Go ahead and do all the cleanup.
             l.onAnimationEnd(null);
         }
     }
@@ -2126,7 +2113,12 @@
         }
         // Just in case there was a problem with animating away from the tab
         // overview
-        mTabControl.getCurrentWebView().setVisibility(View.VISIBLE);
+        WebView current = mTabControl.getCurrentWebView();
+        if (current != null) {
+            current.setVisibility(View.VISIBLE);
+        } else {
+            Log.e(LOGTAG, "No current WebView in dismissTabOverview");
+        }
         // Make the sub window container visible.
         if (mTabControl.getCurrentSubWindow() != null) {
             mTabControl.getCurrentTab().getSubWebViewContainer()
@@ -2140,12 +2132,12 @@
 
     private void openTab(String url) {
         if (mSettings.openInBackground()) {
-            TabControl.Tab t = mTabControl.createNewTab(false);
+            TabControl.Tab t = mTabControl.createNewTab();
             if (t != null) {
                 t.getWebView().loadUrl(url);
             }
         } else {
-            openTabAndShow(url, null, false);
+            openTabAndShow(url, null, false, null);
         }
     }
 
@@ -2218,8 +2210,6 @@
         }
         resetTitleAndIcon(current);
         int progress = current.getProgress();
-        mInLoad = (progress != 100);
-        updateInLoadMenuItems();
         mWebChromeClient.onProgressChanged(current, progress);
     }
 
@@ -2450,7 +2440,7 @@
                 return KeyTracker.State.DONE_TRACKING;
             }
             if (stage == KeyTracker.Stage.LONG_REPEAT) {
-                loadHistory();
+                bookmarksOrHistoryPicker(true);
                 return KeyTracker.State.DONE_TRACKING;
             } else if (stage == KeyTracker.Stage.UP) {
                 // FIXME: Currently, we do not have a notion of the
@@ -2522,13 +2512,6 @@
         }
     }
 
-    private void loadHistory() {
-        Intent intent = new Intent(this, BrowserHistoryPage.class);
-        intent.putExtra("maxTabsOpen",
-                mTabControl.getTabCount() >= TabControl.MAX_TABS);
-        startActivityForResult(intent, CLASSIC_HISTORY_PAGE);
-    }
-
     // called by a non-UI thread to post the message
     public void postMessage(int what, int arg1, int arg2, Object obj) {
         mHandler.sendMessage(mHandler.obtainMessage(what, arg1, arg2, obj));
@@ -2555,8 +2538,7 @@
                 case ANIMATE_FROM_OVERVIEW:
                     final HashMap map = (HashMap) msg.obj;
                     animateFromTabOverview((AnimatingView) map.get("view"),
-                            msg.arg1 == 1, (String) map.get("url"),
-                            (Message) map.get("msg"));
+                            msg.arg1 == 1, (Message) map.get("msg"));
                     break;
 
                 case ANIMATE_TO_OVERVIEW:
@@ -2569,7 +2551,7 @@
                     // the method relies on the value being 0 to start the next
                     // animation.
                     mAnimationCount--;
-                    openTabAndShow((String) msg.obj, null, false);
+                    openTabAndShow((String) msg.obj, null, false, null);
                     break;
 
                 case FOCUS_NODE_HREF:
@@ -2723,6 +2705,18 @@
 
             mInLoad = true;
             updateInLoadMenuItems();
+            if (!mIsNetworkUp) {
+                if ( mAlertDialog == null) {
+                    mAlertDialog = new AlertDialog.Builder(BrowserActivity.this)
+                        .setTitle(R.string.loadSuspendedTitle)
+                        .setMessage(R.string.loadSuspended)
+                        .setPositiveButton(R.string.ok, null)
+                        .show();
+                }
+                if (view != null) {
+                    view.setNetworkAvailable(false);
+                }
+            }
 
             // schedule to check memory condition
             mHandler.sendMessageDelayed(mHandler.obtainMessage(CHECK_MEMORY),
@@ -2739,7 +2733,7 @@
             updateLockIconImage(mLockIconType);
 
             // Performance probe
-             if (false) {
+            if (false) {
                 long[] sysCpu = new long[7];
                 if (Process.readProcFile("/proc/stat", SYSTEM_CPU_FORMAT, null,
                         sysCpu, null)) {
@@ -2834,11 +2828,6 @@
                 }
             }
 
-            if (mInLoad) {
-                mInLoad = false;
-                updateInLoadMenuItems();
-            }
-
             mHandler.removeMessages(CHECK_MEMORY);
             checkMemory();
         }
@@ -3180,7 +3169,7 @@
                 // openTabAndShow will dispatch the message after creating the
                 // new WebView. This will prevent another request from coming
                 // in during the animation.
-                openTabAndShow(null, msg, false);
+                openTabAndShow(null, msg, false, null);
                 parent.addChildTab(mTabControl.getCurrentTab());
                 WebView.WebViewTransport transport =
                         (WebView.WebViewTransport) msg.obj;
@@ -3296,6 +3285,18 @@
                 // onPageFinished() is only called for the main frame. sync
                 // cookie and cache promptly here.
                 CookieSyncManager.getInstance().sync();
+                if (mInLoad) {
+                    mInLoad = false;
+                    updateInLoadMenuItems();
+                }
+            } else {
+                // onPageFinished may have already been called but a subframe
+                // is still loading and updating the progress. Reset mInLoad
+                // and update the menu items.
+                if (!mInLoad) {
+                    mInLoad = true;
+                    updateInLoadMenuItems();
+                }
             }
         }
 
@@ -3367,8 +3368,8 @@
             //     that matches.
             Intent intent = new Intent(Intent.ACTION_VIEW);
             intent.setDataAndType(Uri.parse(url), mimetype);
-            if (getPackageManager().queryIntentActivities(intent,
-                        PackageManager.MATCH_DEFAULT_ONLY).size() != 0) {
+            if (getPackageManager().resolveActivity(intent,
+                        PackageManager.MATCH_DEFAULT_ONLY) != null) {
                 // someone knows how to handle this mime type with this scheme, don't download.
                 try {
                     startActivity(intent);
@@ -3424,10 +3425,44 @@
             return;
         }
 
+        // java.net.URI is a lot stricter than KURL so we have to undo
+        // KURL's percent-encoding and redo the encoding using java.net.URI.
+        URI uri = null;
+        try {
+            // Undo the percent-encoding that KURL may have done.
+            String newUrl = new String(URLUtil.decode(url.getBytes()));
+            // Parse the url into pieces
+            WebAddress w = new WebAddress(newUrl);
+            String frag = null;
+            String query = null;
+            String path = w.mPath;
+            // Break the path into path, query, and fragment
+            if (path.length() > 0) {
+                // Strip the fragment
+                int idx = path.lastIndexOf('#');
+                if (idx != -1) {
+                    frag = path.substring(idx + 1);
+                    path = path.substring(0, idx);
+                }
+                idx = path.lastIndexOf('?');
+                if (idx != -1) {
+                    query = path.substring(idx + 1);
+                    path = path.substring(0, idx);
+                }
+            }
+            uri = new URI(w.mScheme, w.mAuthInfo, w.mHost, w.mPort, path,
+                    query, frag);
+        } catch (Exception e) {
+            Log.e(LOGTAG, "Could not parse url for download: " + url, e);
+            return;
+        }
+
+        // XXX: Have to use the old url since the cookies were stored using the
+        // old percent-encoded url.
         String cookies = CookieManager.getInstance().getCookie(url);
 
         ContentValues values = new ContentValues();
-        values.put(Downloads.COLUMN_URI, url);
+        values.put(Downloads.COLUMN_URI, uri.toString());
         values.put(Downloads.COLUMN_COOKIE_DATA, cookies);
         values.put(Downloads.COLUMN_USER_AGENT, userAgent);
         values.put(Downloads.COLUMN_NOTIFICATION_PACKAGE,
@@ -3437,7 +3472,7 @@
         values.put(Downloads.COLUMN_VISIBILITY, Downloads.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
         values.put(Downloads.COLUMN_MIME_TYPE, mimetype);
         values.put(Downloads.COLUMN_FILE_NAME_HINT, filename);
-        values.put(Downloads.COLUMN_DESCRIPTION, Uri.parse(url).getHost());
+        values.put(Downloads.COLUMN_DESCRIPTION, uri.getHost());
         if (contentLength > 0) {
             values.put(Downloads.COLUMN_TOTAL_BYTES, contentLength);
         }
@@ -3884,7 +3919,7 @@
         }
 
         mHttpAuthHandler = handler;
-        mHttpAuthenticationDialog = new AlertDialog.Builder(this)
+        AlertDialog dialog = new AlertDialog.Builder(this)
                 .setTitle(titleText)
                 .setIcon(android.R.drawable.ic_dialog_alert)
                 .setView(v)
@@ -3920,12 +3955,17 @@
                             mHttpAuthenticationDialog = null;
                             mHttpAuthHandler = null;
                         }})
-                .show();
+                .create();
+        // Make the IME appear when the dialog is displayed if applicable.
+        dialog.getWindow().setSoftInputMode(
+                WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
+        dialog.show();
         if (focusId != 0) {
-            mHttpAuthenticationDialog.findViewById(focusId).requestFocus();
+            dialog.findViewById(focusId).requestFocus();
         } else {
             v.findViewById(R.id.username_edit).requestFocus();
         }
+        mHttpAuthenticationDialog = dialog;
     }
 
     public int getProgress() {
@@ -3956,16 +3996,20 @@
     }
 
     /**
-     * http stack says net has come or gone... inform the user
+     * connectivity manager says net has come or gone... inform the user
      * @param up true if net has come up, false if net has gone down
      */
     public void onNetworkToggle(boolean up) {
-        if (up) {
+        if (up == mIsNetworkUp) {
+            return;
+        } else if (up) {
+            mIsNetworkUp = true;
             if (mAlertDialog != null) {
                 mAlertDialog.cancel();
                 mAlertDialog = null;
             }
         } else {
+            mIsNetworkUp = false;
             if (mInLoad && mAlertDialog == null) {
                 mAlertDialog = new AlertDialog.Builder(this)
                         .setTitle(R.string.loadSuspendedTitle)
@@ -3984,8 +4028,7 @@
     protected void onActivityResult(int requestCode, int resultCode,
                                     Intent intent) {
         switch (requestCode) {
-            case BOOKMARKS_PAGE:
-            case CLASSIC_HISTORY_PAGE:
+            case COMBO_PAGE:
                 if (resultCode == RESULT_OK && intent != null) {
                     String data = intent.getAction();
                     Bundle extras = intent.getExtras();
@@ -4048,7 +4091,7 @@
                 // If the user removes the last tab, act like the New Tab item
                 // was clicked on.
                 if (mTabControl.getTabCount() == 0) {
-                    current = mTabControl.createNewTab(false);
+                    current = mTabControl.createNewTab();
                     sendAnimateFromOverview(current, true,
                             mSettings.getHomePage(), TAB_OVERVIEW_DELAY, null);
                 } else {
@@ -4088,7 +4131,7 @@
 
             // NEW_TAB means that the "New Tab" cell was clicked on.
             if (index == ImageGrid.NEW_TAB) {
-                openTabAndShow(mSettings.getHomePage(), null, false);
+                openTabAndShow(mSettings.getHomePage(), null, false, null);
             } else {
                 sendAnimateFromOverview(mTabControl.getTab(index),
                         false, null, 0, null);
@@ -4202,13 +4245,13 @@
         mMenuState = EMPTY_MENU;
     }
 
-    private void bookmarksPicker() {
+    private void bookmarksOrHistoryPicker(boolean startWithHistory) {
         WebView current = mTabControl.getCurrentWebView();
         if (current == null) {
             return;
         }
         Intent intent = new Intent(this,
-                BrowserBookmarksPage.class);
+                CombinedBookmarkHistoryActivity.class);
         String title = current.getTitle();
         String url = current.getUrl();
         // Just in case the user opens bookmarks before a page finishes loading
@@ -4228,7 +4271,11 @@
         intent.putExtra("url", url);
         intent.putExtra("maxTabsOpen",
                 mTabControl.getTabCount() >= TabControl.MAX_TABS);
-        startActivityForResult(intent, BOOKMARKS_PAGE);
+        if (startWithHistory) {
+            intent.putExtra(CombinedBookmarkHistoryActivity.STARTING_TAB,
+                    CombinedBookmarkHistoryActivity.HISTORY_TAB);
+        }
+        startActivityForResult(intent, COMBO_PAGE);
     }
 
     // Called when loading from context menu or LOAD_URL message
@@ -4351,7 +4398,7 @@
         return composeSearchUrl(inUrl);
     }
 
-    /* package */static String composeSearchUrl(String search) {
+    /* package */ String composeSearchUrl(String search) {
         return URLUtil.composeSearchUrl(search, QuickSearch_G,
                 QUERY_PLACE_HOLDER);
     }
@@ -4376,7 +4423,7 @@
                     R.string.google_search_base, l.getLanguage(),
                     l.getCountry().toLowerCase())
                     + "client=ms-"
-                    + SystemProperties.get("ro.com.google.clientid", "unknown")
+                    + SystemProperties.get("persist.sys.com.google.clientid", "unknown")
                     + "&source=android-" + GOOGLE_SEARCH_SOURCE_SUGGEST + "&q=%s";
         } else {
             QuickSearch_G = url;
@@ -4409,6 +4456,7 @@
     boolean mCanChord;
 
     private boolean mInLoad;
+    private boolean mIsNetworkUp;
 
     private boolean mPageStarted;
     private boolean mActivityInPause = true;
@@ -4503,8 +4551,6 @@
 
     // "source" parameter for Google search through search key
     final static String GOOGLE_SEARCH_SOURCE_SEARCHKEY = "browser-key";
-    // "source" parameter for Google search through search menu
-    final static String GOOGLE_SEARCH_SOURCE_SEARCHMENU = "browser-menu";
     // "source" parameter for Google search through goto menu
     final static String GOOGLE_SEARCH_SOURCE_GOTO = "browser-goto";
     // "source" parameter for Google search through simplily type
@@ -4543,10 +4589,9 @@
     private BroadcastReceiver mNetworkStateIntentReceiver;
 
     // activity requestCode
-    final static int BOOKMARKS_PAGE         = 1;
-    final static int CLASSIC_HISTORY_PAGE   = 2;
-    final static int DOWNLOAD_PAGE          = 3;
-    final static int PREFERENCES_PAGE       = 4;
+    final static int COMBO_PAGE             = 1;
+    final static int DOWNLOAD_PAGE          = 2;
+    final static int PREFERENCES_PAGE       = 3;
 
     // the frenquency of checking whether system memory is low
     final static int CHECK_MEMORY_INTERVAL = 30000;     // 30 seconds
diff --git a/src/com/android/browser/BrowserBookmarksPage.java b/src/com/android/browser/BrowserBookmarksPage.java
index 5c509a8..a7d45f9 100644
--- a/src/com/android/browser/BrowserBookmarksPage.java
+++ b/src/com/android/browser/BrowserBookmarksPage.java
@@ -162,7 +162,7 @@
         }
 
         mBookmarksAdapter = new BrowserBookmarksAdapter(this, 
-                getIntent().getStringExtra("title"), mCreateShortcut);
+                getIntent().getStringExtra("url"), mCreateShortcut);
         mMaxTabsOpen = getIntent().getBooleanExtra("maxTabsOpen", false);
 
         ListView listView = (ListView) findViewById(R.id.list);
@@ -204,7 +204,7 @@
             } else {
                 final Intent intent = createShortcutIntent(getUrl(position),
                         getBookmarkTitle(position));
-                setResult(RESULT_OK, intent);
+                setResultToParent(RESULT_OK, intent);
                 finish();
             }
         }
@@ -212,8 +212,13 @@
 
     private Intent createShortcutIntent(String url, String title) {
         final Intent i = new Intent();
-        i.putExtra(Intent.EXTRA_SHORTCUT_INTENT, new Intent(Intent.ACTION_VIEW,
-                    Uri.parse(url)));
+        final Intent shortcutIntent = new Intent(Intent.ACTION_VIEW,
+                Uri.parse(url));
+        long urlHash = url.hashCode();
+        long uniqueId = (urlHash << 32) | shortcutIntent.hashCode();
+        shortcutIntent.putExtra(Browser.EXTRA_APPLICATION_ID,
+                Long.toString(uniqueId));
+        i.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
         i.putExtra(Intent.EXTRA_SHORTCUT_NAME, title);
         i.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
                 Intent.ShortcutIconResource.fromContext(BrowserBookmarksPage.this,
@@ -231,7 +236,8 @@
     }
 
     private void loadUrl(int position) {
-        setResult(RESULT_OK, (new Intent()).setAction(getUrl(position)));
+        Intent intent = (new Intent()).setAction(getUrl(position));
+        setResultToParent(RESULT_OK, intent);
         finish();
     }
 
@@ -262,7 +268,7 @@
     private void openInNewWindow(int position) {
         Bundle b = new Bundle();
         b.putBoolean("new_window", true);
-        setResult(RESULT_OK,
+        setResultToParent(RESULT_OK,
                 (new Intent()).setAction(getUrl(position)).putExtras(b));
 
         finish();
@@ -367,9 +373,18 @@
     
     public boolean dispatchKeyEvent(KeyEvent event) {    
         if (event.getKeyCode() ==  KeyEvent.KEYCODE_BACK && event.isDown()) {
-            setResult(RESULT_CANCELED);
+            setResultToParent(RESULT_CANCELED, null);
             mCanceled = true;
         }
         return super.dispatchKeyEvent(event);
     }
+
+    // This Activity is generally a sub-Activity of CombinedHistoryActivity. In
+    // that situation, we need to pass our result code up to our parent.
+    // However, if someone calls this Activity directly, then this has no
+    // parent, and it needs to set it on itself.
+    private void setResultToParent(int resultCode, Intent data) {
+        Activity a = getParent() == null ? this : getParent();
+        a.setResult(resultCode, data);
+    }
 }
diff --git a/src/com/android/browser/BrowserHistoryPage.java b/src/com/android/browser/BrowserHistoryPage.java
index 3059126..42ca848 100644
--- a/src/com/android/browser/BrowserHistoryPage.java
+++ b/src/com/android/browser/BrowserHistoryPage.java
@@ -16,14 +16,17 @@
 
 package com.android.browser;
 
-import android.app.ListActivity;
+import android.app.Activity;
+import android.app.ExpandableListActivity;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.database.ContentObserver;
 import android.database.Cursor;
 import android.database.DataSetObserver;
 import android.graphics.Bitmap;
 import android.os.Bundle;
+import android.os.Handler;
 import android.os.ServiceManager;
 import android.provider.Browser;
 import android.text.IClipboard;
@@ -41,11 +44,11 @@
 import android.webkit.DateSorter;
 import android.webkit.WebIconDatabase.IconListener;
 import android.widget.AdapterView;
-import android.widget.ListAdapter;
-import android.widget.ListView;
+import android.widget.ExpandableListAdapter;
+import android.widget.ExpandableListView;
+import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
 import android.widget.TextView;
 
-import java.util.HashMap;
 import java.util.List;
 import java.util.Vector;
 
@@ -53,22 +56,16 @@
  * Activity for displaying the browser's history, divided into
  * days of viewing.
  */
-public class BrowserHistoryPage extends ListActivity {
+public class BrowserHistoryPage extends ExpandableListActivity {
     private HistoryAdapter          mAdapter;
     private DateSorter              mDateSorter;
     private boolean                 mMaxTabsOpen;
 
     private final static String LOGTAG = "browser";
 
-    // FIXME: Make this a part of Browser so we do not have more than one
-    // copy (other copy is in BrowserBookmarksAdapter).
-    // Used to store favicons as we get them from the database
-    private final HashMap<String, Bitmap> mUrlsToIcons =
-            new HashMap<String, Bitmap>();
     // Implementation of WebIconDatabase.IconListener
     private class IconReceiver implements IconListener {
         public void onReceivedIcon(String url, Bitmap icon) {
-            mUrlsToIcons.put(url, icon);
             setListAdapter(mAdapter);
         }
     }
@@ -87,7 +84,7 @@
             b.putBoolean("new_window", true);
             intent.putExtras(b);
         }
-        setResult(RESULT_OK, intent);
+        setResultToParent(RESULT_OK, intent);
         finish();
     }
     
@@ -111,20 +108,32 @@
 
         mAdapter = new HistoryAdapter();
         setListAdapter(mAdapter);
-        ListView list = getListView();
+        final ExpandableListView list = getExpandableListView();
         list.setOnCreateContextMenuListener(this);
         LayoutInflater factory = LayoutInflater.from(this);
         View v = factory.inflate(R.layout.empty_history, null);
         addContentView(v, new LayoutParams(LayoutParams.FILL_PARENT,
                 LayoutParams.FILL_PARENT));
         list.setEmptyView(v);
-
+        // Do not post the runnable if there is nothing in the list.
+        if (list.getExpandableListAdapter().getGroupCount() > 0) {
+            list.post(new Runnable() {
+                public void run() {
+                    // In case the history gets cleared before this event
+                    // happens.
+                    if (list.getExpandableListAdapter().getGroupCount() > 0) {
+                        list.expandGroup(0);
+                    }
+                }
+            });
+        }
         mMaxTabsOpen = getIntent().getBooleanExtra("maxTabsOpen", false);
-        Browser.requestAllIcons(getContentResolver(), null, mIconReceiver);
+        CombinedBookmarkHistoryActivity.getIconListenerSet(getContentResolver())
+                .addListener(mIconReceiver);
         
         // initialize the result to canceled, so that if the user just presses
         // back then it will have the correct result
-        setResult(RESULT_CANCELED);
+        setResultToParent(RESULT_CANCELED, null);
     }
 
     @Override
@@ -160,9 +169,12 @@
     @Override
     public void onCreateContextMenu(ContextMenu menu, View v,
             ContextMenuInfo menuInfo) {
-        AdapterView.AdapterContextMenuInfo i = 
-            (AdapterView.AdapterContextMenuInfo)
-            menuInfo;
+        ExpandableListContextMenuInfo i = 
+            (ExpandableListContextMenuInfo) menuInfo;
+        // Do not allow a context menu to come up from the group views.
+        if (!(i.targetView instanceof HistoryItem)) {
+            return;
+        }
 
         // Inflate the menu
         MenuInflater inflater = getMenuInflater();
@@ -174,22 +186,20 @@
         // Only show open in new tab if we have not maxed out available tabs
         menu.findItem(R.id.new_window_context_menu_id).setVisible(!mMaxTabsOpen);
         
-     // decide whether to show the share link option
+        // decide whether to show the share link option
         PackageManager pm = getPackageManager();
         Intent send = new Intent(Intent.ACTION_SEND);
         send.setType("text/plain");
-        List<ResolveInfo> list = pm.queryIntentActivities(send,
-                PackageManager.MATCH_DEFAULT_ONLY);
-        menu.findItem(R.id.share_link_context_menu_id).setVisible(
-                list.size() > 0);
+        ResolveInfo ri = pm.resolveActivity(send, PackageManager.MATCH_DEFAULT_ONLY);
+        menu.findItem(R.id.share_link_context_menu_id).setVisible(ri != null);
         
         super.onCreateContextMenu(menu, v, menuInfo);
     }
     
     @Override
     public boolean onContextItemSelected(MenuItem item) {
-        AdapterView.AdapterContextMenuInfo i = 
-            (AdapterView.AdapterContextMenuInfo)item.getMenuInfo();
+        ExpandableListContextMenuInfo i = 
+            (ExpandableListContextMenuInfo) item.getMenuInfo();
         String url = ((HistoryItem)i.targetView).getUrl();
         String title = ((HistoryItem)i.targetView).getName();
         switch (item.getItemId()) {
@@ -219,17 +229,48 @@
     }
     
     @Override
-    protected void onListItemClick(ListView l, View v, int position, long id) {
+    public boolean onChildClick(ExpandableListView parent, View v,
+            int groupPosition, int childPosition, long id) {
         if (v instanceof HistoryItem) {
             loadUrl(((HistoryItem) v).getUrl(), false);
+            return true;
         }
+        return false;
     }
 
-    private class HistoryAdapter implements ListAdapter {
+    // This Activity is generally a sub-Activity of CombinedHistoryActivity. In
+    // that situation, we need to pass our result code up to our parent.
+    // However, if someone calls this Activity directly, then this has no
+    // parent, and it needs to set it on itself.
+    private void setResultToParent(int resultCode, Intent data) {
+        Activity a = getParent() == null ? this : getParent();
+        a.setResult(resultCode, data);
+    }
+
+    private class ChangeObserver extends ContentObserver {
+        public ChangeObserver() {
+            super(new Handler());
+        }
+
+        @Override
+        public boolean deliverSelfNotifications() {
+            return true;
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            mAdapter.refreshData();
+        }
+    }
+    
+    private class HistoryAdapter implements ExpandableListAdapter {
         
-        // Map of items. Negative values are labels, positive values
-        // and zero are cursor offsets.
+        // Array for each of our bins.  Each entry represents how many items are
+        // in that bin.
         int mItemMap[];
+        // This is our GroupCount.  We will have at most DateSorter.DAY_COUNT
+        // bins, less if the user has no items in one or more bins.
+        int mNumberOfBins;
         Vector<DataSetObserver> mObservers;
         Cursor mCursor;
         
@@ -245,6 +286,7 @@
                     whereClause, null, orderBy);
             
             buildMap();
+            mCursor.registerContentObserver(new ChangeObserver());
         }
         
         void refreshData() {
@@ -255,107 +297,140 @@
             }
         }
         
-        public void buildMap() {
+        private void buildMap() {
             // The cursor is sorted by date
-            // Make one pass to build up the ItemMap with the inserted 
-            // section separators. 
-            int array[] = new int[mCursor.getCount() + DateSorter.DAY_COUNT];
+            // The ItemMap will store the number of items in each bin.
+            int array[] = new int[DateSorter.DAY_COUNT];
+            // Zero out the array.
+            for (int j = 0; j < DateSorter.DAY_COUNT; j++) {
+                array[j] = 0;
+            }
+            mNumberOfBins = 0;
             int dateIndex = -1;
             if (mCursor.moveToFirst() && mCursor.getCount() > 0) {
-                int itemIndex = 0;
                 while (!mCursor.isAfterLast()) {
                     long date = mCursor.getLong(Browser.HISTORY_PROJECTION_DATE_INDEX);
                     int index = mDateSorter.getIndex(date);
                     if (index > dateIndex) {
+                        mNumberOfBins++;
+                        if (index == DateSorter.DAY_COUNT - 1) {
+                            // We are already in the last bin, so it will
+                            // include all the remaining items
+                            array[index] = mCursor.getCount()
+                                    - mCursor.getPosition();
+                            break;
+                        }
                         dateIndex = index;
-                        array[itemIndex] = dateIndex - DateSorter.DAY_COUNT;
-                        itemIndex++;
                     }
-                    array[itemIndex] = mCursor.getPosition();
-                    itemIndex++;
+                    array[dateIndex]++;
                     mCursor.moveToNext();
                 }
-            } else {
-                // The db is empty, just add the heading for the first item
-                dateIndex = 0;
-                array[0] = dateIndex - DateSorter.DAY_COUNT;
             }
-            // Compress the array as the trailing date sections may be
-            // empty
-            int extraEntries = DateSorter.DAY_COUNT - dateIndex - 1;
-            if (extraEntries > 0) {
-                int newArraySize = array.length - extraEntries;
-                mItemMap = new int[newArraySize];
-                System.arraycopy(array, 0, mItemMap, 0, newArraySize);
-            } else {
-                mItemMap = array;
-            }
+            mItemMap = array;
         }
 
-        public View getView(int position, View convertView, ViewGroup parent) {
-            if (mItemMap[position] < 0) {
-                return getHeaderView(position, convertView);
+        // This translates from a group position in the Adapter to a position in
+        // our array.  This is necessary because some positions in the array
+        // have no history items, so we simply do not present those positions
+        // to the Adapter.
+        private int groupPositionToArrayPosition(int groupPosition) {
+            if (groupPosition < 0 || groupPosition >= DateSorter.DAY_COUNT) {
+                throw new AssertionError("group position out of range");
             }
-            return getHistoryItem(position, convertView);
+            if (DateSorter.DAY_COUNT == mNumberOfBins || 0 == mNumberOfBins) {
+                // In the first case, we have exactly the same number of bins
+                // as our maximum possible, so there is no need to do a
+                // conversion
+                // The second statement is in case this method gets called when
+                // the array is empty, in which case the provided groupPosition
+                // will do fine.
+                return groupPosition;
+            }
+            int arrayPosition = -1;
+            while (groupPosition > -1) {
+                arrayPosition++;
+                if (mItemMap[arrayPosition] != 0) {
+                    groupPosition--;
+                }
+            }
+            return arrayPosition;
         }
-        
-        View getHistoryItem(int position, View convertView) {
+
+        public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
+                View convertView, ViewGroup parent) {
+            groupPosition = groupPositionToArrayPosition(groupPosition);
             HistoryItem item;
             if (null == convertView || !(convertView instanceof HistoryItem)) {
                 item = new HistoryItem(BrowserHistoryPage.this);
+                // Add padding on the left so it will be indented from the
+                // arrows on the group views.
+                item.setPadding(item.getPaddingLeft() + 10,
+                        item.getPaddingTop(),
+                        item.getPaddingRight(),
+                        item.getPaddingBottom());
             } else {
                 item = (HistoryItem) convertView;
             }
-            mCursor.moveToPosition(mItemMap[position]);
+            int index = childPosition;
+            for (int i = 0; i < groupPosition; i++) {
+                index += mItemMap[i];
+            }
+            mCursor.moveToPosition(index);
             item.setName(mCursor.getString(Browser.HISTORY_PROJECTION_TITLE_INDEX));
             String url = mCursor.getString(Browser.HISTORY_PROJECTION_URL_INDEX);
             item.setUrl(url);
-            item.setFavicon((Bitmap) mUrlsToIcons.get(url));
+            item.setFavicon(CombinedBookmarkHistoryActivity.getIconListenerSet(
+                    getContentResolver()).getFavicon(url));
+            item.setIsBookmark(1 ==
+                    mCursor.getInt(Browser.HISTORY_PROJECTION_BOOKMARK_INDEX));
             return item;
         }
         
-        View getHeaderView(int position, View convertView) {
+        public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
+            groupPosition = groupPositionToArrayPosition(groupPosition);
             TextView item;
             if (null == convertView || !(convertView instanceof TextView)) {
                 LayoutInflater factory = 
                         LayoutInflater.from(BrowserHistoryPage.this);
                 item = (TextView) 
-                        factory.inflate(android.R.layout.preference_category, null);
+                        factory.inflate(R.layout.history_header, null);
             } else {
                 item = (TextView) convertView;
             }
-            item.setText(mDateSorter.getLabel(
-                    mItemMap[position] + DateSorter.DAY_COUNT));
+            item.setText(mDateSorter.getLabel(groupPosition));
             return item;
         }
 
         public boolean areAllItemsEnabled() {
-            return false;
+            return true;
         }
 
-        public boolean isEnabled(int position) {
-            return mItemMap[position] >= 0;
+        public boolean isChildSelectable(int groupPosition, int childPosition) {
+            return true;
         }
 
-        public int getCount() {
-            return mItemMap.length;
+        public int getGroupCount() {
+            return mNumberOfBins;
         }
 
-        public Object getItem(int position) {
+        public int getChildrenCount(int groupPosition) {
+            return mItemMap[groupPositionToArrayPosition(groupPosition)];
+        }
+
+        public Object getGroup(int groupPosition) {
             return null;
         }
 
-        public long getItemId(int position) {
-            return mItemMap[position];
+        public Object getChild(int groupPosition, int childPosition) {
+            return null;
         }
 
-        // 0 for TextView, 1 for HistoryItem
-        public int getItemViewType(int position) {
-            return mItemMap[position] < 0 ? 0 : 1;
+        public long getGroupId(int groupPosition) {
+            return groupPosition;
         }
 
-        public int getViewTypeCount() {
-            return 2;
+        public long getChildId(int groupPosition, int childPosition) {
+            return (childPosition << 3) + groupPosition;
         }
 
         public boolean hasStableIds() {
@@ -370,8 +445,24 @@
             mObservers.remove(observer);
         }
 
+        public void onGroupExpanded(int groupPosition) {
+        
+        }
+
+        public void onGroupCollapsed(int groupPosition) {
+        
+        }
+
+        public long getCombinedChildId(long groupId, long childId) {
+            return childId;
+        }
+
+        public long getCombinedGroupId(long groupId) {
+            return groupId;
+        }
+
         public boolean isEmpty() {
-            return getCount() == 1;
+            return mCursor.getCount() == 0;
         }
     }
 }
diff --git a/src/com/android/browser/BrowserHomepagePreference.java b/src/com/android/browser/BrowserHomepagePreference.java
index bc21143..d4708c3 100644
--- a/src/com/android/browser/BrowserHomepagePreference.java
+++ b/src/com/android/browser/BrowserHomepagePreference.java
@@ -48,8 +48,10 @@
         AlertDialog dialog = (AlertDialog) getDialog();
         // This callback is called before the dialog has been fully constructed
         if (dialog != null) {
+            String url = s.toString();
             dialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(
-                    Regex.WEB_URL_PATTERN.matcher(s.toString()).matches());
+                    url.length() == 0 || url.equals("about:blank") ||
+                    Regex.WEB_URL_PATTERN.matcher(url).matches());
         }
     }
 
diff --git a/src/com/android/browser/BrowserPreferencesPage.java b/src/com/android/browser/BrowserPreferencesPage.java
index b23f750..5d6795b 100644
--- a/src/com/android/browser/BrowserPreferencesPage.java
+++ b/src/com/android/browser/BrowserPreferencesPage.java
@@ -83,8 +83,7 @@
             if (needUpdate) {
                 value = value.trim().replace(" ", "%20");
             }
-            Uri path = Uri.parse(value);
-            if (path.getScheme() == null) {
+            if (value.length() != 0 && Uri.parse(value).getScheme() == null) {
                 value = "http://" + value;
                 needUpdate = true;
             }
diff --git a/src/com/android/browser/BrowserProvider.java b/src/com/android/browser/BrowserProvider.java
index 7aa5bb2..0c930c6 100644
--- a/src/com/android/browser/BrowserProvider.java
+++ b/src/com/android/browser/BrowserProvider.java
@@ -16,23 +16,26 @@
 
 package com.android.browser;
 
+import android.app.ISearchManager;
 import android.app.SearchManager;
+import android.content.ComponentName;
 import android.content.ContentProvider;
-import android.content.ContentResolver;
 import android.content.ContentUris;
 import android.content.ContentValues;
 import android.content.Context;
+import android.content.Intent;
 import android.content.UriMatcher;
 import android.database.AbstractCursor;
-import android.database.ContentObserver;
 import android.database.Cursor;
-import android.database.DataSetObserver;
 import android.database.sqlite.SQLiteOpenHelper;
 import android.database.sqlite.SQLiteDatabase;
 import android.net.Uri;
+import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.SystemProperties;
 import android.provider.Browser;
 import android.util.Log;
+import android.server.search.SearchableInfo;
 import android.text.util.Regex;
 
 public class BrowserProvider extends ContentProvider {
@@ -40,22 +43,41 @@
     private SQLiteOpenHelper mOpenHelper;
     private static final String sDatabaseName = "browser.db";
     private static final String TAG = "BrowserProvider";
-    private static final String ORDER_BY = "date DESC";
+    private static final String ORDER_BY = "visits DESC, date DESC";
 
     private static final String[] TABLE_NAMES = new String[] {
         "bookmarks", "searches"
     };
-    private static final String[] SUGGEST_PROJECTION = new String [] {
-        "0 AS " + SearchManager.SUGGEST_COLUMN_FORMAT,
-        "url AS " + SearchManager.SUGGEST_COLUMN_INTENT_DATA,
-        "url AS " + SearchManager.SUGGEST_COLUMN_TEXT_1,
-        "title AS " + SearchManager.SUGGEST_COLUMN_TEXT_2,
-        "_id"
+    private static final String[] SUGGEST_PROJECTION = new String[] {
+            "_id", "url", "title", "bookmark"
     };
     private static final String SUGGEST_SELECTION = 
             "url LIKE ? OR url LIKE ? OR url LIKE ? OR url LIKE ?";
     private String[] SUGGEST_ARGS = new String[4];
 
+    // shared suggestion array index, make sure to match COLUMNS
+    private static final int SUGGEST_COLUMN_INTENT_ACTION_ID = 1;
+    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;
+
+    // shared suggestion columns
+    private static final String[] COLUMNS = new String[] {
+            "_id",
+            SearchManager.SUGGEST_COLUMN_INTENT_ACTION,
+            SearchManager.SUGGEST_COLUMN_INTENT_DATA,
+            SearchManager.SUGGEST_COLUMN_TEXT_1,
+            SearchManager.SUGGEST_COLUMN_TEXT_2,
+            SearchManager.SUGGEST_COLUMN_ICON_1,
+            SearchManager.SUGGEST_COLUMN_ICON_2,
+            SearchManager.SUGGEST_COLUMN_QUERY};
+
+    private static final int MAX_SUGGESTION_SHORT_ENTRIES = 3;
+    private static final int MAX_SUGGESTION_LONG_ENTRIES = 6;
+
     // make sure that these match the index of TABLE_NAMES
     private static final int URI_MATCH_BOOKMARKS = 0;
     private static final int URI_MATCH_SEARCHES = 1;
@@ -206,221 +228,195 @@
     }
 
     /*
-     * Subclass AbstractCursor so we can add "Google Search"
+     * 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 
+     *      "Google Search";
+     * 2. If bookmark/history entries are less than 
+     *      (MAX_SUGGESTION_SHORT_ENTRIES -1), we include Google suggest.
      */
     private class MySuggestionCursor extends AbstractCursor {
-        private Cursor  mCursor;
+        private Cursor  mHistoryCursor;
+        private Cursor  mSuggestCursor;
+        private int     mHistoryCount;
+        private int     mSuggestionCount;
         private boolean mBeyondCursor;
         private String  mString;
-        private Uri     mNotifyUri;
-        private ContentResolver mContentResolver;
-        private AbstractCursor.SelfContentObserver mObserver;
-        private final Object mObserverLock = new Object();
 
-        public MySuggestionCursor(Cursor c, String string) {
-            mCursor = c;
-            if (Regex.WEB_URL_PATTERN.matcher(string).matches()) {
-                mString = "";
-            } else {
-                mString = string;
+        public MySuggestionCursor(Cursor hc, Cursor sc, String string) {
+            mHistoryCursor = hc;
+            mSuggestCursor = sc;
+            mHistoryCount = hc.getCount();
+            mSuggestionCount = sc != null ? sc.getCount() : 0;
+            if (mSuggestionCount > (MAX_SUGGESTION_LONG_ENTRIES - mHistoryCount)) {
+                mSuggestionCount = MAX_SUGGESTION_LONG_ENTRIES - mHistoryCount;
             }
+            mString = string;
             mBeyondCursor = false;
         }
 
+        @Override
         public boolean onMove(int oldPosition, int newPosition) {
-            if (mCursor.getCount() == newPosition) {
-                mBeyondCursor = true;
-            } else {
-                mCursor.moveToPosition(newPosition);
+            if (mHistoryCursor == null) {
+                return false;
+            }
+            if (mHistoryCount > newPosition) {
+                mHistoryCursor.moveToPosition(newPosition);
                 mBeyondCursor = false;
+            } else if (mHistoryCount + mSuggestionCount > newPosition) {
+                mSuggestCursor.moveToPosition(newPosition - mHistoryCount);
+                mBeyondCursor = false;
+            } else {
+                mBeyondCursor = true;
             }
             return true;
         }
 
+        @Override
         public int getCount() {
             if (mString.length() > 0) {
-                return mCursor.getCount() + 1;
+                return mHistoryCount + mSuggestionCount + 1;
             } else {
-                return mCursor.getCount();
+                return mHistoryCount + mSuggestionCount;
             }
         }
 
-        public boolean deleteRow() {
-            return !mBeyondCursor && mCursor.deleteRow();
-        }
-
+        @Override
         public String[] getColumnNames() {
-            return mCursor.getColumnNames();
+            return COLUMNS;
         }
 
-        public int getColumnCount() {
-            return mCursor.getColumnCount();
-        }
-
+        @Override
         public String getString(int columnIndex) {
-            if (!mBeyondCursor) {
-                return mCursor.getString(columnIndex);
+            if ((mPos != -1 && mHistoryCursor != null)) {
+                switch(columnIndex) {
+                    case SUGGEST_COLUMN_INTENT_ACTION_ID:
+                        if (mHistoryCount > mPos) {
+                            return Intent.ACTION_VIEW;
+                        } else {
+                            return Intent.ACTION_SEARCH;
+                        }
+
+                    case SUGGEST_COLUMN_INTENT_DATA_ID:
+                        if (mHistoryCount > mPos) {
+                            return mHistoryCursor.getString(1);
+                        } else {
+                            return null;
+                        }
+
+                    case SUGGEST_COLUMN_TEXT_1_ID:
+                        if (mHistoryCount > mPos) {
+                            return mHistoryCursor.getString(1);
+                        } else if (!mBeyondCursor) {
+                            return mSuggestCursor.getString(1);
+                        } else {
+                            return mString;
+                        }
+
+                    case SUGGEST_COLUMN_TEXT_2_ID:
+                        if (mHistoryCount > mPos) {
+                            return mHistoryCursor.getString(2);
+                        } else if (!mBeyondCursor) {
+                            return mSuggestCursor.getString(2);
+                        } else {
+                            return getContext().getString(R.string.search_google);
+                        }
+
+                    case SUGGEST_COLUMN_ICON_1_ID:
+                        if (mHistoryCount > mPos) {
+                            if (mHistoryCursor.getInt(3) == 1) {
+                                return new Integer(
+                                        R.drawable.ic_search_category_bookmark)
+                                        .toString();
+                            } else {
+                                return new Integer(
+                                        R.drawable.ic_search_category_history)
+                                        .toString();
+                            }
+                        } else {
+                            return new Integer(
+                                    R.drawable.ic_search_category_suggest)
+                                    .toString();
+                        }
+
+                    case SUGGEST_COLUMN_ICON_2_ID:
+                        return new String("0");
+
+                    case SUGGEST_COLUMN_QUERY_ID:
+                        if (mHistoryCount > mPos) {
+                            return null;
+                        } else if (!mBeyondCursor) {
+                            return mSuggestCursor.getString(3);
+                        } else {
+                            return mString;
+                        }
+                }
             }
-            switch (columnIndex) {
-                case 2: // SearchManager.SUGGEST_COLUMN_TEXT_1
-                    return "Google Search for \"" + mString + "\"";
-                case 1: // SearchManager.SUGGEST_COLUMN_INTENT_DATA
-                    return BrowserActivity.composeSearchUrl(mString);
-                case 3: // SearchManager.SUGGEST_COLUMN_TEXT_2
-                default:
-                    return "";
+            return null;
+        }
+
+        @Override
+        public double getDouble(int column) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public float getFloat(int column) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public int getInt(int column) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public long getLong(int column) {
+            if ((mPos != -1) && column == 0) {
+                return mPos;        // use row# as the _Id
             }
+            throw new UnsupportedOperationException();
         }
 
-        public short getShort(int columnIndex) {
-            if (!mBeyondCursor) {
-                return mCursor.getShort(columnIndex);
-            }
-            if (0 == columnIndex) {
-                return 0;
-            }
-            return -1;
+        @Override
+        public short getShort(int column) {
+            throw new UnsupportedOperationException();
         }
 
-        public int getInt(int columnIndex) {
-            if (!mBeyondCursor) {
-                return mCursor.getInt(columnIndex);
-            }
-            if (0 == columnIndex) {
-                return 0;
-            }
-            return -1;
-        }
-
-        public long getLong(int columnIndex) {
-            if (!mBeyondCursor) {
-                return mCursor.getLong(columnIndex);
-            }
-            if (0 == columnIndex) {
-                return 0;
-            }
-            return -1;
-        }
-
-        public float getFloat(int columnIndex) {
-            if (!mBeyondCursor) {
-                return mCursor.getFloat(columnIndex);
-            }
-            if (0 == columnIndex) {
-                return 0f;
-            }
-            return -1f;
-        }
-
-        public double getDouble(int columnIndex) {
-            if (!mBeyondCursor) {
-                return mCursor.getDouble(columnIndex);
-            }
-            if (0 == columnIndex) {
-                return 0.0;
-            }
-            return -1.0;
-        }
-
-        public boolean isNull(int columnIndex) {
-            return mCursor.isNull(columnIndex);
-        }
-
-        public boolean supportsUpdates() {
-            return false;
-        }
-
-        public boolean hasUpdates() {
-            return false;
-        }
-
-        public boolean updateString(int columnIndex, String value) {
-            return false;
-        }
-
-        public boolean updateShort(int columnIndex, short value) {
-            return false;
-        }
-
-        public boolean updateInt(int columnIndex, int value) {
-            return false;
-        }
-
-        public boolean updateLong(int columnIndex, long value) {
-            return false;
-        }
-
-        public boolean updateFloat(int columnIndex, float value) {
-            return false;
-        }
-
-        public boolean updateDouble(int columnIndex, double value) {
-            return false;
+        @Override
+        public boolean isNull(int column) {
+            throw new UnsupportedOperationException();
         }
 
         // TODO Temporary change, finalize after jq's changes go in
         public void deactivate() {
-            if (mCursor != null) {
-                mCursor.deactivate();
+            if (mHistoryCursor != null) {
+                mHistoryCursor.deactivate();
+            }
+            if (mSuggestCursor != null) {
+                mSuggestCursor.deactivate();
             }
             super.deactivate();
         }
 
         public boolean requery() {
-            return mCursor.requery();
+            return (mHistoryCursor != null ? mHistoryCursor.requery() : false) |
+                    (mSuggestCursor != null ? mSuggestCursor.requery() : false);
         }
 
         // TODO Temporary change, finalize after jq's changes go in
         public void close() {
             super.close();
-            if (mCursor != null) {
-                mCursor.close();
-                mCursor = null;
+            if (mHistoryCursor != null) {
+                mHistoryCursor.close();
+                mHistoryCursor = null;
             }
-        }
-
-        public void registerContentObserver(ContentObserver observer) {
-            super.registerContentObserver(observer);
-        }
-
-        public void unregisterContentObserver(ContentObserver observer) {
-            super.unregisterContentObserver(observer);
-        }
-
-        public void registerDataSetObserver(DataSetObserver observer) {
-            super.registerDataSetObserver(observer);
-        }
-
-        public void unregisterDataSetObserver(DataSetObserver observer) {
-            super.unregisterDataSetObserver(observer);
-        }
-
-        protected void onChange(boolean selfChange) {
-            synchronized (mObserverLock) {
-                super.onChange(selfChange);
-                if (mNotifyUri != null && selfChange) {
-                    mContentResolver.notifyChange(mNotifyUri, mObserver);
-                }
+            if (mSuggestCursor != null) {
+                mSuggestCursor.close();
+                mSuggestCursor = null;
             }
         }
-
-        public void setNotificationUri(ContentResolver cr, Uri uri) {
-            synchronized (mObserverLock) {
-                if (mObserver != null) {
-                    cr.unregisterContentObserver(mObserver);
-                }
-                mObserver = new AbstractCursor.SelfContentObserver(this);
-                cr.registerContentObserver(uri, true, mObserver);
-                mCursor.setNotificationUri(cr, uri);
-                super.setNotificationUri(cr, uri);
-                mContentResolver = cr;
-                mNotifyUri = uri;
-            }
-        }
-
-        public boolean getWantsAllOnMoveCalls() {
-            return mCursor.getWantsAllOnMoveCalls();
-        }
     }
 
     @Override
@@ -455,13 +451,58 @@
                     suggestSelection = SUGGEST_SELECTION;
                 }
             }
-            // Suggestions are always performed with the default sort order:
-            // date ASC.
+
             Cursor c = db.query(TABLE_NAMES[URI_MATCH_BOOKMARKS],
                     SUGGEST_PROJECTION, suggestSelection, myArgs, null, null,
-                    ORDER_BY, null);
-            c.setNotificationUri(getContext().getContentResolver(), url);
-            return new MySuggestionCursor(c, selectionArgs[0]);
+                    ORDER_BY,
+                    (new Integer(MAX_SUGGESTION_LONG_ENTRIES)).toString());
+
+            if (Regex.WEB_URL_PATTERN.matcher(selectionArgs[0]).matches()) {
+                return new MySuggestionCursor(c, null, "");
+            } else {
+                // get Google suggest if there is still space in the list
+                if (myArgs != null && myArgs.length > 1
+                        && c.getCount() < (MAX_SUGGESTION_SHORT_ENTRIES - 1)) {
+                    ISearchManager sm = ISearchManager.Stub
+                            .asInterface(ServiceManager
+                                    .getService(Context.SEARCH_SERVICE));
+                    SearchableInfo si = null;
+                    try {
+                        // use the global search to get Google suggest provider
+                        si = sm.getSearchableInfo(new ComponentName(
+                                getContext(), "com.android.browser"), true);
+
+                        // similar to the getSuggestions() in SearchDialog.java
+                        StringBuilder uriStr = new StringBuilder("content://");
+                        uriStr.append(si.getSuggestAuthority());
+                        // if content path provided, insert it now
+                        final String contentPath = si.getSuggestPath();
+                        if (contentPath != null) {
+                            uriStr.append('/');
+                            uriStr.append(contentPath);
+                        }
+                        // append standard suggestion query path 
+                        uriStr.append('/' + SearchManager.SUGGEST_URI_PATH_QUERY);
+                        // inject query, either as selection args or inline
+                        String[] selArgs = null;
+                        if (si.getSuggestSelection() != null) {
+                            selArgs = new String[] {selectionArgs[0]};
+                        } else {
+                            uriStr.append('/');
+                            uriStr.append(Uri.encode(selectionArgs[0]));
+                        }
+
+                        // finally, make the query
+                        Cursor sc = getContext().getContentResolver().query(
+                                Uri.parse(uriStr.toString()), null,
+                                si.getSuggestSelection(), selArgs, null);
+
+                        return new MySuggestionCursor(c, sc, selectionArgs[0]);
+                    } catch (RemoteException e) {
+                    }
+                }
+                return new MySuggestionCursor(c, null, selectionArgs[0]);
+            }
         }
 
         String[] projection = null;
diff --git a/src/com/android/browser/BrowserSettings.java b/src/com/android/browser/BrowserSettings.java
index 6164e38..d8c5186 100644
--- a/src/com/android/browser/BrowserSettings.java
+++ b/src/com/android/browser/BrowserSettings.java
@@ -26,6 +26,7 @@
 import android.view.WindowManager;
 import android.webkit.CacheManager;
 import android.webkit.CookieManager;
+import android.webkit.WebView;
 import android.webkit.WebViewDatabase;
 import android.webkit.WebIconDatabase;
 import android.webkit.WebSettings;
@@ -53,7 +54,7 @@
     // Public variables for settings
     // NOTE: these defaults need to be kept in sync with the XML
     // until the performance of PreferenceManager.setDefaultValues()
-    // is improved. 
+    // is improved.
     private boolean loadsImagesAutomatically = true;
     private boolean javaScriptEnabled = true;
     private boolean pluginsEnabled = true;
@@ -64,13 +65,12 @@
     private boolean saveFormData = true;
     private boolean openInBackground = false;
     private String defaultTextEncodingName;
-    private String homeUrl = "http://www.google.com/m?client=ms-" + 
-        SystemProperties.get("ro.com.google.clientid", "unknown");
+    private String homeUrl = "http://www.google.com/m?client=ms-" +
+        SystemProperties.get("persist.sys.com.google.clientid", "unknown");
     private boolean loginInitialized = false;
-    private int orientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
     private boolean autoFitPage = true;
     private boolean showDebugSettings = false;
-    
+
     // Development settings
     public WebSettings.LayoutAlgorithm layoutAlgorithm =
         WebSettings.LayoutAlgorithm.NARROW_COLUMNS;
@@ -79,7 +79,7 @@
     private boolean tracing = false;
     private boolean lightTouch = false;
     private boolean navDump = false;
-    // Browser only settings 
+    // Browser only settings
     private boolean doFlick = false;
 
     // Private preconfigured values
@@ -89,17 +89,17 @@
     private static int defaultFixedFontSize = 13;
     private static WebSettings.TextSize textSize =
         WebSettings.TextSize.NORMAL;
-    
+
     // Preference keys that are used outside this class
     public final static String PREF_CLEAR_CACHE = "privacy_clear_cache";
     public final static String PREF_CLEAR_COOKIES = "privacy_clear_cookies";
     public final static String PREF_CLEAR_HISTORY = "privacy_clear_history";
     public final static String PREF_HOMEPAGE = "homepage";
-    public final static String PREF_CLEAR_FORM_DATA = 
+    public final static String PREF_CLEAR_FORM_DATA =
             "privacy_clear_form_data";
-    public final static String PREF_CLEAR_PASSWORDS = 
+    public final static String PREF_CLEAR_PASSWORDS =
             "privacy_clear_passwords";
-    public final static String PREF_EXTRAS_RESET_DEFAULTS = 
+    public final static String PREF_EXTRAS_RESET_DEFAULTS =
             "reset_default_preferences";
     public final static String PREF_DEBUG_SETTINGS = "debug_menu";
     public final static String PREF_GEARS_SETTINGS = "gears_settings";
@@ -120,7 +120,7 @@
     public final static int MAX_TEXTVIEW_LEN = 80;
 
     private TabControl mTabControl;
-    
+
     // Single instance of the BrowserSettings for use in the Browser app.
     private static BrowserSettings sSingleton;
 
@@ -128,7 +128,7 @@
     // observer.
     private HashMap<WebSettings,Observer> mWebSettingsToObservers =
         new HashMap<WebSettings,Observer>();
- 
+
     /*
      * An observer wrapper for updating a WebSettings object with the new
      * settings after a call to BrowserSettings.update().
@@ -180,7 +180,7 @@
             s.setAllowFileAccess(false);
         }
     }
-   
+
     /**
      * Load settings from the browser app's database.
      * NOTE: Strings used for the preferences must match those specified
@@ -191,7 +191,7 @@
      *            observers of this object.
      */
     public void loadFromDb(Context ctx) {
-        SharedPreferences p = 
+        SharedPreferences p =
                 PreferenceManager.getDefaultSharedPreferences(ctx);
 
         // Set the default value for the plugins path to the application's
@@ -204,34 +204,33 @@
         //PreferenceManager.setDefaultValues(ctx, R.xml.browser_preferences);
         syncSharedPreferences(p);
     }
-    
+
     /* package */ void syncSharedPreferences(SharedPreferences p) {
-        homeUrl = 
+        homeUrl =
             p.getString(PREF_HOMEPAGE, homeUrl);
-        loadsImagesAutomatically = p.getBoolean("load_images", 
+        loadsImagesAutomatically = p.getBoolean("load_images",
                 loadsImagesAutomatically);
-        javaScriptEnabled = p.getBoolean("enable_javascript", 
+        javaScriptEnabled = p.getBoolean("enable_javascript",
                 javaScriptEnabled);
-        pluginsEnabled = p.getBoolean("enable_plugins", 
+        pluginsEnabled = p.getBoolean("enable_plugins",
                 pluginsEnabled);
         pluginsPath = p.getString("plugins_path", pluginsPath);
         javaScriptCanOpenWindowsAutomatically = !p.getBoolean(
-            "block_popup_windows", 
+            "block_popup_windows",
             !javaScriptCanOpenWindowsAutomatically);
-        showSecurityWarnings = p.getBoolean("show_security_warnings", 
+        showSecurityWarnings = p.getBoolean("show_security_warnings",
                 showSecurityWarnings);
-        rememberPasswords = p.getBoolean("remember_passwords", 
+        rememberPasswords = p.getBoolean("remember_passwords",
                 rememberPasswords);
-        saveFormData = p.getBoolean("save_formdata", 
+        saveFormData = p.getBoolean("save_formdata",
                 saveFormData);
-        boolean accept_cookies = p.getBoolean("accept_cookies", 
+        boolean accept_cookies = p.getBoolean("accept_cookies",
                 CookieManager.getInstance().acceptCookie());
         CookieManager.getInstance().setAcceptCookie(accept_cookies);
         openInBackground = p.getBoolean("open_in_background", openInBackground);
         loginInitialized = p.getBoolean("login_initialized", loginInitialized);
         textSize = WebSettings.TextSize.valueOf(
                 p.getString(PREF_TEXT_SIZE, textSize.name()));
-        orientation = p.getInt("orientation", orientation);
         autoFitPage = p.getBoolean("autofit_pages", autoFitPage);
         useWideViewPort = true; // use wide view port for either setting
         if (autoFitPage) {
@@ -242,23 +241,23 @@
         defaultTextEncodingName =
                 p.getString(PREF_DEFAULT_TEXT_ENCODING,
                         defaultTextEncodingName);
-        
-        showDebugSettings = 
+
+        showDebugSettings =
                 p.getBoolean(PREF_DEBUG_SETTINGS, showDebugSettings);
         // Debug menu items have precidence if the menu is visible
         if (showDebugSettings) {
-            boolean small_screen = p.getBoolean("small_screen", 
-                    layoutAlgorithm == 
+            boolean small_screen = p.getBoolean("small_screen",
+                    layoutAlgorithm ==
                     WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
             if (small_screen) {
                 layoutAlgorithm = WebSettings.LayoutAlgorithm.SINGLE_COLUMN;
             } else {
-                boolean normal_layout = p.getBoolean("normal_layout", 
+                boolean normal_layout = p.getBoolean("normal_layout",
                         layoutAlgorithm == WebSettings.LayoutAlgorithm.NORMAL);
                 if (normal_layout) {
                     layoutAlgorithm = WebSettings.LayoutAlgorithm.NORMAL;
                 } else {
-                    layoutAlgorithm = 
+                    layoutAlgorithm =
                             WebSettings.LayoutAlgorithm.NARROW_COLUMNS;
                 }
             }
@@ -273,7 +272,7 @@
         }
         update();
     }
-    
+
     public String getPluginsPath() {
         return pluginsPath;
     }
@@ -281,42 +280,27 @@
     public String getHomePage() {
         return homeUrl;
     }
-    
+
     public void setHomePage(Context context, String url) {
         Editor ed = PreferenceManager.
-                getDefaultSharedPreferences(context).edit();      
+                getDefaultSharedPreferences(context).edit();
         ed.putString(PREF_HOMEPAGE, url);
         ed.commit();
         homeUrl = url;
     }
-    
+
     public boolean isLoginInitialized() {
         return loginInitialized;
     }
-    
+
     public void setLoginInitialized(Context context) {
         loginInitialized = true;
         Editor ed = PreferenceManager.
-                getDefaultSharedPreferences(context).edit();      
+                getDefaultSharedPreferences(context).edit();
         ed.putBoolean("login_initialized", loginInitialized);
         ed.commit();
     }
-    
-    public int getOrientation() {
-        return orientation;
-    }
-    
-    public void setOrientation(Context context, int o) {
-        if (orientation == o) {
-            return;
-        }
-        orientation = o;
-        Editor ed = PreferenceManager.
-                getDefaultSharedPreferences(context).edit();      
-        ed.putInt("orientation", orientation);
-        ed.commit();
-    }
-    
+
     public WebSettings.TextSize getTextSize() {
         return textSize;
     }
@@ -344,19 +328,21 @@
     public boolean doFlick() {
         return doFlick;
     }
-    
+
     public boolean showDebugSettings() {
         return showDebugSettings;
     }
-    
+
     public void toggleDebugSettings() {
         showDebugSettings = !showDebugSettings;
+        navDump = showDebugSettings;
+        update();
     }
 
     /**
      * Add a WebSettings object to the list of observers that will be updated
      * when update() is called.
-     * 
+     *
      * @param s A WebSettings object that is strictly tied to the life of a
      *            WebView.
      */
@@ -412,7 +398,10 @@
     /*package*/ void clearCache(Context context) {
         WebIconDatabase.getInstance().removeAllIcons();
         if (mTabControl != null) {
-            mTabControl.getCurrentWebView().clearCache(true);
+            WebView current = mTabControl.getCurrentWebView();
+            if (current != null) {
+                current.clearCache(true);
+            }
         }
     }
 
@@ -438,12 +427,12 @@
         db.clearUsernamePassword();
         db.clearHttpAuthUsernamePassword();
     }
-    
+
     /*package*/ void resetDefaultPreferences(Context context) {
-        SharedPreferences p = 
+        SharedPreferences p =
             PreferenceManager.getDefaultSharedPreferences(context);
         p.edit().clear().commit();
-        PreferenceManager.setDefaultValues(context, R.xml.browser_preferences, 
+        PreferenceManager.setDefaultValues(context, R.xml.browser_preferences,
                 true);
     }
 
diff --git a/src/com/android/browser/CombinedBookmarkHistoryActivity.java b/src/com/android/browser/CombinedBookmarkHistoryActivity.java
new file mode 100644
index 0000000..963f179
--- /dev/null
+++ b/src/com/android/browser/CombinedBookmarkHistoryActivity.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.browser;
+
+import android.app.Activity;
+import android.app.TabActivity;
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.provider.Browser;
+import android.webkit.WebIconDatabase.IconListener;
+import android.widget.TabHost;
+import android.widget.TabHost.TabSpec;
+import android.view.Window;
+
+import java.util.HashMap;
+import java.util.Vector;
+
+public class CombinedBookmarkHistoryActivity extends TabActivity
+        implements TabHost.OnTabChangeListener {
+    /* package */ static String BOOKMARKS_TAB = "bookmark";
+    /* package */ static String VISITED_TAB = "visited";
+    /* package */ static String HISTORY_TAB = "history";
+    /* package */ static String STARTING_TAB = "tab";
+
+    static class IconListenerSet implements IconListener {
+        // Used to store favicons as we get them from the database
+        // FIXME: We use a different method to get the Favicons in
+        // BrowserBookmarksAdapter. They should probably be unified.
+        private HashMap<String, Bitmap> mUrlsToIcons;
+        private Vector<IconListener> mListeners;
+
+        public IconListenerSet() {
+            mUrlsToIcons = new HashMap<String, Bitmap>();
+            mListeners = new Vector<IconListener>();
+        }
+        public void onReceivedIcon(String url, Bitmap icon) {
+            mUrlsToIcons.put(url, icon);
+            for (IconListener listener : mListeners) {
+                listener.onReceivedIcon(url, icon);
+            }
+        }
+        public void addListener(IconListener listener) {
+            mListeners.add(listener);
+        }
+        public Bitmap getFavicon(String url) {
+            return (Bitmap) mUrlsToIcons.get(url);
+        }
+    }
+    private static IconListenerSet sIconListenerSet;
+    static IconListenerSet getIconListenerSet(ContentResolver cr) {
+        if (null == sIconListenerSet) {
+            sIconListenerSet = new IconListenerSet();
+            Browser.requestAllIcons(cr, null, sIconListenerSet);
+        }
+        return sIconListenerSet;
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        requestWindowFeature(Window.FEATURE_NO_TITLE);
+        setContentView(R.layout.tabs);
+        TabHost tabHost = getTabHost();
+        tabHost.setOnTabChangedListener(this);
+
+        Bundle extras = getIntent().getExtras();
+        Resources resources = getResources();
+
+        getIconListenerSet(getContentResolver());
+        Intent bookmarksIntent = new Intent(this, BrowserBookmarksPage.class);
+        bookmarksIntent.putExtras(extras);
+        tabHost.addTab(tabHost.newTabSpec(BOOKMARKS_TAB)
+                .setIndicator(resources.getString(R.string.tab_bookmarks),
+                resources.getDrawable(R.drawable.browser_bookmark_tab))
+                .setContent(bookmarksIntent));
+
+        Intent visitedIntent = new Intent(this, MostVisitedActivity.class);
+        visitedIntent.putExtras(extras);
+        tabHost.addTab(tabHost.newTabSpec(VISITED_TAB)
+                .setIndicator(resources.getString(R.string.tab_most_visited),
+                resources.getDrawable(R.drawable.browser_visited_tab))
+                .setContent(visitedIntent));
+
+        Intent historyIntent = new Intent(this, BrowserHistoryPage.class);
+        historyIntent.putExtras(extras);
+        tabHost.addTab(tabHost.newTabSpec(HISTORY_TAB)
+                .setIndicator(resources.getString(R.string.tab_history),
+                resources.getDrawable(R.drawable.
+                browser_history_tab)).setContent(historyIntent));
+
+        String defaultTab = extras.getString(STARTING_TAB);
+        if (defaultTab != null) {
+            tabHost.setCurrentTab(2);
+        }
+    }
+
+    // Copied from DialTacts Activity
+    /** {@inheritDoc} */
+    public void onTabChanged(String tabId) {
+        Activity activity = getLocalActivityManager().getActivity(tabId);
+        if (activity != null) {
+            activity.onWindowFocusChanged(true);
+        }
+    }
+
+    
+}
diff --git a/src/com/android/browser/FakeWebView.java b/src/com/android/browser/FakeWebView.java
index 7997672..633b799 100644
--- a/src/com/android/browser/FakeWebView.java
+++ b/src/com/android/browser/FakeWebView.java
@@ -34,10 +34,12 @@
  */
 public class FakeWebView extends ImageView {
     private TabControl.Tab mTab;
+    private Picture        mPicture;
     private boolean        mUsesResource;
 
     private class Listener implements WebView.PictureListener {
         public void onNewPicture(WebView view, Picture p) {
+            FakeWebView.this.mPicture = p;
             FakeWebView.this.invalidate();
         }
     };
@@ -69,13 +71,12 @@
             if (mTab != null) {
                 final WebView w = mTab.getTopWindow();
                 if (w != null) {
-                    Picture p = w.capturePicture();
-                    if (p != null) {
+                    if (mPicture != null) {
                         canvas.save();
                         float scale = getWidth() * w.getScale() / w.getWidth();
                         canvas.scale(scale, scale);
                         canvas.translate(-w.getScrollX(), -w.getScrollY());
-                        canvas.drawPicture(p);
+                        canvas.drawPicture(mPicture);
                         canvas.restore();
                     }
                 }
@@ -99,10 +100,12 @@
         mTab = t;
         if (t != null && t.getWebView() != null) {
             Listener l = new Listener();
-            t.getWebView().setPictureListener(l);
             if (t.getSubWebView() != null) {
                 t.getSubWebView().setPictureListener(l);
+            } else {
+                t.getWebView().setPictureListener(l);
             }
+            mPicture = mTab.getTopWindow().capturePicture();
         }
     }
 }
diff --git a/src/com/android/browser/FindDialog.java b/src/com/android/browser/FindDialog.java
index 2b26784..43cd1c4 100644
--- a/src/com/android/browser/FindDialog.java
+++ b/src/com/android/browser/FindDialog.java
@@ -17,6 +17,7 @@
 package com.android.browser;
 
 import android.app.Dialog;
+import android.content.Context;
 import android.content.res.Configuration;
 import android.os.Bundle;
 import android.os.Handler;
@@ -29,6 +30,8 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.Window;
+import android.view.WindowManager;
+import android.view.inputmethod.InputMethodManager;
 import android.webkit.WebView;
 import android.widget.EditText;
 import android.widget.TextView;
@@ -65,9 +68,19 @@
                 throw new AssertionError("No WebView for FindDialog::onClick");
             }
             mWebView.findNext(false);
+            hideSoftInput();
         }
     };
-    
+
+    /*
+     * Remove the soft keyboard from the screen.
+     */
+    private void hideSoftInput() {
+        InputMethodManager imm = (InputMethodManager)
+                mBrowserActivity.getSystemService(Context.INPUT_METHOD_SERVICE);
+        imm.hideSoftInputFromWindow(mEditText.getWindowToken(), 0);
+    }
+
     private void disableButtons() {
         mPrevButton.setEnabled(false);
         mNextButton.setEnabled(false);
@@ -121,6 +134,8 @@
         mMatches = (TextView) findViewById(R.id.matches);
         mMatchesView = findViewById(R.id.matches_view);
         disableButtons();
+        theWindow.setSoftInputMode(
+                WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
     }
     
     public void dismiss() {
@@ -154,8 +169,9 @@
             throw new AssertionError("No WebView for FindDialog::findNext");
         }
         mWebView.findNext(true);
+        hideSoftInput();
     }
-    
+
     public void show() {
         super.show();
         mEditText.requestFocus();
diff --git a/src/com/android/browser/GearsBaseDialog.java b/src/com/android/browser/GearsBaseDialog.java
index afd0dd2..638ba27 100644
--- a/src/com/android/browser/GearsBaseDialog.java
+++ b/src/com/android/browser/GearsBaseDialog.java
@@ -17,6 +17,7 @@
 package com.android.browser;
 
 import android.app.Activity;
+import android.app.Dialog;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
@@ -67,6 +68,7 @@
   public static final int UPDATE_ICON = 5;
   public static final int REQUEST_ICON = 6;
   public static final int PAUSE_REQUEST_ICON = 7;
+  public static final int CLEAR_REQUEST_ICON = 8;
 
   protected final String LOCAL_DATA_STRING = "localData";
   protected final String LOCAL_STORAGE_STRING = "localStorage";
@@ -191,14 +193,14 @@
                   public void onClick(View v) {
                     mHandler.sendEmptyMessage(ALWAYS_DENY);
                   }
-                }, true, false);
+                });
 
     setupButton(R.id.button_allow, allowRsc,
                 new Button.OnClickListener() {
                   public void onClick(View v) {
                     mHandler.sendEmptyMessage(ALLOW);
                   }
-                }, false, true);
+                });
 
     setupButton(R.id.button_deny, denyRsc,
                 new Button.OnClickListener() {
@@ -249,6 +251,77 @@
   }
 
   /**
+   * Utility method to hide a view.
+   */
+  void hideView(View v, int rsc) {
+    if (rsc == 0) {
+      return;
+    }
+    View view;
+    if (v == null) {
+      view = findViewById(rsc);
+    } else {
+      view = v.findViewById(rsc);
+    }
+    if (view != null) {
+      view.setVisibility(View.GONE);
+    }
+  }
+
+  /**
+   * Utility method to show a view.
+   */
+  void showView(View v, int rsc) {
+    if (rsc == 0) {
+      return;
+    }
+    View view;
+    if (v == null) {
+      view = findViewById(rsc);
+    } else {
+      view = v.findViewById(rsc);
+    }
+    if (view != null) {
+      view.setVisibility(View.VISIBLE);
+    }
+  }
+
+  /**
+   * Utility method to set a text.
+   */
+  void setText(View v, int rsc, CharSequence text) {
+    if (rsc == 0) {
+      return;
+    }
+    View view = v.findViewById(rsc);
+    if (view != null) {
+      TextView textView = (TextView) view;
+      textView.setText(text);
+      textView.setVisibility(View.VISIBLE);
+    }
+  }
+
+  /**
+   * Utility method to set a text.
+   */
+  void setText(View v, int rsc, int txtRsc) {
+    if (rsc == 0) {
+      return;
+    }
+    View view = v.findViewById(rsc);
+    if (view != null) {
+      TextView textView = (TextView) view;
+      if (txtRsc == 0) {
+        textView.setVisibility(View.GONE);
+      } else {
+        CharSequence text = getString(txtRsc);
+        textView.setText(text);
+        textView.setVisibility(View.VISIBLE);
+      }
+    }
+  }
+
+  /**
    * Utility class to download an icon in the background.
    * Once done ask the UI thread to update the icon.
    */
@@ -360,7 +433,7 @@
    */
   public void setupDialog(TextView message, ImageView icon) {
     message.setText(R.string.unrecognized_dialog_message);
-    icon.setImageResource(R.drawable.gears_icon_48x48);
+    icon.setImageResource(R.drawable.ic_dialog_menu_generic);
     message.setVisibility(View.VISIBLE);
   }
 
@@ -373,4 +446,29 @@
     setupDialog();
   }
 
+  /**
+   * Method called when the back button is pressed,
+   * allowing the dialog to intercept the default behaviour.
+   */
+  public boolean handleBackButton() {
+    return false;
+  }
+
+  /**
+   * Returns the resource string of the notification displayed
+   * after the dialog. By default, does not return one.
+   */
+  public int notification() {
+    return 0;
+  }
+
+  /**
+   * If a secondary dialog (e.g. a confirmation dialog) is created,
+   * GearsNativeDialog will call this method.
+   */
+  public Dialog onCreateDialog(int id) {
+    // This should be redefined by subclasses as needed.
+    return null;
+  }
+
 }
diff --git a/src/com/android/browser/GearsDialog.java b/src/com/android/browser/GearsDialog.java
deleted file mode 100644
index 62a8aaa..0000000
--- a/src/com/android/browser/GearsDialog.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.browser;
-
-import android.app.Activity;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Configuration;
-import android.net.Uri;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.View;
-import android.widget.Button;
-
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-
-import android.webkit.WebView;
-import android.webkit.WebViewClient;
-import android.webkit.WebSettings;
-
-import android.view.KeyEvent;
-import android.view.ViewGroup.LayoutParams;
-
-import android.widget.LinearLayout;
-
-public class GearsDialog extends Activity {
-
-  private static final String TAG = "GearsDialog";
-
-  private WebView webview;
-
-  private String htmlContent;
-  private String dialogArguments;
-
-  private boolean dismissed = false;
-
-  @Override
-  public void onCreate(Bundle icicle) {
-    super.onCreate(icicle);
-    webview = new WebView(this);
-    webview.getSettings().setJavaScriptEnabled(true);
-    webview.addJavascriptInterface(this, "bridge");
-
-    setContentView(webview);
-    setTitle("Gears");
-  }
-
-  @Override
-  public void onStart() {
-    super.onStart();
-    loadHTML();
-  }
-
-  @Override
-  public void onDestroy() {
-    super.onDestroy();
-    // In case we reach this point without
-    // notifying GearsDialogService, we do it now.
-    if (!dismissed) {
-      notifyEndOfDialog();
-    }
-  }
-
-  @Override
-  public void onConfigurationChanged(Configuration newConfig) {
-    super.onConfigurationChanged(newConfig);
-    Intent i = getIntent();
-    boolean inSettings = i.getBooleanExtra("inSettings", false);
-    // If we are called from the settings, we
-    // dismiss ourselve upon rotation
-    if (inSettings) {
-      notifyEndOfDialog();
-      finish();
-    }
-  }
-
-  /**
-   * Load the HTML content in the WebView
-   */
-  private void loadHTML() {
-    Intent i = getIntent();
-    htmlContent = i.getStringExtra("htmlContent");
-    dialogArguments = i.getStringExtra("dialogArguments");
-    webview.loadDataWithBaseURL("", htmlContent, "text/html", "", "");
-  }
-
-  /**
-   * Signal to GearsDialogService that we are done.
-   */
-  private void notifyEndOfDialog() {
-    GearsDialogService.signalFinishedDialog();
-    dismissed = true;
-  }
-
-  /**
-   * Intercepts the back key to immediately notify
-   * GearsDialogService that we are done.
-   */
-  public boolean dispatchKeyEvent(KeyEvent event) {
-    if (event.getKeyCode() ==  KeyEvent.KEYCODE_BACK && event.isDown()) {
-      notifyEndOfDialog();
-    }
-    return super.dispatchKeyEvent(event);
-  }
-
-  /**
-   * Returns a json-formatted string containing the information
-   * about the site.
-   * This method is accessible through the javascript bridge.
-   */
-  public String getDialogArguments() {
-    return dialogArguments;
-  }
-
-  /**
-   * Set the results string and closes the dialog.
-   * This method is accessible through the javascript bridge.
-   */
-  public void closeDialog(String results) {
-    GearsDialogService.closeDialog(results);
-    notifyEndOfDialog();
-    finish();
-  }
-
-  /**
-   * Debug method outputting a message.
-   * This method is accessible through the javascript bridge.
-   */
-  public void log(String msg) {
-    Log.v(TAG, msg);
-  }
-}
diff --git a/src/com/android/browser/GearsDialogService.java b/src/com/android/browser/GearsDialogService.java
deleted file mode 100644
index b92ff47..0000000
--- a/src/com/android/browser/GearsDialogService.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.browser;
-
-import android.app.Service;
-import android.util.Log;
-import android.content.Intent;
-import android.os.IBinder;
-
-import android.content.Intent;
-import android.content.ContentValues;
-import android.content.ActivityNotFoundException;
-
-import java.util.concurrent.locks.Condition;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantLock;
-
-import java.lang.InterruptedException;
-
-public class GearsDialogService extends Service {
-  private static final String TAG = "GearsDialogService";
-  private final String DIALOG_PACKAGE = "com.android.browser";
-  private final String DIALOG_CLASS = DIALOG_PACKAGE + ".GearsDialog";
-
-  public static Lock lock = new ReentrantLock();
-  public static Condition dialogFinished = lock.newCondition();
-  public static String results = null;
-
-  @Override
-  public IBinder onBind(Intent intent) {
-    if (IGearsDialogService.class.getName().equals(intent.getAction())) {
-      return mBinder;
-    }
-    return null;
-  }
-
-  private final IGearsDialogService.Stub mBinder = new IGearsDialogService.Stub() {
-    public String showDialog(String htmlContent, String dialogArguments,
-          boolean inSettings) {
-      return GearsDialogService.this.showDialog(htmlContent, dialogArguments,
-                 inSettings);
-    }
-  };
-
-  public static void closeDialog(String res) {
-    results = res;
-  }
-
-  public static void signalFinishedDialog() {
-    lock.lock();
-    dialogFinished.signal();
-    lock.unlock();
-  }
-
-  /**
-   * Show a 'true' modal dialog displaying html content, necessary
-   * for Gears. The method starts the GearsDialog activity, passing
-   * the necessary parameters to it, and then wait until the activity
-   * is finished. When the dialog closes, it sets the variable results.
-   */
-  public String showDialog(String htmlContent, String dialogArguments,
-      boolean inSettings) {
-    try {
-      Intent intent = new Intent();
-      intent.setClassName(DIALOG_PACKAGE, DIALOG_CLASS);
-      intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-      intent.putExtra("htmlContent", htmlContent);
-      intent.putExtra("dialogArguments", dialogArguments);
-      intent.putExtra("inSettings", inSettings);
-      lock.lock();
-      startActivity(intent);
-      dialogFinished.await();
-    } catch (InterruptedException e) {
-      Log.e(TAG, "exception e: " + e);
-    } catch (ActivityNotFoundException e) {
-      Log.e(TAG, "exception e: " + e);
-    } finally {
-      lock.unlock();
-    }
-    return results;
-  }
-}
diff --git a/src/com/android/browser/GearsFilePickerDialog.java b/src/com/android/browser/GearsFilePickerDialog.java
deleted file mode 100644
index d84a970..0000000
--- a/src/com/android/browser/GearsFilePickerDialog.java
+++ /dev/null
@@ -1,725 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.browser;
-
-import android.app.Activity;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Color;
-import android.net.Uri;
-import android.os.Environment;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.provider.MediaStore;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.BaseAdapter;
-import android.widget.GridView;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Vector;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-/**
- * Gears FilePicker dialog
- */
-class GearsFilePickerDialog extends GearsBaseDialog
-  implements View.OnTouchListener {
-
-  private static final String TAG = "Gears FilePicker";
-  private static Bitmap mDirectoryIcon;
-  private static Bitmap mDefaultIcon;
-  private static Bitmap mImageIcon;
-  private static Bitmap mBackIcon;
-
-  private static String MULTIPLE_FILES = "MULTIPLE_FILES";
-  private static String SINGLE_FILE = "SINGLE_FILE";
-
-  private static ImagesLoad mImagesLoader;
-  private FilePickerAdapter mAdapter;
-  private String mSelectionMode;
-  private boolean mMultipleSelection;
-  private String mCurrentPath;
-
-  // Disable saving thumbnails until this is refactored to fit into
-  // existing schemes.
-  private static final boolean enableSavedThumbnails = false;
-
-  public GearsFilePickerDialog(Activity activity,
-                               Handler handler,
-                               String arguments) {
-    super (activity, handler, arguments);
-    mAdapter = new FilePickerAdapter(activity);
-    parseArguments();
-  }
-
-  public void parseArguments() {
-    mSelectionMode = MULTIPLE_FILES;
-    try {
-      JSONObject json = new JSONObject(mDialogArguments);
-
-      if (json.has("mode")) {
-        mSelectionMode = json.getString("mode");
-      }
-    } catch (JSONException e) {
-      Log.e(TAG, "exc: " + e);
-    }
-    if (mSelectionMode.equalsIgnoreCase(SINGLE_FILE)) {
-      mMultipleSelection = false;
-    } else {
-      mMultipleSelection = true;
-    }
-  }
-
-  public void setup() {
-    inflate(R.layout.gears_dialog_filepicker, R.id.panel_content);
-    setupButtons(0,
-                 R.string.filepicker_button_allow,
-                 R.string.filepicker_button_deny);
-    setupDialog();
-
-    TextView textViewPath = (TextView) findViewById(R.id.path_name);
-    if (textViewPath != null) {
-      textViewPath.setText(R.string.filepicker_path);
-    }
-
-    GridView view = (GridView) findViewById(R.id.files_list);
-    view.setAdapter(mAdapter);
-    view.setOnTouchListener(this);
-
-    setSelectionText();
-
-    mImagesLoader = new ImagesLoad(mAdapter);
-    mImagesLoader.setAdapterView(view);
-    Thread thread = new Thread(mImagesLoader);
-    thread.start();
-  }
-
-  public void setSelectionText() {
-    Vector elements = mAdapter.selectedElements();
-    if (elements == null)
-      return;
-    TextView info = (TextView) findViewById(R.id.selection);
-    int nbElements = elements.size();
-    if (nbElements == 0) {
-      info.setText(R.string.filepicker_no_files_selected);
-    } else if (nbElements == 1) {
-      info.setText(R.string.filepicker_one_file_selected);
-    } else {
-      info.setText(nbElements + " " +
-                   mActivity.getString(
-                       R.string.filepicker_some_files_selected));
-    }
-  }
-
-  public void setCurrentPath(String path) {
-    if (path != null) {
-      mCurrentPath = path;
-      TextView textViewPath = (TextView) findViewById(R.id.current_path);
-      if (textViewPath != null) {
-        textViewPath.setText(path);
-      }
-    }
-  }
-
-  public void setupDialog(TextView message, ImageView icon) {
-    message.setText(R.string.filepicker_message);
-    message.setTextSize(24);
-    icon.setImageResource(R.drawable.gears_icon_32x32);
-  }
-
-  public boolean onTouch(View v, MotionEvent event) {
-    mImagesLoader.pauseIconRequest();
-    return false;
-  }
-
-  /**
-   * Utility class to load and generate thumbnails
-   * for image files
-   */
-  class ImagesLoad implements Runnable {
-    private Map mImagesMap;
-    private Vector mImagesPath;
-    private BaseAdapter mAdapter;
-    private AdapterView mAdapterView;
-    private Vector<FilePickerElement> mElements;
-    private Handler mLoaderHandler;
-
-    ImagesLoad(BaseAdapter adapter) {
-      mAdapter = adapter;
-    }
-
-    public void signalChanges() {
-      Message message = mHandler.obtainMessage(GearsBaseDialog.NEW_ICON,
-                                               mAdapter);
-      mHandler.sendMessage(message);
-    }
-
-    /**
-     * TODO: use the same thumbnails as the photo app
-     * (bug: http://b/issue?id=1497927)
-     */
-    public String getThumbnailPath(String path) {
-      File f = new File(path);
-      String myPath = f.getParent() + "/.thumbnails";
-      File d = new File(myPath);
-      if (!d.exists()) {
-        d.mkdirs();
-      }
-      return myPath + "/" + f.getName();
-    }
-
-    public boolean saveImage(String path, Bitmap image) {
-      boolean ret = false;
-      try {
-        FileOutputStream outStream = new FileOutputStream(path);
-        ret = image.compress(Bitmap.CompressFormat.JPEG, 100, outStream);
-      } catch (IOException e) {
-        Log.e(TAG, "IOException ", e);
-      }
-      return ret;
-    }
-
-    public Bitmap generateImage(FilePickerElement elem) {
-      String path = elem.getPath();
-      Bitmap finalImage = null;
-      try {
-        String thumbnailPath = getThumbnailPath(path);
-        if (enableSavedThumbnails) {
-          File thumbnail = new File(thumbnailPath);
-          if (thumbnail.exists()) {
-            finalImage = BitmapFactory.decodeFile(thumbnailPath);
-            if (finalImage != null) {
-              return finalImage;
-            }
-          }
-        }
-        BitmapFactory.Options options = new BitmapFactory.Options();
-        options.inJustDecodeBounds = true;
-        BitmapFactory.decodeFile(path, options);
-
-        int width = options.outWidth;
-        int height = options.outHeight;
-        int size = 128;
-        int sampleSize = 1;
-        if (width > size || height > size) {
-          sampleSize = 2;
-          while ((width / sampleSize > size)
-                 || (height / sampleSize > size)) {
-            sampleSize += 2;
-          }
-        }
-        options.inJustDecodeBounds = false;
-        options.inSampleSize = sampleSize;
-        Bitmap originalImage = BitmapFactory.decodeFile(path, options);
-        if (originalImage == null) {
-          return null;
-        }
-        finalImage = Bitmap.createScaledBitmap(originalImage, size, size, true);
-        if (enableSavedThumbnails) {
-          if (saveImage(thumbnailPath, finalImage)) {
-            if (mDebug) {
-              Log.v(TAG, "Saved thumbnail for file " + path);
-            }
-          } else {
-            Log.e(TAG, "Could NOT Save thumbnail for file " + path);
-          }
-        }
-        originalImage.recycle();
-      } catch (java.lang.OutOfMemoryError e) {
-        Log.e(TAG, "Intercepted OOM ", e);
-      }
-      return finalImage;
-    }
-
-    public void pauseIconRequest() {
-      Message message = Message.obtain(mLoaderHandler,
-                                       GearsBaseDialog.PAUSE_REQUEST_ICON);
-      mLoaderHandler.sendMessageAtFrontOfQueue(message);
-    }
-    public void postIconRequest(FilePickerElement item, int position) {
-      if (item == null) {
-        return;
-      }
-      Message message = mLoaderHandler.obtainMessage(
-          GearsBaseDialog.REQUEST_ICON, position, 0, item);
-      mLoaderHandler.sendMessage(message);
-    }
-
-    public void generateIcon(FilePickerElement elem) {
-      if (elem.isImage()) {
-        if (elem.getThumbnail() == null) {
-          Bitmap image = generateImage(elem);
-          if (image != null) {
-            elem.setThumbnail(image);
-          }
-        }
-      }
-    }
-
-    public void setAdapterView(AdapterView view) {
-      mAdapterView = view;
-    }
-
-    public void run() {
-      Looper.prepare();
-      mLoaderHandler = new Handler() {
-        public void handleMessage(Message msg) {
-          int visibleElements = 10;
-          if (msg.what == GearsBaseDialog.PAUSE_REQUEST_ICON) {
-            try {
-              // We are busy (likely) scrolling the view,
-              // so we just pause the loading.
-              Thread.sleep(1000);
-              mLoaderHandler.removeMessages(
-                  GearsBaseDialog.PAUSE_REQUEST_ICON);
-            } catch (InterruptedException e) {
-              Log.e(TAG, "InterruptedException ", e);
-            }
-          } else if (msg.what == GearsBaseDialog.REQUEST_ICON) {
-            try {
-              Thread.sleep(10);
-            } catch (InterruptedException e) {
-              Log.e(TAG, "InterruptedException ", e);
-            }
-            FilePickerElement elem = (FilePickerElement) msg.obj;
-            int firstVisiblePosition = mAdapterView.getFirstVisiblePosition();
-            // If the elements are not visible, we slow down the update
-            // TODO: replace this by a low-priority thread
-            if ((msg.arg1 < firstVisiblePosition - visibleElements)
-                && msg.arg1 > firstVisiblePosition + visibleElements) {
-              try {
-                Thread.sleep(100);
-              } catch (InterruptedException e) {
-              }
-            }
-            generateIcon(elem);
-            signalChanges();
-          }
-        }
-      };
-      Looper.loop();
-    }
-  }
-
-  /**
-   * Utility class representing an element displayed in the
-   * file picker, associated with an icon and/or thumbnail
-   */
-  class FilePickerElement {
-    private File mPath;
-    private String mName;
-    private Bitmap mIcon;
-    private boolean mIsSelected;
-    private Vector mChildren;
-    private FilePickerElement mParent;
-    private boolean mIsParent;
-    private BaseAdapter mAdapter;
-    private String mExtension;
-    private Bitmap mThumbnail;
-    private boolean mIsImage;
-
-    public FilePickerElement(String name, BaseAdapter adapter) {
-      this(name, adapter, null);
-    }
-
-    public FilePickerElement(String path, String name, BaseAdapter adapter) {
-      this(path, name, adapter, null);
-    }
-
-    public FilePickerElement(String name,
-                             BaseAdapter adapter,
-                             FilePickerElement parent) {
-      mName = name;
-      mAdapter = adapter;
-      mParent = parent;
-      mIsSelected = false;
-      mChildren = null;
-    }
-
-    public FilePickerElement(String path,
-                             String name,
-                             BaseAdapter adapter,
-                             FilePickerElement parent) {
-      mPath = new File(path);
-      mName = name;
-      mIsSelected = false;
-      mChildren = null;
-      mParent = parent;
-      mAdapter = adapter;
-      mExtension = null;
-
-      setIcons();
-    }
-
-    public void setIcons() {
-      if (mPath.isDirectory()) {
-        if (mDirectoryIcon == null) {
-          mDirectoryIcon = BitmapFactory.decodeResource(
-              getResources(), R.drawable.gears_folder);
-        }
-        mIcon = mDirectoryIcon;
-
-      } else {
-        if (isImage()) {
-          if (mImageIcon == null) {
-            mImageIcon = BitmapFactory.decodeResource(
-                getResources(), R.drawable.gears_file_image);
-          }
-          mIcon = mImageIcon;
-        } else if (isAudio()) {
-          mIcon = BitmapFactory.decodeResource(
-              getResources(), R.drawable.gears_file_audio);
-        } else if (isVideo()) {
-          mIcon = BitmapFactory.decodeResource(
-              getResources(), R.drawable.gears_file_video);
-        } else {
-          if (mDefaultIcon == null) {
-            mDefaultIcon = BitmapFactory.decodeResource(
-                getResources(), R.drawable.gears_file_unknown);
-          }
-          mIcon = mDefaultIcon;
-        }
-      }
-      if (mBackIcon == null) {
-        mBackIcon = BitmapFactory.decodeResource(getResources(),
-                com.android.internal.R.drawable.ic_menu_back);
-      }
-    }
-
-    public boolean isImage() {
-      if (mIsImage) return mIsImage;
-      String extension = getExtension();
-      if (extension != null) {
-        if (extension.equalsIgnoreCase("jpg") ||
-            extension.equalsIgnoreCase("jpeg") ||
-            extension.equalsIgnoreCase("png") ||
-            extension.equalsIgnoreCase("gif")) {
-          mIsImage = true;
-          return true;
-        }
-      }
-      return false;
-    }
-
-    public boolean isAudio() {
-      String extension = getExtension();
-      if (extension != null) {
-        if (extension.equalsIgnoreCase("mp3") ||
-            extension.equalsIgnoreCase("wav") ||
-            extension.equalsIgnoreCase("aac")) {
-          return true;
-        }
-      }
-      return false;
-    }
-
-    public boolean isVideo() {
-      String extension = getExtension();
-      if (extension != null) {
-        if (extension.equalsIgnoreCase("mpg") ||
-            extension.equalsIgnoreCase("mpeg") ||
-            extension.equalsIgnoreCase("mpe") ||
-            extension.equalsIgnoreCase("divx") ||
-            extension.equalsIgnoreCase("3gpp") ||
-            extension.equalsIgnoreCase("avi")) {
-          return true;
-        }
-      }
-      return false;
-    }
-
-    public void setParent(boolean isParent) {
-      mIsParent = isParent;
-    }
-
-    public boolean isDirectory() {
-      return mPath.isDirectory();
-    }
-
-    public String getExtension() {
-      if (isDirectory()) {
-        return null;
-      }
-      if (mExtension == null) {
-        String path = getPath();
-        int index = path.lastIndexOf(".");
-        if ((index != -1) && (index != path.length() - 1)){
-          // if we find a dot that is not the last character
-          mExtension = path.substring(index+1);
-          return mExtension;
-        }
-      }
-      return mExtension;
-    }
-
-    public void refresh() {
-      mChildren = null;
-      Vector children = getChildren();
-      for (int i = 0; i < children.size(); i++) {
-        FilePickerElement elem = (FilePickerElement) children.get(i);
-        mImagesLoader.postIconRequest(elem, i);
-      }
-    }
-
-    public Vector getChildren() {
-      if (isDirectory()) {
-        if (mChildren == null) {
-          mChildren = new Vector();
-          File[] files = mPath.listFiles();
-          if (mParent != null) {
-            mChildren.add(mParent);
-            mParent.setParent(true);
-          }
-          for (int i = 0; i < files.length; i++) {
-            String name = files[i].getName();
-            String fpath = files[i].getPath();
-            if (!name.startsWith(".")) { // hide dotfiles
-              FilePickerElement elem = new FilePickerElement(fpath, name,
-                                                             mAdapter, this);
-              elem.setParent(false);
-              mChildren.add(elem);
-            }
-          }
-        }
-      }
-      return mChildren;
-    }
-
-    public FilePickerElement getChild(int position) {
-      Vector children = getChildren();
-      if (children != null) {
-        return (FilePickerElement) children.get(position);
-      }
-      return null;
-    }
-
-    public Bitmap getIcon(int position) {
-      if (mIsParent) {
-        return mBackIcon;
-      }
-      if (isImage()) {
-        if (mThumbnail != null) {
-          return mThumbnail;
-        } else {
-          mImagesLoader.postIconRequest(this, position);
-        }
-      }
-      return mIcon;
-    }
-
-    public Bitmap getThumbnail() {
-      return mThumbnail;
-    }
-
-    public void setThumbnail(Bitmap icon) {
-      mThumbnail = icon;
-    }
-
-    public String getName() {
-      return mName;
-    }
-
-    public String getPath() {
-      return mPath.getPath();
-    }
-
-    public void toggleSelection() {
-      mIsSelected = !mIsSelected;
-    }
-
-    public boolean isSelected() {
-      return mIsSelected;
-    }
-
-  }
-
-  /**
-   * Adapter for the GridView
-   */
-  class FilePickerAdapter extends BaseAdapter {
-    private Context mContext;
-    private Map mImagesMap;
-    private Map mImagesSelected;
-
-    private Vector mImages;
-    private Vector<FilePickerElement> mFiles;
-
-    private FilePickerElement mRootElement;
-    private FilePickerElement mCurrentElement;
-
-    public FilePickerAdapter(Context context) {
-      mContext = context;
-      mImages = new Vector();
-      mFiles = new Vector();
-
-      mImagesMap = Collections.synchronizedMap(new HashMap());
-      mImagesSelected = new HashMap();
-
-      Uri requests[] = { MediaStore.Images.Media.INTERNAL_CONTENT_URI,
-                         MediaStore.Images.Media.EXTERNAL_CONTENT_URI };
-
-      String startingPath = Environment.getExternalStorageDirectory().getPath();
-      mRootElement = new FilePickerElement(startingPath, "SD Card", this);
-      mCurrentElement = mRootElement;
-    }
-
-    public void addImage(String path) {
-      mImages.add(path);
-      Bitmap image = BitmapFactory.decodeResource(
-          getResources(), R.drawable.gears_file_unknown);
-      mImagesMap.put(path, image);
-      mImagesSelected.put(path, Boolean.FALSE);
-    }
-
-    public int getCount() {
-      Vector elems = mCurrentElement.getChildren();
-      setCurrentPath(mCurrentElement.getPath());
-      return elems.size();
-    }
-
-    public Object getItem(int position) {
-      return position;
-    }
-
-    public long getItemId(int position) {
-      return position;
-    }
-
-    public Vector selectedElements() {
-      if (mCurrentElement == null) {
-        return null;
-      }
-      Vector children = mCurrentElement.getChildren();
-      Vector ret = new Vector();
-      for (int i = 0; i < children.size(); i++) {
-        FilePickerElement elem = (FilePickerElement) children.get(i);
-        if (elem.isSelected()) {
-          ret.add(elem);
-        }
-      }
-      return ret;
-    }
-
-    public View getView(int position, View convertView, ViewGroup parent) {
-      View cell = convertView;
-      if (cell == null) {
-        LayoutInflater inflater = (LayoutInflater) getSystemService(
-            Context.LAYOUT_INFLATER_SERVICE);
-        cell = inflater.inflate(R.layout.gears_dialog_filepicker_cell, null);
-      }
-      ImageView imageView = (ImageView) cell.findViewById(R.id.icon);
-      TextView textView = (TextView) cell.findViewById(R.id.name);
-      FilePickerElement elem = mCurrentElement.getChild(position);
-      if (elem == null) {
-        String message = "Could not get elem " + position;
-        message += " for " + mCurrentElement.getPath();
-        Log.e(TAG, message);
-        return null;
-      }
-      String path = elem.getPath();
-      textView.setText(elem.getName());
-
-      View.OnClickListener listener = new View.OnClickListener() {
-        public void onClick(View view) {
-          int pos = (Integer) view.getTag();
-          FilePickerElement elem = mCurrentElement.getChild(pos);
-          if (elem.isDirectory()) {
-            mCurrentElement = elem;
-            mCurrentElement.refresh();
-          } else {
-            if (mMultipleSelection) {
-              elem.toggleSelection();
-            } else {
-              Vector elems = selectedElements();
-              if (elems != null) {
-                if (elems.size() == 0) {
-                  elem.toggleSelection();
-                } else if ((elems.size() == 1)
-                           && elem.isSelected()) {
-                  elem.toggleSelection();
-                }
-              }
-            }
-          }
-          setSelectionText();
-          notifyDataSetChanged();
-        }
-      };
-      cell.setLayoutParams(new GridView.LayoutParams(96, 96));
-      cell.setOnClickListener(listener);
-      cell.setOnTouchListener(new View.OnTouchListener() {
-        public boolean onTouch(View v, MotionEvent event) {
-          if (event.getAction() == MotionEvent.ACTION_DOWN) {
-            int color = getResources().getColor(R.color.icon_selection);
-            v.setBackgroundColor(color);
-          } else {
-            v.setBackgroundColor(Color.WHITE);
-          }
-          return false;
-        }
-      });
-
-      cell.setTag(position);
-
-      if (elem.isSelected()) {
-        int color = getResources().getColor(R.color.icon_selection);
-        cell.setBackgroundColor(color);
-      } else {
-        cell.setBackgroundColor(Color.WHITE);
-      }
-      Bitmap bmp = elem.getIcon(position);
-      if (bmp != null) {
-        imageView.setImageBitmap(bmp);
-      }
-
-      return cell;
-    }
-  }
-
-  private String selectedFiles() {
-    Vector selection = mAdapter.selectedElements();
-    JSONArray jsonSelection = new JSONArray();
-    if (selection != null) {
-      for (int i = 0; i < selection.size(); i++) {
-        FilePickerElement elem = (FilePickerElement) selection.get(i);
-        jsonSelection.put(elem.getPath());
-      }
-    }
-    return jsonSelection.toString();
-  }
-
-  public String closeDialog(int closingType) {
-    return selectedFiles();
-  }
-}
diff --git a/src/com/android/browser/GearsNativeDialog.java b/src/com/android/browser/GearsNativeDialog.java
index c8ae741..ecf166d 100644
--- a/src/com/android/browser/GearsNativeDialog.java
+++ b/src/com/android/browser/GearsNativeDialog.java
@@ -17,6 +17,7 @@
 package com.android.browser;
 
 import android.app.Activity;
+import android.app.Dialog;
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Bundle;
@@ -24,17 +25,17 @@
 import android.os.Message;
 import android.util.Config;
 import android.util.Log;
+import android.view.Gravity;
 import android.view.KeyEvent;
 import android.view.Window;
 import android.widget.BaseAdapter;
+import android.widget.Toast;
 
 import android.webkit.gears.NativeDialog;
 
 import com.android.browser.GearsBaseDialog;
 import com.android.browser.GearsPermissionsDialog;
 import com.android.browser.GearsSettingsDialog;
-import com.android.browser.GearsShortcutDialog;
-import com.android.browser.GearsFilePickerDialog;
 
 /**
  * Native dialog Activity used by gears
@@ -54,16 +55,12 @@
   private int mDialogType;
   private final int SETTINGS_DIALOG = 1;
   private final int PERMISSION_DIALOG = 2;
-  private final int SHORTCUT_DIALOG = 3;
-  private final int LOCATION_DIALOG = 4;
-  private final int FILEPICKER_DIALOG = 5;
+  private final int LOCATION_DIALOG = 3;
 
   private final String VERSION_STRING = "version";
   private final String SETTINGS_DIALOG_STRING = "settings_dialog";
   private final String PERMISSION_DIALOG_STRING = "permissions_dialog";
-  private final String SHORTCUT_DIALOG_STRING = "shortcuts_dialog";
   private final String LOCATION_DIALOG_STRING = "locations_dialog";
-  private final String FILEPICKER_DIALOG_STRING = "filepicker_dialog";
 
   private boolean mDialogDismissed = false;
 
@@ -90,10 +87,15 @@
 
   @Override
   public void onCreate(Bundle icicle) {
-    super.onCreate(icicle);
-    requestWindowFeature(Window.FEATURE_NO_TITLE);
-    setContentView(R.layout.gears_dialog);
     getArguments();
+    if (mDialogType == SETTINGS_DIALOG) {
+      setTheme(android.R.style.Theme);
+    }
+    super.onCreate(icicle);
+    if (mDialogType != SETTINGS_DIALOG) {
+      requestWindowFeature(Window.FEATURE_NO_TITLE);
+      setContentView(R.layout.gears_dialog);
+    }
 
     switch (mDialogType) {
       case SETTINGS_DIALOG:
@@ -103,15 +105,9 @@
       case PERMISSION_DIALOG:
         dialog = new GearsPermissionsDialog(this, mHandler, mDialogArguments);
         break;
-      case SHORTCUT_DIALOG:
-        dialog = new GearsShortcutDialog(this, mHandler, mDialogArguments);
-        break;
       case LOCATION_DIALOG:
         dialog = new GearsPermissionsDialog(this, mHandler, mDialogArguments);
         break;
-      case FILEPICKER_DIALOG:
-        dialog = new GearsFilePickerDialog(this, mHandler, mDialogArguments);
-        break;
       default:
         dialog = new GearsBaseDialog(this, mHandler, mDialogArguments);
     }
@@ -128,7 +124,7 @@
    */
   private void getArguments() {
     if (mDebug) {
-      mDialogType = FILEPICKER_DIALOG +1;
+      mDialogType = LOCATION_DIALOG +1;
       mockArguments();
 
       return;
@@ -150,12 +146,8 @@
       mGearsVersion = intent.getStringExtra(VERSION_STRING);
     } else if (dialogTypeString.equalsIgnoreCase(PERMISSION_DIALOG_STRING)) {
       mDialogType = PERMISSION_DIALOG;
-    } else if (dialogTypeString.equalsIgnoreCase(SHORTCUT_DIALOG_STRING)) {
-      mDialogType = SHORTCUT_DIALOG;
     } else if (dialogTypeString.equalsIgnoreCase(LOCATION_DIALOG_STRING)) {
       mDialogType = LOCATION_DIALOG;
-    } else if (dialogTypeString.equalsIgnoreCase(FILEPICKER_DIALOG_STRING)) {
-      mDialogType = FILEPICKER_DIALOG;
     }
   }
 
@@ -165,17 +157,6 @@
    * Set mock arguments.
    */
   private void mockArguments() {
-    String argumentsShortcuts = "{ locale: \"en-US\","
-        + "name: \"My Application\", link: \"http://www.google.com/\","
-        + "description: \"This application does things does things!\","
-        + "icon16x16: \"http://google-gears.googlecode.com/"
-        + "svn/trunk/gears/test/manual/shortcuts/16.png\","
-        + "icon32x32: \"http://google-gears.googlecode.com/"
-        + "svn/trunk/gears/test/manual/shortcuts/32.png\","
-        + "icon48x48: \"http://google-gears.googlecode.com/"
-        + "svn/trunk/gears/test/manual/shortcuts/48.png\","
-        + "icon128x128: \"http://google-gears.googlecode.com/"
-        + "svn/trunk/gears/test/manual/shortcuts/128.png\"}";
 
     String argumentsPermissions = "{ locale: \"en-US\", "
         + "origin: \"http://www.google.com\", dialogType: \"localData\","
@@ -185,6 +166,9 @@
         + "customMessage: \"Press the button to enable my "
         + "application to run offline!\" };";
 
+    String argumentsPermissions2 = "{ locale: \"en-US\", "
+        + "origin: \"http://www.google.com\", dialogType: \"localData\" };";
+
     String argumentsLocation = "{ locale: \"en-US\", "
         + "origin: \"http://www.google.com\", dialogType: \"locationData\","
         + "customIcon: \"http://google-gears.googlecode.com/"
@@ -195,8 +179,8 @@
 
     String argumentsSettings = "{ locale: \"en-US\", permissions: [ { "
         + "name: \"http://www.google.com\", "
-        + "localStorage: { permissionState: 1 }, "
-        + "locationData: { permissionState: 0 } }, "
+        + "localStorage: { permissionState: 0 }, "
+        + "locationData: { permissionState: 1 } }, "
         + "{ name: \"http://www.aaronboodman.com\", "
         + "localStorage: { permissionState: 1 }, "
         + "locationData: { permissionState: 2 } }, "
@@ -205,9 +189,6 @@
         + "locationData: { permissionState: 2 } } ] }";
 
     switch (mDialogType) {
-      case SHORTCUT_DIALOG:
-        mDialogArguments = argumentsShortcuts;
-        break;
       case PERMISSION_DIALOG:
         mDialogArguments = argumentsPermissions;
         break;
@@ -216,6 +197,7 @@
         break;
       case SETTINGS_DIALOG:
         mDialogArguments = argumentsSettings;
+        break;
     }
   }
 
@@ -232,6 +214,14 @@
     NativeDialog.closeDialog(ret);
     notifyEndOfDialog();
     finish();
+
+    // If the dialog sets a notification, we display it.
+    int notification = dialog.notification();
+    if (notification != 0) {
+      Toast toast = Toast.makeText(this, notification, Toast.LENGTH_LONG);
+      toast.setGravity(Gravity.BOTTOM, 0, 0);
+      toast.show();
+    }
   }
 
   @Override
@@ -265,10 +255,26 @@
    * NativeDialog that we are done.
    */
   public boolean dispatchKeyEvent(KeyEvent event) {
-    if (event.getKeyCode() ==  KeyEvent.KEYCODE_BACK && event.isDown()) {
-      closeDialog(GearsBaseDialog.CANCEL);
+    if ((event.getKeyCode() == KeyEvent.KEYCODE_BACK)
+      && (event.getAction() == KeyEvent.ACTION_DOWN)) {
+      if (!dialog.handleBackButton()) {
+        // if the dialog doesn't do anything with the back button
+        closeDialog(GearsBaseDialog.CANCEL);
+      }
+      return true; // event consumed
     }
     return super.dispatchKeyEvent(event);
   }
 
+  /**
+   * If the dialog call showDialog() on ourself, we let
+   * it handle the creation of this secondary dialog.
+   * It is used in GearsSettingsDialog, to create the confirmation
+   * dialog when the user click on "Remove this site from Gears"
+   */
+  @Override
+  protected Dialog onCreateDialog(int id) {
+    return dialog.onCreateDialog(id);
+  }
+
 }
diff --git a/src/com/android/browser/GearsPermissions.java b/src/com/android/browser/GearsPermissions.java
index cd46324..e48e045 100644
--- a/src/com/android/browser/GearsPermissions.java
+++ b/src/com/android/browser/GearsPermissions.java
@@ -49,37 +49,37 @@
     public static final int PERMISSION_DENIED = 2;
 
     String mName;
-    int mRowRsc;
-    int mAllowedButtonRsc;
-    int mDeniedButtonRsc;
+    int mTitleRsc;
+    int mSubtitleOnRsc;
+    int mSubtitleOffRsc;
 
     PermissionType(String name) {
       mName = name;
     }
 
-    public void setResources(int rowRsc, int allowedButtonRsc,
-        int deniedButtonRsc) {
-      mRowRsc = rowRsc;
-      mAllowedButtonRsc = allowedButtonRsc;
-      mDeniedButtonRsc = deniedButtonRsc;
-    }
-
-    public int getRowRsc() {
-      return mRowRsc;
-    }
-
-    public int getAllowedButtonRsc() {
-      return mAllowedButtonRsc;
-    }
-
-    public int getDeniedButtonRsc() {
-      return mDeniedButtonRsc;
+    public void setResources(int titleRsc,
+        int subtitleOnRsc, int subtitleOffRsc) {
+      mTitleRsc = titleRsc;
+      mSubtitleOnRsc = subtitleOnRsc;
+      mSubtitleOffRsc = subtitleOffRsc;
     }
 
     public String getName() {
       return mName;
     }
 
+    public int getTitleRsc() {
+      return mTitleRsc;
+    }
+
+    public int getSubtitleOnRsc() {
+      return mSubtitleOnRsc;
+    }
+
+    public int getSubtitleOffRsc() {
+      return mSubtitleOffRsc;
+    }
+
   }
 
   /**
diff --git a/src/com/android/browser/GearsPermissionsDialog.java b/src/com/android/browser/GearsPermissionsDialog.java
index b57ab0b..dbec363 100644
--- a/src/com/android/browser/GearsPermissionsDialog.java
+++ b/src/com/android/browser/GearsPermissionsDialog.java
@@ -35,6 +35,7 @@
   private static final String TAG = "GearsPermissionsDialog";
 
   private String mDialogType;
+  private int mNotification = 0;
 
   public GearsPermissionsDialog(Activity activity,
                                 Handler handler,
@@ -48,15 +49,6 @@
                  R.string.permission_button_allow,
                  R.string.permission_button_deny);
 
-    View contentBorder = findViewById(R.id.content_border);
-    if (contentBorder != null) {
-      contentBorder.setBackgroundResource(R.color.permission_border);
-    }
-    View contentBackground = findViewById(R.id.content_background);
-    if (contentBackground != null) {
-      contentBackground.setBackgroundResource(R.color.permission_background);
-    }
-
     try {
       JSONObject json = new JSONObject(mDialogArguments);
 
@@ -84,6 +76,16 @@
         downloadIcon(iconUrl);
       }
 
+      View msg = findViewById(R.id.permission_dialog_message);
+      if (msg != null) {
+        TextView dialogMessage = (TextView) msg;
+        if (mDialogType.equalsIgnoreCase(LOCAL_DATA_STRING)) {
+          dialogMessage.setText(R.string.query_data_message);
+        } else if (mDialogType.equalsIgnoreCase(LOCATION_DATA_STRING)) {
+          dialogMessage.setText(R.string.location_message);
+        }
+      }
+
     } catch (JSONException e) {
       Log.e(TAG, "JSON exception ", e);
     }
@@ -91,15 +93,11 @@
 
   public void setupDialog(TextView message, ImageView icon) {
     if (mDialogType.equalsIgnoreCase(LOCAL_DATA_STRING)) {
-      message.setText(R.string.query_data_message);
-      icon.setImageResource(R.drawable.gears_local_data);
+      message.setText(R.string.query_data_prompt);
+      icon.setImageResource(android.R.drawable.ic_popup_disk_full);
     } else if (mDialogType.equalsIgnoreCase(LOCATION_DATA_STRING)) {
-      message.setText(R.string.location_message);
-      icon.setImageResource(R.drawable.gears_location_data);
-      View privacyPolicyLabel = findViewById(R.id.privacy_policy_label);
-      if (privacyPolicyLabel != null) {
-        privacyPolicyLabel.setVisibility(View.VISIBLE);
-      }
+      message.setText(R.string.location_prompt);
+      icon.setImageResource(R.drawable.ic_dialog_menu_generic);
     }
   }
 
@@ -108,9 +106,19 @@
     switch (closingType) {
       case ALWAYS_DENY:
         ret = "{\"allow\": false, \"permanently\": true }";
+        if (mDialogType.equalsIgnoreCase(LOCAL_DATA_STRING)) {
+          mNotification = R.string.storage_notification_alwaysdeny;
+        } else if (mDialogType.equalsIgnoreCase(LOCATION_DATA_STRING)) {
+          mNotification = R.string.location_notification_alwaysdeny;
+        }
         break;
       case ALLOW:
         ret = "{\"allow\": true, \"permanently\": true }";
+        if (mDialogType.equalsIgnoreCase(LOCAL_DATA_STRING)) {
+          mNotification = R.string.storage_notification;
+        } else if (mDialogType.equalsIgnoreCase(LOCATION_DATA_STRING)) {
+          mNotification = R.string.location_notification;
+        }
         break;
       case DENY:
         ret = "{\"allow\": false, \"permanently\": false }";
@@ -119,4 +127,7 @@
     return ret;
   }
 
+  public int notification() {
+    return mNotification;
+  }
 }
diff --git a/src/com/android/browser/GearsSettingsDialog.java b/src/com/android/browser/GearsSettingsDialog.java
index 56a1d8d..5ea2342 100644
--- a/src/com/android/browser/GearsSettingsDialog.java
+++ b/src/com/android/browser/GearsSettingsDialog.java
@@ -17,14 +17,21 @@
 package com.android.browser;
 
 import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
 import android.content.Context;
+import android.content.DialogInterface;
 import android.os.Handler;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ArrayAdapter;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.BaseAdapter;
 import android.widget.Button;
+import android.widget.CheckBox;
 import android.widget.CompoundButton;
 import android.widget.ImageView;
 import android.widget.ListAdapter;
@@ -33,6 +40,7 @@
 import android.widget.TextView;
 
 import com.android.browser.GearsPermissions.OriginPermissions;
+import com.android.browser.GearsPermissions.Permission;
 import com.android.browser.GearsPermissions.PermissionsChangesListener;
 import com.android.browser.GearsPermissions.PermissionType;
 
@@ -55,6 +63,7 @@
   private Vector<OriginPermissions> mCurrentPermissions = null;
 
   private Vector<PermissionType> mPermissions;
+  private static final int CONFIRMATION_REMOVE_DIALOG = 1;
 
   // We declare the permissions globally to simplify the code
   private final PermissionType LOCAL_STORAGE =
@@ -64,23 +73,23 @@
 
   private boolean mChanges = false;
 
+  SettingsAdapter mListAdapter;
 
   public GearsSettingsDialog(Activity activity,
                              Handler handler,
                              String arguments) {
     super (activity, handler, arguments);
+    activity.setContentView(R.layout.gears_settings);
   }
 
   public void setup() {
     // First let's add the permissions' resources
-    LOCAL_STORAGE.setResources(R.id.local_storage_choice,
-       R.id.local_storage_allowed,
-       R.id.local_storage_denied);
-
-    LOCATION_DATA.setResources(R.id.location_data_choice,
-       R.id.location_data_allowed,
-       R.id.location_data_denied);
-
+    LOCAL_STORAGE.setResources(R.string.settings_storage_title,
+                               R.string.settings_storage_subtitle_on,
+                               R.string.settings_storage_subtitle_off);
+    LOCATION_DATA.setResources(R.string.settings_location_title,
+                               R.string.settings_location_subtitle_on,
+                               R.string.settings_location_subtitle_off);
     // add the permissions to the list of permissions.
     mPermissions = new Vector<PermissionType>();
     mPermissions.add(LOCAL_STORAGE);
@@ -88,25 +97,7 @@
     OriginPermissions.setListener(this);
 
 
-    inflate(R.layout.gears_dialog_settings, R.id.panel_content);
     setupDialog();
-    setupButtons(0,
-                 R.string.settings_button_allow,
-                 R.string.settings_button_deny);
-
-    // by default disable the allow button (it will get enabled if
-    // something is changed by the user)
-    View buttonView = findViewById(R.id.button_allow);
-    if (buttonView != null) {
-      Button button = (Button) buttonView;
-      button.setEnabled(false);
-    }
-
-    View gearsVersionView = findViewById(R.id.gears_version);
-    if (gearsVersionView != null) {
-      TextView gearsVersion = (TextView) gearsVersionView;
-      gearsVersion.setText(mGearsVersion);
-    }
 
     // We manage the permissions using three vectors, mSitesPermissions,
     // mOriginalPermissions and mCurrentPermissions.
@@ -165,32 +156,23 @@
     View listView = findViewById(R.id.sites_list);
     if (listView != null) {
       ListView list = (ListView) listView;
-      list.setAdapter(new SettingsAdapter(mActivity, mSitesPermissions));
+      mListAdapter = new SettingsAdapter(mActivity, mSitesPermissions);
+      list.setAdapter(mListAdapter);
       list.setScrollBarStyle(android.view.View.SCROLLBARS_OUTSIDE_INSET);
+      list.setOnItemClickListener(mListAdapter);
     }
     if (mDebug) {
       printPermissions();
     }
   }
 
+  private void setMainTitle() {
+    String windowTitle = mActivity.getString(R.string.pref_extras_gears_settings);
+    mActivity.setTitle(windowTitle);
+  }
+
   public void setupDialog() {
-    View dialogTitleView = findViewById(R.id.dialog_title);
-    if (dialogTitleView != null) {
-      TextView dialogTitle = (TextView) dialogTitleView;
-      dialogTitle.setText(R.string.settings_title);
-      dialogTitle.setVisibility(View.VISIBLE);
-    }
-    View dialogSubtitleView = findViewById(R.id.dialog_subtitle);
-    if (dialogSubtitleView != null) {
-      TextView dialogSubtitle = (TextView) dialogSubtitleView;
-      dialogSubtitle.setText(R.string.settings_message);
-      dialogSubtitle.setVisibility(View.VISIBLE);
-    }
-    View iconView = findViewById(R.id.icon);
-    if (iconView != null) {
-      ImageView icon = (ImageView) iconView;
-      icon.setImageResource(R.drawable.gears_icon_32x32);
-    }
+    setMainTitle();
   }
 
   /**
@@ -198,164 +180,95 @@
    */
   public boolean setPermission(PermissionType type, int perm) {
     if (mChanges == false) {
-      signalChanges();
+      mChanges = true;
     }
     return mChanges;
   }
 
-  /**
-   * Controller class for binding the model (OriginPermissions) with
-   * the UI.
-   */
-  class PermissionController {
-    final static int ALLOWED_BUTTON = 1;
-    final static int DENIED_BUTTON = 2;
-    private int mButtonType;
-    private PermissionType mPermissionType;
-    private OriginPermissions mPermissions;
-
-    PermissionController(PermissionType permissionType, int buttonType,
-        OriginPermissions permissions) {
-      mPermissionType = permissionType;
-      mButtonType = buttonType;
-      mPermissions = permissions;
-    }
-
-    public boolean isChecked() {
-      boolean checked = false;
-
-      switch (mButtonType) {
-        case ALLOWED_BUTTON:
-          if (mPermissions.getPermission(mPermissionType) ==
-              PermissionType.PERMISSION_ALLOWED) {
-            checked = true;
-          } break;
-        case DENIED_BUTTON:
-          if (mPermissions.getPermission(mPermissionType) ==
-              PermissionType.PERMISSION_DENIED) {
-            checked = true;
-          }
-      }
-      return checked;
-    }
-
-    public String print() {
-        return printType() + " for " + mPermissions.getOrigin();
-    }
-
-    private String printType() {
-      switch (mButtonType) {
-        case ALLOWED_BUTTON:
-          return "ALLOWED_BUTTON";
-        case DENIED_BUTTON:
-          return "DENIED_BUTTON";
-      }
-      return "UNKNOWN BUTTON";
-    }
-
-    public void changed(boolean isChecked) {
-      if (isChecked == isChecked()) {
-        return; // already set
-      }
-
-      switch (mButtonType) {
-        case ALLOWED_BUTTON:
-          mPermissions.setPermission(mPermissionType,
-              PermissionType.PERMISSION_ALLOWED);
-          break;
-        case DENIED_BUTTON:
-          mPermissions.setPermission(mPermissionType,
-              PermissionType.PERMISSION_DENIED);
-          break;
-      }
-    }
+  public boolean handleBackButton() {
+    return mListAdapter.backButtonPressed();
   }
 
-
+  /**
+   * We use this to create a confirmation dialog when the user
+   * clicks on "remove this site from gears"
+   */
+  public Dialog onCreateDialog(int id) {
+    return new AlertDialog.Builder(mActivity)
+        .setTitle(R.string.settings_confirmation_remove_title)
+        .setMessage(R.string.settings_confirmation_remove)
+        .setPositiveButton(android.R.string.ok,
+                           new AlertDialog.OnClickListener() {
+          public void onClick(DialogInterface dlg, int which) {
+            mListAdapter.removeCurrentSite();
+          }
+        })
+        .setNegativeButton(android.R.string.cancel, null)
+        .setIcon(android.R.drawable.ic_dialog_alert)
+        .create();
+  }
 
   /**
    * Adapter class for the list view in the settings dialog
    *
-   * Every row in the settings dialog display the permissions
-   * for a given origin. For every type of permission
-   * (location, local data...) there is two radio buttons to
-   * authorize or deny the permission.
-   * A remove button is also present to let the user remove
-   * all the authorization of an origin in one step.
+   * We first display a list of all the origins (sites), or
+   * a message saying that no permission is set if the list is empty.
+   * When the user click on one of the origin, we then display
+   * the list of the permissions existing for that origin.
+   * Each permission can be either allowed or denied by clicking
+   * on the checkbox.
+   * The last row is a special case, allowing to remove the entire origin.
    */
-  class SettingsAdapter extends ArrayAdapter {
+  class SettingsAdapter extends BaseAdapter
+      implements AdapterView.OnItemClickListener {
     private Activity mContext;
     private List mItems;
+    private OriginPermissions mCurrentSite;
+    private Vector mCurrentPermissions;
+    private int MAX_ROW_HEIGHT = 64;
 
     SettingsAdapter(Activity context, List items) {
-      super(context, R.layout.gears_dialog_settings_row, items);
       mContext = context;
       mItems = items;
+      mCurrentSite = null;
     }
 
-    /*
-     * setup the necessary listeners for the radiobuttons
-     * When the buttons are clicked the permissions change.
-     */
-    private void createAndSetButtonListener(View buttonView,
-        OriginPermissions perms, PermissionType permissionType,
-        int buttonType) {
-      if (buttonView == null) {
-        return;
-      }
-      RadioButton button = (RadioButton) buttonView;
-
-      button.setOnCheckedChangeListener(null);
-      PermissionController p = new PermissionController(permissionType,
-          buttonType, perms);
-      button.setTag(p);
-
-      CompoundButton.OnCheckedChangeListener listener =
-          new CompoundButton.OnCheckedChangeListener() {
-        public void onCheckedChanged(CompoundButton buttonView,
-            boolean isChecked) {
-          PermissionController perm = (PermissionController)buttonView.getTag();
-          perm.changed(isChecked);
+    public int getCount() {
+      if (mCurrentSite == null) {
+        int size = mItems.size();
+        if (size == 0) {
+          return 1;
+        } else {
+          return size;
         }
-      };
-
-      button.setOnCheckedChangeListener(listener);
-
-      if (p.isChecked() != button.isChecked()) {
-        button.setChecked(p.isChecked());
       }
+      return mCurrentPermissions.size() + 1;
     }
 
-    /*
-     * setup the remove button for an origin: each row has a global
-     * remove button in addition to the radio buttons controlling the
-     * permissions.
-     */
-    private void setRemoveButton(Button button, OriginPermissions perms) {
-      Button.OnClickListener listener = new Button.OnClickListener() {
-        public void onClick(View buttonView) {
-          if (mChanges == false) {
-            signalChanges();
-          }
-          OriginPermissions perm = (OriginPermissions) buttonView.getTag();
-          perm.setPermission(LOCAL_STORAGE, PermissionType.PERMISSION_NOT_SET);
-          perm.setPermission(LOCATION_DATA, PermissionType.PERMISSION_NOT_SET);
-          mSitesPermissions.remove(perm);
+    public long getItemId(int position) {
+      return position;
+    }
 
-          View view = findViewById(R.id.sites_list);
-          if (view != null) {
-            ListView listView = (ListView) view;
-            ListAdapter listAdapter = listView.getAdapter();
-            if (listAdapter != null) {
-              SettingsAdapter settingsAdapter = (SettingsAdapter) listAdapter;
-              settingsAdapter.notifyDataSetChanged();
-            }
-          }
+    private String shortName(String url) {
+        // We remove the http and https prefix
+        if (url.startsWith("http://")) {
+          return url.substring(7);
         }
-      };
-      button.setTag(perms);
-      button.setOnClickListener(listener);
-      displayAsLink(button);
+        if (url.startsWith("https://")) {
+          return url.substring(8);
+        }
+        return url;
+    }
+
+    public Object getItem(int position) {
+      if (mCurrentSite == null) {
+        if (mItems.size() == 0) {
+          return null;
+        } else {
+          return mItems.get(position);
+        }
+      }
+      return mCurrentPermissions.get(position);
     }
 
     public View getView(int position, View convertView, ViewGroup parent) {
@@ -363,46 +276,117 @@
       if (row == null) { // no cached view, we create one
         LayoutInflater inflater = (LayoutInflater) getSystemService(
             Context.LAYOUT_INFLATER_SERVICE);
-        row = inflater.inflate(R.layout.gears_dialog_settings_row, null);
+        row = inflater.inflate(R.layout.gears_settings_row, null);
       }
+      row.setMinimumHeight(MAX_ROW_HEIGHT);
 
-      OriginPermissions perms = (OriginPermissions) mItems.get(position);
+      if (mCurrentSite == null) {
+        if (mItems.size() == 0) {
+          hideView(row, R.id.title);
+          hideView(row, R.id.subtitle);
+          hideView(row, R.id.checkbox);
+          hideView(row, R.id.icon);
+          setText(row, R.id.info, R.string.settings_empty);
+        } else {
+          hideView(row, R.id.subtitle);
+          hideView(row, R.id.info);
+          hideView(row, R.id.checkbox);
+          OriginPermissions perms = (OriginPermissions) mItems.get(position);
+          setText(row, R.id.title, shortName(perms.getOrigin()));
+          showView(row, R.id.icon);
+        }
+      } else {
+        if (position == getCount() - 1) {
+          // last position: "remove this site from gears"
+          hideView(row, R.id.subtitle);
+          hideView(row, R.id.info);
+          hideView(row, R.id.checkbox);
+          hideView(row, R.id.icon);
+          setText(row, R.id.title, R.string.settings_remove_site);
+        } else {
+          hideView(row, R.id.info);
+          hideView(row, R.id.icon);
+          showView(row, R.id.checkbox);
 
-      View nameView = row.findViewById(R.id.origin_name);
-      if (nameView != null) {
-        TextView originName = (TextView) nameView;
-        originName.setText(perms.getOrigin());
-      }
+          PermissionType type =
+              (PermissionType) mCurrentPermissions.get(position);
+          setText(row, R.id.title, type.getTitleRsc());
 
-      View removeButtonView = row.findViewById(R.id.origin_remove);
-      if (removeButtonView != null) {
-        Button removeButton = (Button) removeButtonView;
-        setRemoveButton(removeButton, perms);
-      }
-
-      for (int i = 0; i < mPermissions.size(); i++) {
-        PermissionType type = mPermissions.get(i);
-        int rowRsc = type.getRowRsc();
-        int allowedButtonRsc = type.getAllowedButtonRsc();
-        int deniedButtonRsc = type.getDeniedButtonRsc();
-
-        View rowView = row.findViewById(rowRsc);
-        if (rowView != null) {
-          int perm = perms.getPermission(type);
-          if (perm != PermissionType.PERMISSION_NOT_SET) {
-            createAndSetButtonListener(row.findViewById(allowedButtonRsc),
-                perms, type, PermissionController.ALLOWED_BUTTON);
-            createAndSetButtonListener(row.findViewById(deniedButtonRsc),
-                perms, type, PermissionController.DENIED_BUTTON);
-            rowView.setVisibility(View.VISIBLE);
-          } else {
-            rowView.setVisibility(View.GONE);
+          View checkboxView = row.findViewById(R.id.checkbox);
+          if (checkboxView != null) {
+            CheckBox checkbox = (CheckBox) checkboxView;
+            int perm = mCurrentSite.getPermission(type);
+            if (perm == PermissionType.PERMISSION_DENIED) {
+              setText(row, R.id.subtitle, type.getSubtitleOffRsc());
+              checkbox.setChecked(false);
+            } else {
+              setText(row, R.id.subtitle, type.getSubtitleOnRsc());
+              checkbox.setChecked(true);
+            }
           }
         }
       }
-
       return row;
     }
+
+    public void removeCurrentSite() {
+      mCurrentSite.setPermission(LOCAL_STORAGE,
+                                 PermissionType.PERMISSION_NOT_SET);
+      mCurrentSite.setPermission(LOCATION_DATA,
+                                 PermissionType.PERMISSION_NOT_SET);
+      mSitesPermissions.remove(mCurrentSite);
+      mCurrentSite = null;
+      setMainTitle();
+      notifyDataSetChanged();
+    }
+
+    public void onItemClick(AdapterView<?> parent,
+                            View view,
+                            int position,
+                            long id) {
+      if (mItems.size() == 0) {
+        return;
+      }
+      if (mCurrentSite == null) {
+         mCurrentSite = (OriginPermissions) mItems.get(position);
+         mCurrentPermissions = new Vector();
+         for (int i = 0; i < mPermissions.size(); i++) {
+           PermissionType type = mPermissions.get(i);
+           int perm = mCurrentSite.getPermission(type);
+           if (perm != PermissionType.PERMISSION_NOT_SET) {
+             mCurrentPermissions.add(type);
+           }
+         }
+         mContext.setTitle(shortName(mCurrentSite.getOrigin()));
+      } else {
+        if (position == getCount() - 1) { // last item (remove site)
+          // Ask the user to confirm
+          // If yes, removeCurrentSite() will be called via the dialog callback.
+          mActivity.showDialog(CONFIRMATION_REMOVE_DIALOG);
+        } else {
+          PermissionType type =
+              (PermissionType) mCurrentPermissions.get(position);
+          if (mCurrentSite.getPermission(type) ==
+              PermissionType.PERMISSION_ALLOWED) {
+            mCurrentSite.setPermission(type, PermissionType.PERMISSION_DENIED);
+          } else {
+            mCurrentSite.setPermission(type, PermissionType.PERMISSION_ALLOWED);
+          }
+        }
+      }
+      notifyDataSetChanged();
+    }
+
+    public boolean backButtonPressed() {
+      if (mCurrentSite != null) { // we intercept the back button
+        mCurrentSite = null;
+        setMainTitle();
+        notifyDataSetChanged();
+        return true;
+      }
+      return false;
+    }
+
   }
 
   /**
@@ -423,21 +407,6 @@
   }
 
   /**
-   * Utility method used by the settings dialog, signaling
-   * the user the settings have been modified.
-   * We reflect this by enabling the Allow button (disabled
-   * by default).
-   */
-  public void signalChanges() {
-    View view = findViewById(R.id.button_allow);
-    if (view != null) {
-      Button button = (Button) view;
-      button.setEnabled(true);
-    }
-    mChanges = true;
-  }
-
-  /**
    * Computes the difference between the original permissions and the
    * current ones. Returns a json-formatted string.
    * It is used by the Settings dialog.
@@ -479,18 +448,7 @@
   }
 
   public String closeDialog(int closingType) {
-    String ret = null;
-    switch (closingType) {
-      case ALWAYS_DENY:
-        ret = "{\"allow\": false }";
-        break;
-      case ALLOW:
-        ret = computeDiff(true);
-        break;
-      case DENY:
-        ret = computeDiff(false);
-        break;
-    }
+    String ret = computeDiff(mChanges);
 
     if (mDebug) {
       printPermissions();
diff --git a/src/com/android/browser/GearsShortcutDialog.java b/src/com/android/browser/GearsShortcutDialog.java
deleted file mode 100644
index deede12..0000000
--- a/src/com/android/browser/GearsShortcutDialog.java
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.browser;
-
-import android.app.Activity;
-import android.os.Handler;
-import android.util.Log;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-
-/**
- * Gears Shortcut dialog
- */
-class GearsShortcutDialog extends GearsBaseDialog {
-
-  private static final String TAG = "GearsPermissionsDialog";
-
-  private final String ICON_16 = "icon16x16";
-  private final String ICON_32 = "icon32x32";
-  private final String ICON_48 = "icon48x48";
-  private final String ICON_128 = "icon128x128";
-
-  public GearsShortcutDialog(Activity activity,
-                             Handler handler,
-                             String arguments) {
-    super (activity, handler, arguments);
-  }
-
-  public void setup() {
-    inflate(R.layout.gears_dialog_permission, R.id.panel_content);
-    setupButtons(R.string.shortcut_button_alwaysdeny,
-                 R.string.shortcut_button_allow,
-                 R.string.shortcut_button_deny);
-
-    View contentBorder = findViewById(R.id.content_border);
-    if (contentBorder != null) {
-      contentBorder.setBackgroundResource(R.color.shortcut_border);
-    }
-    View contentBackground = findViewById(R.id.content_background);
-    if (contentBackground != null) {
-      contentBackground.setBackgroundResource(R.color.shortcut_background);
-    }
-
-    try {
-      JSONObject json = new JSONObject(mDialogArguments);
-
-      String iconUrl = pickIconToRender(json);
-      if (iconUrl != null) {
-        downloadIcon(iconUrl);
-      }
-
-      setupDialog();
-
-      setLabel(json, "name", R.id.origin_title);
-      setLabel(json, "link", R.id.origin_subtitle);
-      setLabel(json, "description", R.id.origin_message);
-    } catch (JSONException e) {
-      Log.e(TAG, "JSON exception", e);
-    }
-  }
-
-  public void setupDialog(TextView message, ImageView icon) {
-    message.setText(R.string.shortcut_message);
-    icon.setImageResource(R.drawable.gears_icon_48x48);
-  }
-
-  /**
-   * Utility method to validate an icon url. Used in the
-   * shortcut dialog.
-   */
-  boolean validIcon(JSONObject json, String name) {
-    try {
-      if (json.has(name)) {
-        String str = json.getString(name);
-        if (str.length() > 0) {
-          return true;
-        }
-      }
-    } catch (JSONException e) {
-      Log.e(TAG, "JSON exception", e);
-    }
-    return false;
-  }
-
-
-  /**
-   * Utility method to pick the best indicated icon
-   * from the dialogs' arguments. Used in the
-   * shortcut dialog.
-   */
-  String pickIconToRender(JSONObject json) {
-    try {
-      if (validIcon(json, ICON_48)) { // ideal size
-        mChoosenIconSize = 48;
-        return json.getString(ICON_48);
-      } else if (validIcon(json, ICON_32)) {
-        mChoosenIconSize = 32;
-        return json.getString(ICON_32);
-      } else if (validIcon(json, ICON_128)) {
-        mChoosenIconSize = 128;
-        return json.getString(ICON_128);
-      } else if (validIcon(json, ICON_16)) {
-        mChoosenIconSize = 16;
-        return json.getString(ICON_16);
-      }
-    } catch (JSONException e) {
-      Log.e(TAG, "JSON exception", e);
-    }
-    mChoosenIconSize = 0;
-    return null;
-  }
-
-  public String closeDialog(int closingType) {
-    String ret = null;
-    switch (closingType) {
-      case ALWAYS_DENY:
-        ret = "{\"allow\": false, \"permanently\": true }";
-        break;
-      case ALLOW:
-        ret = "{\"allow\": true, \"locations\": 0 }";
-        break;
-      case DENY:
-        ret = null;
-        break;
-    }
-    return ret;
-  }
-
-}
diff --git a/src/com/android/browser/HistoryItem.java b/src/com/android/browser/HistoryItem.java
index c83ced1..55e43f0 100644
--- a/src/com/android/browser/HistoryItem.java
+++ b/src/com/android/browser/HistoryItem.java
@@ -17,26 +17,31 @@
  
 package com.android.browser;
 
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
 import android.content.Context;
+import android.database.Cursor;
 import android.graphics.Bitmap;
-import android.graphics.Color;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
-import android.graphics.drawable.PaintDrawable;
-import android.view.LayoutInflater;
-import android.widget.LinearLayout;
+import android.net.Uri;
+import android.provider.Browser;
+import android.util.Log;
+import android.view.View;
+import android.webkit.WebIconDatabase;
+import android.widget.CompoundButton;
+import android.widget.ImageView;
 import android.widget.TextView;
+import android.widget.Toast;
+
+import java.util.Date;
 
 /**
  *  Layout representing a history item in the classic history viewer.
  */
-/* package */ class HistoryItem extends LinearLayout {
+/* package */ class HistoryItem extends BookmarkItem {
 
-    private TextView    mTitleView; // Truncated Title
-    private String      mUrl;       // Full Url
-    private TextView    mUrlText;   // Truncated Url
-    
+    private CompoundButton  mStar;      // Star for bookmarking
+    private CompoundButton.OnCheckedChangeListener  mListener;
     /**
      *  Create a new HistoryItem.
      *  @param context  Context for this HistoryItem.
@@ -44,83 +49,79 @@
     /* package */ HistoryItem(Context context) {
         super(context);
 
-        setWillNotDraw(false);
-        LayoutInflater factory = LayoutInflater.from(context);
-        factory.inflate(R.layout.history_item, this);
-        mTitleView = (TextView) findViewById(R.id.title);
-        mUrlText = (TextView) findViewById(R.id.url);
+        mStar = (CompoundButton) findViewById(R.id.star);
+        mStar.setVisibility(View.VISIBLE);
+        mListener = new CompoundButton.OnCheckedChangeListener() {
+            public void onCheckedChanged(CompoundButton buttonView,
+                    boolean isChecked) {
+                ContentResolver cr = mContext.getContentResolver();
+                Cursor cursor = cr.query(
+                        Browser.BOOKMARKS_URI,
+                        Browser.HISTORY_PROJECTION,
+                        "url = ?",
+                        new String[] { mUrl },
+                        null);
+                boolean first = cursor.moveToFirst();
+                // Should be in the database no matter what
+                if (!first) {
+                    throw new AssertionError("URL is not in the database!");
+                }
+                if (isChecked) {
+                    // Add to bookmarks
+                    // FIXME: Share code with AddBookmarkPage.java
+                    ContentValues map = new ContentValues();
+                    map.put(Browser.BookmarkColumns.CREATED,
+                            new Date().getTime());
+                    map.put(Browser.BookmarkColumns.TITLE, getName());
+                    map.put(Browser.BookmarkColumns.BOOKMARK, 1);
+                    try {
+                        cr.update(Browser.BOOKMARKS_URI, map, 
+                                "_id = " + cursor.getInt(0), null);
+                    } catch (IllegalStateException e) {
+                        Log.e("HistoryItem", "no database!");
+                    }
+                    WebIconDatabase.getInstance().retainIconForPageUrl(mUrl);
+                    // catch IllegalStateException?
+                    Toast.makeText(mContext, R.string.added_to_bookmarks,
+                            Toast.LENGTH_LONG).show();
+                } else {
+                    // Remove from bookmarks
+                    // FIXME: This code should be shared with
+                    // BrowserBookmarksAdapter.java
+                    WebIconDatabase.getInstance().releaseIconForPageUrl(mUrl);
+                    Uri uri = ContentUris.withAppendedId(Browser.BOOKMARKS_URI,
+                            cursor.getInt(Browser.HISTORY_PROJECTION_ID_INDEX));
+                    // It is no longer a bookmark, but it is still a visited
+                    // site.
+                    ContentValues values = new ContentValues();
+                    values.put(Browser.BookmarkColumns.BOOKMARK, 0);
+                    try {
+                        cr.update(uri, values, null, null);
+                    } catch (IllegalStateException e) {
+                        Log.e("HistoryItem", "no database!");
+                    }
+                    Toast.makeText(mContext, R.string.removed_from_bookmarks,
+                            Toast.LENGTH_LONG).show();
+                }
+                cursor.deactivate();
+            }
+        };
     }
     
     void copyTo(HistoryItem item) {
-        item.mTitleView.setText(mTitleView.getText());
+        item.mTextView.setText(mTextView.getText());
         item.mUrlText.setText(mUrlText.getText());
-    }
-    
-    /**
-     * Return the name of this HistoryItem.
-     * @return  String name of this HistoryItem.
-     /
-    /* package */ String getName() {
-        return mTitleView.getText().toString();
+        item.setIsBookmark(mStar.isChecked());
+        item.mImageView.setImageDrawable(mImageView.getDrawable());
     }
 
     /**
-     * Return the url of this HistoryItem.
-     * @return  String url of this HistoryItem.
-     /
-    /* package */ String getUrl() {
-        return mUrl;
-    }
-
-    /**
-     *  Set the favicon for this item.
-     *
-     *  @param b    The new bitmap for this item.
-     *              If it is null, will use the default.
+     *  Set whether or not this represents a bookmark, and make sure the star
+     *  behaves appropriately.
      */
-    /* package */ void setFavicon(Bitmap b) {
-        Drawable[] array = new Drawable[2];
-        PaintDrawable p = new PaintDrawable(Color.WHITE);
-        p.setCornerRadius(3f);
-        array[0] = p;
-        if (b != null) {
-            array[1] = new BitmapDrawable(b);
-        } else {
-            array[1] = new BitmapDrawable(mContext.getResources().
-                    openRawResource(R.drawable.app_web_browser_sm));
-        }
-        LayerDrawable d = new LayerDrawable(array);
-        d.setLayerInset(1, 2, 2, 2, 2);
-        d.setBounds(0, 0, 20, 20);
-        mTitleView.setCompoundDrawables(d, null, null, null);
-    }
-    
-    /**
-     *  Set the name for this HistoryItem.
-     *  If the name is longer that BrowserSettings.MAX_TEXTVIEW_LEN characters, 
-     *  the name is truncated to BrowserSettings.MAX_TEXTVIEW_LEN characters. 
-     *  The History activity does not expose a UI element that can show the 
-     *  full title.
-     *  @param  name String representing new name for this HistoryItem.
-     */
-    /* package */ void setName(String name) {
-        if (name != null && name.length() > BrowserSettings.MAX_TEXTVIEW_LEN) {
-            name = name.substring(0, BrowserSettings.MAX_TEXTVIEW_LEN);
-        }
-        mTitleView.setText(name);
-    }
-    
-    /**
-     *  Set the url for this HistoryItem.
-     *  @param  url String representing new url for this HistoryItem.
-     */
-    /* package */ void setUrl(String url) {
-        mUrl = url;
-        // Truncate the url for the screen
-        if (url.length() > BrowserSettings.MAX_TEXTVIEW_LEN) {
-            mUrlText.setText(url.substring(0, BrowserSettings.MAX_TEXTVIEW_LEN));
-        } else {
-            mUrlText.setText(url);
-        }
+    void setIsBookmark(boolean isBookmark) {
+        mStar.setOnCheckedChangeListener(null);
+        mStar.setChecked(isBookmark);
+        mStar.setOnCheckedChangeListener(mListener);
     }
 }
diff --git a/src/com/android/browser/IGearsDialogService.aidl b/src/com/android/browser/IGearsDialogService.aidl
deleted file mode 100644
index 02b30a2..0000000
--- a/src/com/android/browser/IGearsDialogService.aidl
+++ /dev/null
@@ -1,6 +0,0 @@
-package com.android.browser;
-
-interface IGearsDialogService {
-  String showDialog(String htmlContent, String dialogArguments,
-             boolean inSettings);
-}
diff --git a/src/com/android/browser/ImageAdapter.java b/src/com/android/browser/ImageAdapter.java
index b4c1209..e957143 100644
--- a/src/com/android/browser/ImageAdapter.java
+++ b/src/com/android/browser/ImageAdapter.java
@@ -231,20 +231,7 @@
         if (l == null) {
             return;
         }
-        DialogInterface.OnClickListener confirm =
-                new DialogInterface.OnClickListener() {
-                    public void onClick(DialogInterface dialog,
-                            int whichButton) {
-                        l.remove(position);
-                    }
-                };
-        new AlertDialog.Builder(mContext)
-                .setTitle(R.string.close)
-                .setIcon(android.R.drawable.ic_dialog_alert)
-                .setMessage(R.string.close_window)
-                .setPositiveButton(R.string.ok, confirm)
-                .setNegativeButton(R.string.cancel, null)
-                .show();
+        l.remove(position);
     }
 
     /* (non-Javadoc)
diff --git a/src/com/android/browser/MostVisitedActivity.java b/src/com/android/browser/MostVisitedActivity.java
new file mode 100644
index 0000000..704ee27
--- /dev/null
+++ b/src/com/android/browser/MostVisitedActivity.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.browser;
+
+import android.app.Activity;
+import android.app.ListActivity;
+import android.content.Intent;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.database.DataSetObserver;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.os.Handler;
+import android.provider.Browser;
+import android.webkit.WebIconDatabase.IconListener;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+import android.widget.SimpleCursorAdapter;
+import android.widget.TextView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
+
+import java.util.Vector;
+
+public class MostVisitedActivity extends ListActivity {
+
+    private MyAdapter   mAdapter;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mAdapter = new MyAdapter();
+        CombinedBookmarkHistoryActivity.getIconListenerSet(getContentResolver())
+                .addListener(new IconReceiver());
+        setListAdapter(mAdapter);
+        ListView list = getListView();
+        LayoutInflater factory = LayoutInflater.from(this);
+        View v = factory.inflate(R.layout.empty_history, null);
+        addContentView(v, new LayoutParams(LayoutParams.FILL_PARENT,
+                LayoutParams.FILL_PARENT));
+        list.setEmptyView(v);
+    }
+
+    private class IconReceiver implements IconListener {
+        public void onReceivedIcon(String url, Bitmap icon) {
+            setListAdapter(mAdapter);
+        }
+    }
+
+    protected void onListItemClick(ListView l, View v, int position, long id) {
+        TextView tv = (TextView) v.findViewById(R.id.url);
+        String url = tv.getText().toString();
+        loadUrl(url, false);
+    }
+
+    private void loadUrl(String url, boolean newWindow) {
+        Intent intent = new Intent().setAction(url);
+        if (newWindow) {
+            Bundle b = new Bundle();
+            b.putBoolean("new_window", true);
+            intent.putExtras(b);
+        }
+        setResultToParent(RESULT_OK, intent);
+        finish();
+    }
+
+    private class MyAdapter implements ListAdapter {
+        private Vector<DataSetObserver> mObservers;
+        private Cursor mCursor;
+        // These correspond with projection below.
+        private final int mUrlIndex = 0;
+        private final int mTitleIndex = 1;
+        private final int mBookmarkIndex = 2;
+
+        MyAdapter() {
+            mObservers = new Vector<DataSetObserver>();
+            String[] projection = new String[] {
+                    Browser.BookmarkColumns.URL,
+                    Browser.BookmarkColumns.TITLE,
+                    Browser.BookmarkColumns.BOOKMARK };
+            String whereClause = Browser.BookmarkColumns.VISITS + " != 0";
+            String orderBy = Browser.BookmarkColumns.VISITS + " DESC";
+            mCursor = managedQuery(Browser.BOOKMARKS_URI, projection,
+                    whereClause, null, orderBy);
+            mCursor.registerContentObserver(new ChangeObserver());
+        }
+
+        private class ChangeObserver extends ContentObserver {
+            public ChangeObserver() {
+                super(new Handler());
+            }
+
+            @Override
+            public boolean deliverSelfNotifications() {
+                return true;
+            }
+
+            @Override
+            public void onChange(boolean selfChange) {
+                MyAdapter.this.refreshData();
+            }
+        }
+
+        void refreshData() {
+            mCursor.requery();
+            for (DataSetObserver o : mObservers) {
+                o.onChanged();
+            }
+        }
+
+        public View getView(int position, View convertView, ViewGroup parent) {
+            HistoryItem item;
+            if (null == convertView) {
+                item = new HistoryItem(MostVisitedActivity.this);
+            } else {
+                item = (HistoryItem) convertView;
+            }
+            mCursor.moveToPosition(position);
+            item.setName(mCursor.getString(mTitleIndex));
+            String url = mCursor.getString(mUrlIndex);
+            item.setUrl(url);
+            item.setFavicon(CombinedBookmarkHistoryActivity.getIconListenerSet(
+                    getContentResolver()).getFavicon(url));
+            item.setIsBookmark(1 == mCursor.getInt(mBookmarkIndex));
+            return item;
+        }
+
+        public boolean areAllItemsEnabled() {
+            return true;
+        }
+
+        public boolean isEnabled(int position) {
+            return true;
+        }
+
+        public int getCount() {
+            return mCursor.getCount();
+        }
+
+        public Object getItem(int position) {
+            return null;
+        }
+
+        public long getItemId(int position) {
+            return position;
+        }
+
+        // Always a HistoryItem
+        public int getItemViewType(int position) {
+            return 0;
+        }
+
+        public int getViewTypeCount() {
+            return 1;
+        }
+
+        public boolean hasStableIds() {
+            return true;
+        }
+
+        public void registerDataSetObserver(DataSetObserver observer) {
+            mObservers.add(observer);
+        }
+
+        public void unregisterDataSetObserver(DataSetObserver observer) {
+            mObservers.remove(observer);
+        }
+
+        public boolean isEmpty() {
+            return getCount() == 0;
+        }
+    }
+
+    // This Activity is generally a sub-Activity of CombinedHistoryActivity. In
+    // that situation, we need to pass our result code up to our parent.
+    // However, if someone calls this Activity directly, then this has no
+    // parent, and it needs to set it on itself.
+    private void setResultToParent(int resultCode, Intent data) {
+        Activity a = getParent() == null ? this : getParent();
+        a.setResult(resultCode, data);
+    }
+}
+
diff --git a/src/com/android/browser/TabControl.java b/src/com/android/browser/TabControl.java
index dfac185..d58ee22 100644
--- a/src/com/android/browser/TabControl.java
+++ b/src/com/android/browser/TabControl.java
@@ -17,7 +17,9 @@
 package com.android.browser;
 
 import android.content.Context;
+import android.net.http.SslError;
 import android.os.Bundle;
+import android.os.Message;
 import android.util.Config;
 import android.util.Log;
 import android.view.Gravity;
@@ -25,8 +27,10 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.View.OnClickListener;
+import android.webkit.HttpAuthHandler;
 import android.webkit.JsPromptResult;
 import android.webkit.JsResult;
+import android.webkit.SslErrorHandler;
 import android.webkit.WebBackForwardList;
 import android.webkit.WebChromeClient;
 import android.webkit.WebHistoryItem;
@@ -79,6 +83,26 @@
         public boolean shouldOverrideUrlLoading(WebView view, String url) {
             return mClient.shouldOverrideUrlLoading(view, url);
         }
+        @Override
+        public void onReceivedSslError(WebView view, SslErrorHandler handler,
+                SslError error) {
+            mClient.onReceivedSslError(view, handler, error);
+        }
+        @Override
+        public void onReceivedHttpAuthRequest(WebView view,
+                HttpAuthHandler handler, String host, String realm) {
+            mClient.onReceivedHttpAuthRequest(view, handler, host, realm);
+        }
+        @Override
+        public void onFormResubmission(WebView view, Message dontResend,
+                Message resend) {
+            mClient.onFormResubmission(view, dontResend, resend);
+        }
+        @Override
+        public void onReceivedError(WebView view, int errorCode,
+                String description, String failingUrl) {
+            mClient.onReceivedError(view, errorCode, description, failingUrl);
+        }
     }
     // Subclass of WebChromeClient to display javascript dialogs.
     private class SubWindowChromeClient extends WebChromeClient {
@@ -150,11 +174,19 @@
         private Vector<Tab> mChildTabs;
 
         private Boolean mCloseOnExit;
+        // Application identifier used to find tabs that another application
+        // wants to reuse.
+        private String mAppId;
+        // Keep the original url around to avoid killing the old WebView if the
+        // url has not changed.
+        private String mOriginalUrl;
 
         // Construct a new tab
-        private Tab(WebView w, boolean closeOnExit) {
+        private Tab(WebView w, boolean closeOnExit, String appId, String url) {
             mMainView = w;
             mCloseOnExit = closeOnExit;
+            mAppId = appId;
+            mOriginalUrl = url;
         }
 
         /**
@@ -379,30 +411,34 @@
     }
 
     /**
-     * Create a new tab and display the new tab immediately.
+     * Create a new tab.
      * @return The newly createTab or null if we have reached the maximum
      *         number of open tabs.
      */
-    Tab createNewTab(boolean closeOnExit) {
+    Tab createNewTab(boolean closeOnExit, String appId, String url) {
         int size = mTabs.size();
         // Return false if we have maxed out on tabs
         if (MAX_TABS == size) {
             return null;
         }
-        // Create a new WebView
-        WebView w = new WebView(mActivity);
-        w.setMapTrackballToArrowKeys(false); // use trackball directly
-        // Add this WebView to the settings observer list and update the
-        // settings
-        final BrowserSettings s = BrowserSettings.getInstance();
-        s.addObserver(w.getSettings()).update(s, null);
+        final WebView w = createNewWebView();
         // Create a new tab and add it to the tab list
-        Tab t = new Tab(w, closeOnExit);
+        Tab t = new Tab(w, closeOnExit, appId, url);
         mTabs.add(t);
+        // Initially put the tab in the background.
+        putTabInBackground(t);
         return t;
     }
 
     /**
+     * Create a new tab with default values for closeOnExit(false),
+     * appId(null), and url(null).
+     */
+    Tab createNewTab() {
+        return createNewTab(false, null, null);
+    }
+
+    /**
      * Remove the tab from the list. If the tab is the current tab shown, the
      * last created tab will be shown.
      * @param t The tab to be removed.
@@ -510,6 +546,8 @@
     private static final String CURRTITLE = "currentTitle";
     private static final String CLOSEONEXIT = "closeonexit";
     private static final String PARENTTAB = "parentTab";
+    private static final String APPID = "appid";
+    private static final String ORIGINALURL = "originalUrl";
 
     /**
      * Save the state of all the Tabs.
@@ -543,7 +581,7 @@
             final int currentTab = inState.getInt(CURRTAB, -1);
             for (int i = 0; i < numTabs; i++) {
                 if (i == currentTab) {
-                    Tab t = createNewTab(false);
+                    Tab t = createNewTab();
                     // Me must set the current tab before restoring the state
                     // so that all the client classes are set.
                     setCurrentTab(t);
@@ -555,11 +593,15 @@
                 } else {
                     // Create a new tab and don't restore the state yet, add it
                     // to the tab list
-                    Tab t = new Tab(null, false);
+                    Tab t = new Tab(null, false, null, null);
                     t.mSavedState = inState.getBundle(WEBVIEW + i);
                     if (t.mSavedState != null) {
                         t.mUrl = t.mSavedState.getString(CURRURL);
                         t.mTitle = t.mSavedState.getString(CURRTITLE);
+                        // Need to maintain the app id and original url so we
+                        // can possibly reuse this tab.
+                        t.mAppId = t.mSavedState.getString(APPID);
+                        t.mOriginalUrl = t.mSavedState.getString(ORIGINALURL);
                     }
                     mTabs.add(t);
                     mTabQueue.add(t);
@@ -602,7 +644,9 @@
         // free the WebView cache
         Log.w(LOGTAG, "Free WebView cache");
         WebView view = getCurrentWebView();
-        view.clearCache(false);
+        if (view != null) {
+            view.clearCache(false);
+        }
         // force a gc
         System.gc();
     }
@@ -699,13 +743,92 @@
     }
 
     /**
+     * Return the tab with the matching application id.
+     * @param id The application identifier.
+     */
+    Tab getTabFromId(String id) {
+        if (id == null) {
+            return null;
+        }
+        final int size = getTabCount();
+        for (int i = 0; i < size; i++) {
+            final Tab t = getTab(i);
+            if (id.equals(t.mAppId)) {
+                return t;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Recreate the main WebView of the given tab. Returns true if the WebView
+     * was deleted.
+     */
+    boolean recreateWebView(Tab t, String url) {
+        final WebView w = t.mMainView;
+        if (w != null) {
+            if (url != null && url.equals(t.mOriginalUrl)) {
+                // The original url matches the current url. Just go back to the
+                // first history item so we can load it faster than if we
+                // rebuilt the WebView.
+                final WebBackForwardList list = w.copyBackForwardList();
+                if (list != null) {
+                    w.goBackOrForward(-list.getCurrentIndex());
+                    w.clearHistory(); // maintains the current page.
+                    return false;
+                }
+            }
+            // Remove the settings object from the global settings and destroy
+            // the WebView.
+            BrowserSettings.getInstance().deleteObserver(
+                    t.mMainView.getSettings());
+            t.mMainView.destroy();
+        }
+        // Create a new WebView. If this tab is the current tab, we need to put
+        // back all the clients so force it to be the current tab.
+        t.mMainView = createNewWebView();
+        if (getCurrentTab() == t) {
+            setCurrentTab(t, true);
+        }
+        // Clear the saved state except for the app id and close-on-exit
+        // values.
+        t.mSavedState = null;
+        t.mUrl = null;
+        t.mTitle = null;
+        // Save the new url in order to avoid deleting the WebView.
+        t.mOriginalUrl = url;
+        return true;
+    }
+
+    /**
+     * Creates a new WebView and registers it with the global settings.
+     */
+    private WebView createNewWebView() {
+        // Create a new WebView
+        WebView w = new WebView(mActivity);
+        w.setMapTrackballToArrowKeys(false); // use trackball directly
+        // Add this WebView to the settings observer list and update the
+        // settings
+        final BrowserSettings s = BrowserSettings.getInstance();
+        s.addObserver(w.getSettings()).update(s, null);
+        return w;
+    }
+
+    /**
      * Put the current tab in the background and set newTab as the current tab.
      * @param newTab The new tab. If newTab is null, the current tab is not
      *               set.
      */
     boolean setCurrentTab(Tab newTab) {
+        return setCurrentTab(newTab, false);
+    }
+
+    /**
+     * If force is true, this method skips the check for newTab == current.
+     */
+    private boolean setCurrentTab(Tab newTab, boolean force) {
         Tab current = getTab(mCurrentTab);
-        if (current == newTab) {
+        if (current == newTab && !force) {
             return true;
         }
         if (current != null) {
@@ -733,13 +856,7 @@
         boolean needRestore = (mainView == null);
         if (needRestore) {
             // Same work as in createNewTab() except don't do new Tab()
-            newTab.mMainView = mainView = new WebView(mActivity);
-            mainView.setMapTrackballToArrowKeys(false); // use t-ball directly
-
-            // Add this WebView to the settings observer list and update the
-            // settings
-            final BrowserSettings s = BrowserSettings.getInstance();
-            s.addObserver(mainView.getSettings()).update(s, null);
+            newTab.mMainView = mainView = createNewWebView();
         }
         mainView.setWebViewClient(mActivity.getWebViewClient());
         mainView.setWebChromeClient(mActivity.getWebChromeClient());
@@ -872,6 +989,12 @@
                 b.putString(CURRTITLE, t.mTitle);
             }
             b.putBoolean(CLOSEONEXIT, t.mCloseOnExit);
+            if (t.mAppId != null) {
+                b.putString(APPID, t.mAppId);
+            }
+            if (t.mOriginalUrl != null) {
+                b.putString(ORIGINALURL, t.mOriginalUrl);
+            }
 
             // Remember the parent tab so the relationship can be restored.
             if (t.mParentTab != null) {
@@ -892,6 +1015,15 @@
         if (b == null) {
             return false;
         }
+        // Restore the internal state even if the WebView fails to restore.
+        // This will maintain the app id, original url and close-on-exit values.
+        t.mSavedState = null;
+        t.mUrl = null;
+        t.mTitle = null;
+        t.mCloseOnExit = b.getBoolean(CLOSEONEXIT);
+        t.mAppId = b.getString(APPID);
+        t.mOriginalUrl = b.getString(ORIGINALURL);
+
         final WebView w = t.mMainView;
         final WebBackForwardList list = w.restoreState(b);
         if (list == null) {
@@ -902,10 +1034,6 @@
             w.restorePicture(b, f);
             f.delete();
         }
-        t.mSavedState = null;
-        t.mUrl = null;
-        t.mTitle = null;
-        t.mCloseOnExit = b.getBoolean(CLOSEONEXIT);
         return true;
     }
 }