Merge "Fix for the 'Unable to add window' crash."
diff --git a/res/layout/title_bar.xml b/res/layout/title_bar.xml
index 22ab6e4..3765024 100644
--- a/res/layout/title_bar.xml
+++ b/res/layout/title_bar.xml
@@ -15,12 +15,9 @@
    limitations under the License.
 -->
 
-<!-- Manually set the height, to ensure that it matches the SearchDialog's
-        height.  It also ensures that when the stop button is showing it does
-        not change its height. -->
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="57dip"
+    android:layout_height="wrap_content"
     android:orientation="vertical"
     android:paddingLeft="8dip"
     android:paddingRight="12dip"
diff --git a/src/com/android/browser/Bookmarks.java b/src/com/android/browser/Bookmarks.java
index 2cbd851..5ada9dc 100644
--- a/src/com/android/browser/Bookmarks.java
+++ b/src/com/android/browser/Bookmarks.java
@@ -47,6 +47,7 @@
             "content:"
     };
 
+    private final static String LOGTAG = "Bookmarks";
     /**
      *  Add a bookmark to the database.
      *  @param context Context of the calling Activity.  This is used to make
@@ -66,66 +67,74 @@
         // Want to append to the beginning of the list
         long creationTime = new Date().getTime();
         ContentValues map = new ContentValues();
-        Cursor cursor = Browser.getVisitedLike(cr, url);
-        if (cursor.moveToFirst() && cursor.getInt(
-                Browser.HISTORY_PROJECTION_BOOKMARK_INDEX) == 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, name);
-            map.put(Browser.BookmarkColumns.BOOKMARK, 1);
-            map.put(Browser.BookmarkColumns.THUMBNAIL, bitmapToBytes(thumbnail));
-            cr.update(Browser.BOOKMARKS_URI, map,
-                    "_id = " + cursor.getInt(0), null);
-        } else {
-            int count = cursor.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
-                cursor.moveToPosition(i);
-                if (cursor.getString(Browser.HISTORY_PROJECTION_TITLE_INDEX)
-                        .equals(name)) {
-                    // The old bookmark has the same name.
-                    // Update its creation time.
-                    map.put(Browser.BookmarkColumns.CREATED,
-                            creationTime);
-                    cr.update(Browser.BOOKMARKS_URI, map,
-                            "_id = " + cursor.getInt(0), null);
-                    matchedTitle = true;
-                    break;
-                }
-            }
-            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, name);
-                map.put(Browser.BookmarkColumns.URL, url);
+        Cursor cursor = null;
+        try {
+            cursor = Browser.getVisitedLike(cr, url);
+            if (cursor.moveToFirst() && cursor.getInt(
+                    Browser.HISTORY_PROJECTION_BOOKMARK_INDEX) == 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, name);
                 map.put(Browser.BookmarkColumns.BOOKMARK, 1);
-                map.put(Browser.BookmarkColumns.DATE, 0);
-                map.put(Browser.BookmarkColumns.THUMBNAIL, bitmapToBytes(thumbnail));
-                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 = cursor.getInt(
-                            Browser.HISTORY_PROJECTION_VISITS_INDEX);
+                map.put(Browser.BookmarkColumns.THUMBNAIL,
+                        bitmapToBytes(thumbnail));
+                cr.update(Browser.BOOKMARKS_URI, map,
+                        "_id = " + cursor.getInt(0), null);
+            } else {
+                int count = cursor.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
+                    cursor.moveToPosition(i);
+                    if (cursor.getString(Browser.HISTORY_PROJECTION_TITLE_INDEX)
+                            .equals(name)) {
+                        // The old bookmark has the same name.
+                        // Update its creation time.
+                        map.put(Browser.BookmarkColumns.CREATED,
+                                creationTime);
+                        cr.update(Browser.BOOKMARKS_URI, map,
+                                "_id = " + cursor.getInt(0), null);
+                        matchedTitle = true;
+                        break;
+                    }
                 }
-                // 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);
+                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, name);
+                    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.THUMBNAIL,
+                            bitmapToBytes(thumbnail));
+                    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 = cursor.getInt(
+                                Browser.HISTORY_PROJECTION_VISITS_INDEX);
+                    }
+                    // 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);
+                }
             }
+        } catch (IllegalStateException e) {
+            Log.e(LOGTAG, "addBookmark", e);
+        } finally {
+            if (cursor != null) cursor.close();
         }
         if (retainIcon) {
             WebIconDatabase.getInstance().retainIconForPageUrl(url);
         }
-        cursor.deactivate();
         if (context != null) {
             Toast.makeText(context, R.string.added_to_bookmarks,
                     Toast.LENGTH_LONG).show();
@@ -144,41 +153,48 @@
      */
     /* package */ static void removeFromBookmarks(Context context,
             ContentResolver cr, String url, String title) {
-        Cursor cursor = cr.query(
-                Browser.BOOKMARKS_URI,
-                Browser.HISTORY_PROJECTION,
-                "url = ? AND title = ?",
-                new String[] { url, title },
-                null);
-        boolean first = cursor.moveToFirst();
-        // Should be in the database no matter what
-        if (!first) {
-            throw new AssertionError("URL is not in the database! " + url + " " + title);
-        }
-        // Remove from bookmarks
-        WebIconDatabase.getInstance().releaseIconForPageUrl(url);
-        Uri uri = ContentUris.withAppendedId(Browser.BOOKMARKS_URI,
-                cursor.getInt(Browser.HISTORY_PROJECTION_ID_INDEX));
-        int numVisits = cursor.getInt(
-                Browser.HISTORY_PROJECTION_VISITS_INDEX);
-        if (0 == numVisits) {
-            cr.delete(uri, null, null);
-        } else {
-            // 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("removeFromBookmarks", "no database!");
+        Cursor cursor = null;
+        try {
+            cursor = cr.query(
+                    Browser.BOOKMARKS_URI,
+                    Browser.HISTORY_PROJECTION,
+                    "url = ? AND title = ?",
+                    new String[] { url, title },
+                    null);
+            boolean first = cursor.moveToFirst();
+            // Should be in the database no matter what
+            if (!first) {
+                throw new AssertionError("URL is not in the database! " + url
+                        + " " + title);
             }
+            // Remove from bookmarks
+            WebIconDatabase.getInstance().releaseIconForPageUrl(url);
+            Uri uri = ContentUris.withAppendedId(Browser.BOOKMARKS_URI,
+                    cursor.getInt(Browser.HISTORY_PROJECTION_ID_INDEX));
+            int numVisits = cursor.getInt(
+                    Browser.HISTORY_PROJECTION_VISITS_INDEX);
+            if (0 == numVisits) {
+                cr.delete(uri, null, null);
+            } else {
+                // 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("removeFromBookmarks", "no database!");
+                }
+            }
+            if (context != null) {
+                Toast.makeText(context, R.string.removed_from_bookmarks,
+                        Toast.LENGTH_LONG).show();
+            }
+        } catch (IllegalStateException e) {
+            Log.e(LOGTAG, "removeFromBookmarks", e);
+        } finally {
+            if (cursor != null) cursor.close();
         }
-        if (context != null) {
-            Toast.makeText(context, R.string.removed_from_bookmarks,
-                    Toast.LENGTH_LONG).show();
-        }
-        cursor.deactivate();
     }
 
     private static byte[] bitmapToBytes(Bitmap bm) {
diff --git a/src/com/android/browser/BrowserActivity.java b/src/com/android/browser/BrowserActivity.java
index 2f4f39a..4a4e325 100644
--- a/src/com/android/browser/BrowserActivity.java
+++ b/src/com/android/browser/BrowserActivity.java
@@ -553,7 +553,11 @@
                     // If the WebView has the same original url and is on that
                     // page, it can be reused.
                     boolean needsLoad =
-                            mTabControl.recreateWebView(appTab, urlData.mUrl);
+                            mTabControl.recreateWebView(appTab, urlData.mUrl)
+                            // If there is a voice intent in the UrlData, then
+                            // recreateWebView may return false, but we still
+                            // need to force a load.
+                            || urlData.mVoiceIntent != null;
 
                     if (current != appTab) {
                         switchToTab(mTabControl.getTabIndex(appTab));
@@ -1249,20 +1253,20 @@
     private void retainIconsOnStartup() {
         final WebIconDatabase db = WebIconDatabase.getInstance();
         db.open(getDir("icons", 0).getPath());
+        Cursor c = null;
         try {
-            Cursor c = Browser.getAllBookmarks(mResolver);
-            if (!c.moveToFirst()) {
-                c.deactivate();
-                return;
+            c = Browser.getAllBookmarks(mResolver);
+            if (c.moveToFirst()) {
+                int urlIndex = c.getColumnIndex(Browser.BookmarkColumns.URL);
+                do {
+                    String url = c.getString(urlIndex);
+                    db.retainIconForPageUrl(url);
+                } while (c.moveToNext());
             }
-            int urlIndex = c.getColumnIndex(Browser.BookmarkColumns.URL);
-            do {
-                String url = c.getString(urlIndex);
-                db.retainIconForPageUrl(url);
-            } while (c.moveToNext());
-            c.deactivate();
         } catch (IllegalStateException e) {
             Log.e(LOGTAG, "retainIconsOnStartup", e);
+        } finally {
+            if (c!= null) c.close();
         }
     }
 
diff --git a/src/com/android/browser/BrowserDownloadPage.java b/src/com/android/browser/BrowserDownloadPage.java
index c56991e..6a5ef28 100644
--- a/src/com/android/browser/BrowserDownloadPage.java
+++ b/src/com/android/browser/BrowserDownloadPage.java
@@ -31,6 +31,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.provider.Downloads;
+import android.util.Log;
 import android.view.ContextMenu;
 import android.view.ContextMenu.ContextMenuInfo;
 import android.view.LayoutInflater;
@@ -63,6 +64,7 @@
     // will be reopened on this View.
     private View                    mSelectedView;
 
+    private final static String LOGTAG = "BrowserDownloadPage";
     @Override 
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
@@ -252,25 +254,31 @@
 
         @Override
         public void onChange(boolean selfChange) {
-            Cursor cursor = getContentResolver().query(mTrack,
-                    new String[] { Downloads.Impl.COLUMN_STATUS }, null, null,
-                    null);
-            if (cursor.moveToFirst() && Downloads.Impl.isStatusSuccess(
-                    cursor.getInt(0))) {
-                // Do this right away, so we get no more updates.
-                getContentResolver().unregisterContentObserver(
-                        mContentObserver);
-                // Post a runnable in case this ContentObserver gets notified
-                // before the one that updates the ListView.
-                mListView.post(new Runnable() {
-                    public void run() {
-                        // Close the context menu, reopen with up to date data.
-                        closeContextMenu();
-                        openContextMenu(mSelectedView);
-                    }
-                });
+            Cursor cursor = null;
+            try {
+                cursor = getContentResolver().query(mTrack,
+                        new String[] { Downloads.Impl.COLUMN_STATUS }, null, null,
+                        null);
+                if (cursor.moveToFirst() && Downloads.Impl.isStatusSuccess(
+                        cursor.getInt(0))) {
+                    // Do this right away, so we get no more updates.
+                    getContentResolver().unregisterContentObserver(
+                            mContentObserver);
+                    // Post a runnable in case this ContentObserver gets notified
+                    // before the one that updates the ListView.
+                    mListView.post(new Runnable() {
+                        public void run() {
+                            // Close the context menu, reopen with up to date data.
+                            closeContextMenu();
+                            openContextMenu(mSelectedView);
+                        }
+                    });
+                }
+            } catch (IllegalStateException e) {
+                Log.e(LOGTAG, "onChange", e);
+            } finally {
+                if (cursor != null) cursor.close();
             }
-            cursor.deactivate();
         }
     }
 
diff --git a/src/com/android/browser/Tab.java b/src/com/android/browser/Tab.java
index 409f68c..535e8e7 100644
--- a/src/com/android/browser/Tab.java
+++ b/src/com/android/browser/Tab.java
@@ -229,7 +229,7 @@
                     .EXTRA_VOICE_SEARCH_RESULT_HTTP_HEADERS);
             mVoiceSearchData.mSourceIsGoogle = intent.getBooleanExtra(
                     VoiceSearchData.SOURCE_IS_GOOGLE, false);
-            mVoiceSearchData.mVoiceSearchIntent = intent;
+            mVoiceSearchData.mVoiceSearchIntent = new Intent(intent);
         }
         String extraData = intent.getStringExtra(
                 SearchManager.EXTRA_DATA_KEY);
@@ -250,8 +250,11 @@
                 mActivity.sendBroadcast(logIntent);
             }
             if (mVoiceSearchData.mVoiceSearchIntent != null) {
-                mVoiceSearchData.mVoiceSearchIntent.putExtra(
-                        SearchManager.EXTRA_DATA_KEY, extraData);
+                // Copy the Intent, so that each history item will have its own
+                // Intent, with different (or none) extra data.
+                Intent latest = new Intent(mVoiceSearchData.mVoiceSearchIntent);
+                latest.putExtra(SearchManager.EXTRA_DATA_KEY, extraData);
+                mVoiceSearchData.mVoiceSearchIntent = latest;
             }
         }
         mVoiceSearchData.mLastVoiceSearchTitle
diff --git a/src/com/android/browser/TitleBar.java b/src/com/android/browser/TitleBar.java
index 03d44a6..ee6aa9c 100644
--- a/src/com/android/browser/TitleBar.java
+++ b/src/com/android/browser/TitleBar.java
@@ -36,6 +36,7 @@
 import android.text.Spanned;
 import android.text.TextUtils;
 import android.text.style.ImageSpan;
+import android.util.DisplayMetrics;
 import android.util.TypedValue;
 import android.view.ContextMenu;
 import android.view.LayoutInflater;
@@ -43,6 +44,7 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewConfiguration;
+import android.view.ViewGroup;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.ProgressBar;
@@ -77,6 +79,7 @@
     private Drawable        mNormalBackground;
     private Drawable        mLoadingBackground;
     private ImageSpan       mArcsSpan;
+    private int             mExtraMargin;
 
     private static int LONG_PRESS = 1;
 
@@ -99,9 +102,11 @@
         Resources resources = context.getResources();
         mCircularProgress = (Drawable) resources.getDrawable(
                 com.android.internal.R.drawable.search_spinner);
+        DisplayMetrics metrics = resources.getDisplayMetrics();
+        mExtraMargin = (int) TypedValue.applyDimension(
+                TypedValue.COMPLEX_UNIT_DIP, 6.5f, metrics);
         mIconDimension = (int) TypedValue.applyDimension(
-                TypedValue.COMPLEX_UNIT_DIP, 20f,
-                resources.getDisplayMetrics());
+                TypedValue.COMPLEX_UNIT_DIP, 20f, metrics);
         mCircularProgress.setBounds(0, 0, mIconDimension, mIconDimension);
         mHorizontalProgress = (ProgressBar) findViewById(
                 R.id.progress_horizontal);
@@ -261,22 +266,39 @@
         if (mInVoiceMode == inVoiceMode) return;
         mInVoiceMode = inVoiceMode && mVoiceSearchIntent != null;
         Drawable titleDrawable;
+        ViewGroup.MarginLayoutParams params
+                = (ViewGroup.MarginLayoutParams) mTitleBg.getLayoutParams();
         if (mInVoiceMode) {
             mRtButton.setImageDrawable(mVoiceDrawable);
             titleDrawable = mVoiceModeBackground;
             mTitle.setEllipsize(null);
             mRtButton.setVisibility(View.VISIBLE);
             mStopButton.setVisibility(View.GONE);
+            if (params != null) {
+                params.setMargins(0, 0, 0, 0);
+            }
         } else {
             if (mInLoad) {
                 titleDrawable = mLoadingBackground;
                 mRtButton.setVisibility(View.GONE);
                 mStopButton.setVisibility(View.VISIBLE);
+                ViewGroup.MarginLayoutParams stopParams
+                        = (ViewGroup.MarginLayoutParams)
+                        mStopButton.getLayoutParams();
+                if (stopParams != null) {
+                    stopParams.setMargins(0,0,0, mExtraMargin);
+                }
+                if (params != null) {
+                    params.setMargins(0, 0, 0, mExtraMargin);
+                }
             } else {
                 titleDrawable = mNormalBackground;
                 mRtButton.setVisibility(View.VISIBLE);
                 mStopButton.setVisibility(View.GONE);
                 mRtButton.setImageDrawable(mBookmarkDrawable);
+                if (params != null) {
+                    params.setMargins(0, 0, 0, 0);
+                }
             }
             mTitle.setEllipsize(TextUtils.TruncateAt.END);
         }
@@ -300,6 +322,8 @@
      * Update the progress, from 0 to 100.
      */
     /* package */ void setProgress(int newProgress) {
+        ViewGroup.MarginLayoutParams params
+                = (ViewGroup.MarginLayoutParams) mTitleBg.getLayoutParams();
         if (newProgress >= mHorizontalProgress.getMax()) {
             mTitle.setCompoundDrawables(null, null, null, null);
             ((Animatable) mCircularProgress).stop();
@@ -309,6 +333,11 @@
                 mRtButton.setVisibility(View.VISIBLE);
                 mStopButton.setVisibility(View.GONE);
                 mTitleBg.setBackgroundDrawable(mNormalBackground);
+                // Set the margin for the textfield to 0, which is appropriate
+                // for the normal background
+                if (params != null) {
+                    params.setMargins(0, 0, 0, 0);
+                }
             }
             mInLoad = false;
         } else {
@@ -326,6 +355,18 @@
                     mTitleBg.setBackgroundDrawable(mLoadingBackground);
                     mRtButton.setVisibility(View.GONE);
                     mStopButton.setVisibility(View.VISIBLE);
+                    // Set a margin for the bottom of the textfield and the stop
+                    // button so that the total height matches that of the
+                    // title bar when the normal background is showing.
+                    if (params != null) {
+                        params.setMargins(0,0,0, mExtraMargin);
+                    }
+                    ViewGroup.MarginLayoutParams stopParams
+                            = (ViewGroup.MarginLayoutParams)
+                            mStopButton.getLayoutParams();
+                    if (stopParams != null) {
+                        stopParams.setMargins(0,0,0, mExtraMargin);
+                    }
                 }
                 mInLoad = true;
             }