am 2c326e62: DO NOT MERGE Refactor find and select dialogs

Merge commit '2c326e62316884a20822035c5470168d0e3b89ff' into gingerbread-plus-aosp

* commit '2c326e62316884a20822035c5470168d0e3b89ff':
  DO NOT MERGE Refactor find and select dialogs
diff --git a/res/anim/find_dialog_enter.xml b/res/anim/dialog_enter.xml
similarity index 82%
rename from res/anim/find_dialog_enter.xml
rename to res/anim/dialog_enter.xml
index 5e597a4..f98d845 100644
--- a/res/anim/find_dialog_enter.xml
+++ b/res/anim/dialog_enter.xml
@@ -16,6 +16,6 @@
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
         android:interpolator="@android:anim/decelerate_interpolator">
-	<translate android:fromYDelta="25%" android:toYDelta="0" android:duration="75"/>
-	<alpha android:fromAlpha="0.0" android:toAlpha="1.0" android:duration="75" />
+    <translate android:fromYDelta="25%" android:toYDelta="0" android:duration="75"/>
+    <alpha android:fromAlpha="0.0" android:toAlpha="1.0" android:duration="75" />
 </set>
diff --git a/res/anim/find_dialog_exit.xml b/res/anim/dialog_exit.xml
similarity index 82%
rename from res/anim/find_dialog_exit.xml
rename to res/anim/dialog_exit.xml
index 854abd0..dacb5c3 100644
--- a/res/anim/find_dialog_exit.xml
+++ b/res/anim/dialog_exit.xml
@@ -16,7 +16,7 @@
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
         android:interpolator="@android:anim/accelerate_interpolator">
-	<translate android:fromYDelta="0" android:toYDelta="50%" android:duration="50"/>
-	<alpha android:fromAlpha="1.0" android:toAlpha="0.0" android:duration="50" />
+    <translate android:fromYDelta="0" android:toYDelta="50%" android:duration="50"/>
+    <alpha android:fromAlpha="1.0" android:toAlpha="0.0" android:duration="50" />
 </set>
 
diff --git a/res/drawable-hdpi/ic_btn_copy.png b/res/drawable-hdpi/ic_btn_copy.png
new file mode 100644
index 0000000..04fda7f
--- /dev/null
+++ b/res/drawable-hdpi/ic_btn_copy.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_btn_find.png b/res/drawable-hdpi/ic_btn_find.png
new file mode 100755
index 0000000..20e1fbc
--- /dev/null
+++ b/res/drawable-hdpi/ic_btn_find.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_btn_select_all.png b/res/drawable-hdpi/ic_btn_select_all.png
new file mode 100644
index 0000000..839915b
--- /dev/null
+++ b/res/drawable-hdpi/ic_btn_select_all.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_btn_share.png b/res/drawable-hdpi/ic_btn_share.png
new file mode 100644
index 0000000..44db9b1
--- /dev/null
+++ b/res/drawable-hdpi/ic_btn_share.png
Binary files differ
diff --git a/res/layout/browser_select.xml b/res/layout/browser_select.xml
new file mode 100644
index 0000000..b30be8d
--- /dev/null
+++ b/res/layout/browser_select.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/selectControls"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingTop="5dip"
+    android:paddingLeft="4dip"
+    android:paddingRight="4dip"
+    android:paddingBottom="1dip"
+    android:background="@android:drawable/bottom_bar">
+    <ImageButton
+        android:src="@drawable/ic_btn_copy"
+        android:id="@+id/copy"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        />
+
+    <ImageButton
+        android:src="@drawable/ic_btn_share"
+        android:id="@+id/share"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        />
+
+    <ImageButton
+        android:src="@drawable/ic_btn_select_all"
+        android:id="@+id/select_all"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        />
+
+    <ImageButton
+        android:src="@drawable/ic_btn_find"
+        android:id="@+id/find"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        />
+
+    <LinearLayout
+        android:layout_height="fill_parent"
+        android:layout_width="fill_parent"
+        android:layout_weight="1"
+        />
+
+    <ImageButton
+        android:src="@drawable/ic_btn_close_panel"
+        android:id="@+id/done"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        />
+</LinearLayout>
+
diff --git a/res/layout/browser_subwindow.xml b/res/layout/browser_subwindow.xml
index 76d72d5..adf3284 100644
--- a/res/layout/browser_subwindow.xml
+++ b/res/layout/browser_subwindow.xml
@@ -23,6 +23,7 @@
         android:layout_height="match_parent"
         android:padding="10dip" >
         <LinearLayout
+            android:id="@+id/inner_container"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:orientation="vertical"
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 4779aa1..2e8510a 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -30,11 +30,6 @@
         <item name="android:windowContentOverlay">@null</item>
     </style>
 
-    <style name="FindDialog">
-        <item name="android:windowEnterAnimation">@anim/find_dialog_enter</item>
-        <item name="android:windowExitAnimation">@anim/find_dialog_exit</item>
-    </style>
-
     <style name="TitleBar">
         <item name="android:windowEnterAnimation">@anim/title_bar_enter</item>
         <item name="android:windowExitAnimation">@anim/title_bar_exit</item>
diff --git a/res/values/themes.xml b/res/values/themes.xml
deleted file mode 100644
index bb922dd..0000000
--- a/res/values/themes.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<resources>
-    <style name="FindDialogTheme"> 
-        <item name="android:windowFrame">@null</item>
-        <item name="android:windowIsFloating">true</item>
-        <item name="android:windowIsTranslucent">true</item>
-        <item name="android:windowNoTitle">true</item>
-        <item name="android:background">@null</item>
-        <item name="android:windowBackground">@null</item>
-        <item name="android:windowAnimationStyle">@style/FindDialog</item>
-        <item name="android:backgroundDimEnabled">false</item>
-    </style>
-</resources>
diff --git a/src/com/android/browser/BrowserActivity.java b/src/com/android/browser/BrowserActivity.java
index 5e55789..8db4436 100644
--- a/src/com/android/browser/BrowserActivity.java
+++ b/src/com/android/browser/BrowserActivity.java
@@ -834,6 +834,13 @@
             if (mainView == null) {
                 return;
             }
+            // Do not need to check for null, since the current tab will have
+            // at least a main WebView, or we would have returned above.
+            if (dialogIsUp()) {
+                // Do not show the fake title bar, which would cover up the
+                // find or select dialog.
+                return;
+            }
 
             WindowManager manager
                     = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
@@ -1271,6 +1278,22 @@
         getTopWindow().requestFocus();
     }
 
+    private WebView showDialog(WebDialog dialog) {
+        // Need to do something special for Tablet
+        Tab tab = mTabControl.getCurrentTab();
+        if (tab.getSubWebView() == null) {
+            // If the find or select is being performed on the main webview,
+            // remove the embedded title bar.
+            WebView mainView = tab.getWebView();
+            if (mainView != null) {
+                mainView.setEmbeddedTitleBar(null);
+            }
+        }
+        hideFakeTitleBar();
+        mMenuState = EMPTY_MENU;
+        return tab.showDialog(dialog);
+    }
+
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
         if (!mCanChord) {
@@ -1364,18 +1387,13 @@
                 break;
 
             case R.id.find_menu_id:
-                if (null == mFindDialog) {
-                    mFindDialog = new FindDialog(this);
-                }
-                mFindDialog.setWebView(getTopWindow());
-                mFindDialog.show();
-                getTopWindow().setFindIsUp(true);
-                mMenuState = EMPTY_MENU;
+                showFindDialog();
                 break;
 
             case R.id.select_text_id:
-                getTopWindow().emulateShiftHeld();
+                showSelectDialog();
                 break;
+
             case R.id.page_info_menu_id:
                 showPageInfo(mTabControl.getCurrentTab(), false);
                 break;
@@ -1450,8 +1468,59 @@
         return true;
     }
 
-    public void closeFind() {
+    private boolean dialogIsUp() {
+        return null != mFindDialog && mFindDialog.isVisible() ||
+            null != mSelectDialog && mSelectDialog.isVisible();
+    }
+
+    private boolean closeDialog(WebDialog dialog) {
+        if (null == dialog || !dialog.isVisible()) return false;
+        Tab currentTab = mTabControl.getCurrentTab();
+        currentTab.closeDialog(dialog);
+        dialog.dismiss();
+        return true;
+    }
+
+    /*
+     * Remove the find dialog or select dialog.
+     */
+    public void closeDialogs() {
+        if (!(closeDialog(mFindDialog) || closeDialog(mSelectDialog))) return;
+        // If the Find was being performed in the main WebView, replace the
+        // embedded title bar.
+        Tab currentTab = mTabControl.getCurrentTab();
+        if (currentTab.getSubWebView() == null) {
+            WebView mainView = currentTab.getWebView();
+            if (mainView != null) {
+                mainView.setEmbeddedTitleBar(mTitleBar);
+            }
+        }
         mMenuState = R.id.MAIN_MENU;
+        if (mInLoad) {
+            // The title bar was hidden, because otherwise it would cover up the
+            // find or select dialog.  Now that the dialog has been removed,
+            // show the fake title bar once again.
+            showFakeTitleBar();
+        }
+    }
+
+    public void showFindDialog() {
+        if (null == mFindDialog) {
+            mFindDialog = new FindDialog(this);
+        }
+        showDialog(mFindDialog).setFindIsUp(true);
+    }
+
+    public void setFindDialogText(String text) {
+        mFindDialog.setText(text);
+    }
+
+    public void showSelectDialog() {
+        if (null == mSelectDialog) {
+            mSelectDialog = new SelectDialog(this);
+        }
+        showDialog(mSelectDialog).setUpSelect();
+        mSelectDialog.hideSoftInput();
     }
 
     @Override
@@ -2462,7 +2531,7 @@
         onProgressChanged(view, INITIAL_PROGRESS);
         mDidStopLoad = false;
         if (!mIsNetworkUp) createAndShowNetworkDialog();
-
+        closeDialogs();
         if (mSettings.isTracing()) {
             String host;
             try {
@@ -3770,6 +3839,7 @@
     private Menu mMenu;
 
     private FindDialog mFindDialog;
+    private SelectDialog mSelectDialog;
     // Used to prevent chording to result in firing two shortcuts immediately
     // one after another.  Fixes bug 1211714.
     boolean mCanChord;
diff --git a/src/com/android/browser/FindDialog.java b/src/com/android/browser/FindDialog.java
index 45c8016..726138e 100644
--- a/src/com/android/browser/FindDialog.java
+++ b/src/com/android/browser/FindDialog.java
@@ -16,27 +16,25 @@
 
 package com.android.browser;
 
-import android.app.Dialog;
 import android.content.Context;
-import android.os.Bundle;
 import android.text.Editable;
+import android.text.Selection;
 import android.text.Spannable;
 import android.text.TextWatcher;
 import android.view.Gravity;
 import android.view.KeyEvent;
+import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.Window;
-import android.view.WindowManager;
+import android.view.animation.AnimationUtils;
 import android.view.inputmethod.InputMethodManager;
 import android.webkit.WebView;
 import android.widget.EditText;
+import android.widget.LinearLayout;
 import android.widget.TextView;
 
-/* package */ class FindDialog extends Dialog implements TextWatcher {
-    private WebView         mWebView;
+/* package */ class FindDialog extends WebDialog implements TextWatcher {
     private TextView        mMatches;
-    private BrowserActivity mBrowserActivity;
     
     // Views with which the user can interact.
     private EditText        mEditText;
@@ -44,39 +42,30 @@
     private View            mPrevButton;
     private View            mMatchesView;
 
+    // When the dialog is opened up with old text, enter needs to be pressed
+    // (or the text needs to be changed) before WebView.findAll can be called.
+    // Once it has been called, enter should move to the next match.
+    private boolean         mMatchesFound;
+    private int             mNumberOfMatches;
+
     private View.OnClickListener mFindListener = new View.OnClickListener() {
         public void onClick(View v) {
             findNext();
         }
     };
 
-    private View.OnClickListener mFindCancelListener  = 
-            new View.OnClickListener() {
-        public void onClick(View v) {
-            dismiss();
-        }
-    };
-    
-    private View.OnClickListener mFindPreviousListener  = 
+    private View.OnClickListener mFindPreviousListener  =
             new View.OnClickListener() {
         public void onClick(View v) {
             if (mWebView == null) {
                 throw new AssertionError("No WebView for FindDialog::onClick");
             }
             mWebView.findNext(false);
+            updateMatchesString();
             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);
@@ -84,28 +73,13 @@
         mNextButton.setFocusable(false);
     }
 
-    /* package */ void setWebView(WebView webview) {
-        mWebView = webview;
-    }
-
     /* package */ FindDialog(BrowserActivity context) {
-        super(context, R.style.FindDialogTheme);
-        mBrowserActivity = context;
-        setCanceledOnTouchOutside(true);
-    }
+        super(context);
 
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
+        LayoutInflater factory = LayoutInflater.from(context);
+        factory.inflate(R.layout.browser_find, this);
 
-        Window theWindow = getWindow();
-        theWindow.setGravity(Gravity.BOTTOM|Gravity.FILL_HORIZONTAL);
-
-        setContentView(R.layout.browser_find);
-
-        theWindow.setLayout(ViewGroup.LayoutParams.MATCH_PARENT,
-                ViewGroup.LayoutParams.WRAP_CONTENT);
-
+        addCancel();
         mEditText = (EditText) findViewById(R.id.edit);
         
         View button = findViewById(R.id.next);
@@ -116,29 +90,59 @@
         button.setOnClickListener(mFindPreviousListener);
         mPrevButton = button;
         
-        button = findViewById(R.id.done);
-        button.setOnClickListener(mFindCancelListener);
-        
         mMatches = (TextView) findViewById(R.id.matches);
         mMatchesView = findViewById(R.id.matches_view);
         disableButtons();
-        theWindow.setSoftInputMode(
-                WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
+
     }
-    
+
+    /**
+     * Called by BrowserActivity.closeDialog.  Start the animation to hide
+     * the dialog, inform the WebView that the dialog is being dismissed,
+     * and hide the soft keyboard.
+     */
     public void dismiss() {
         super.dismiss();
-        mBrowserActivity.closeFind();
         mWebView.notifyFindDialogDismissed();
+        hideSoftInput();
+    }
+
+    @Override
+    public boolean dispatchKeyEventPreIme(KeyEvent event) {
+        if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
+            KeyEvent.DispatcherState state = getKeyDispatcherState();
+            if (state != null) {
+                int action = event.getAction();
+                if (KeyEvent.ACTION_DOWN == action
+                        && event.getRepeatCount() == 0) {
+                    state.startTracking(event, this);
+                    return true;
+                } else if (KeyEvent.ACTION_UP == action
+                        && !event.isCanceled() && state.isTracking(event)) {
+                    mBrowserActivity.closeDialogs();
+                    return true;
+                }
+            }
+        }
+        return super.dispatchKeyEventPreIme(event);
     }
 
     @Override
     public boolean dispatchKeyEvent(KeyEvent event) {
-        if (event.getKeyCode() == KeyEvent.KEYCODE_ENTER
-                && event.getAction() == KeyEvent.ACTION_UP
-                && mEditText.hasFocus()) {
-            findNext();
-            return true;
+        int keyCode = event.getKeyCode();
+        if (event.getAction() == KeyEvent.ACTION_UP) {
+            if (keyCode == KeyEvent.KEYCODE_ENTER
+                    && mEditText.hasFocus()) {
+                if (mMatchesFound) {
+                    findNext();
+                } else {
+                    findAll();
+                    // Set the selection to the end.
+                    Spannable span = (Spannable) mEditText.getText();
+                    Selection.setSelection(span, span.length());
+                }
+                return true;
+            }
         }
         return super.dispatchKeyEvent(event);
     }
@@ -148,18 +152,26 @@
             throw new AssertionError("No WebView for FindDialog::findNext");
         }
         mWebView.findNext(true);
+        updateMatchesString();
         hideSoftInput();
     }
 
     public void show() {
         super.show();
+        // In case the matches view is showing from a previous search
+        mMatchesView.setVisibility(View.INVISIBLE);
+        mMatchesFound = false;
+        // This text is only here to ensure that mMatches has a height.
+        mMatches.setText("0");
         mEditText.requestFocus();
-        mEditText.setText("");
         Spannable span = (Spannable) mEditText.getText();
-        span.setSpan(this, 0, span.length(), 
-                     Spannable.SPAN_INCLUSIVE_INCLUSIVE);
-        setMatchesFound(0);
+        int length = span.length();
+        Selection.setSelection(span, 0, length);
+        span.setSpan(this, 0, length, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
         disableButtons();
+        InputMethodManager imm = (InputMethodManager)
+                mBrowserActivity.getSystemService(Context.INPUT_METHOD_SERVICE);
+        imm.showSoftInput(mEditText, 0);
     }
     
     // TextWatcher methods
@@ -173,9 +185,13 @@
                               int start, 
                               int before, 
                               int count) {
+        findAll();
+    }
+
+    private void findAll() {
         if (mWebView == null) {
             throw new AssertionError(
-                    "No WebView for FindDialog::onTextChanged");
+                    "No WebView for FindDialog::findAll");
         }
         CharSequence find = mEditText.getText();
         if (0 == find.length()) {
@@ -184,14 +200,18 @@
             mMatchesView.setVisibility(View.INVISIBLE);
         } else {
             mMatchesView.setVisibility(View.VISIBLE);
-            mWebView.setFindDialogHeight(
-                getWindow().getDecorView().getHeight());
             int found = mWebView.findAll(find.toString());
+            mMatchesFound = true;
             setMatchesFound(found);
             if (found < 2) {
                 disableButtons();
                 if (found == 0) {
-                    setMatchesFound(0);
+                    // Cannot use getQuantityString, which ignores the "zero"
+                    // quantity.
+                    // FIXME: is this fix is beyond the scope
+                    // of adding touch selection to gingerbread?
+                    // mMatches.setText(mBrowserActivity.getResources().getString(
+                    //        R.string.no_matches));
                 }
             } else {
                 mPrevButton.setFocusable(true);
@@ -203,8 +223,21 @@
     }
 
     private void setMatchesFound(int found) {
+        mNumberOfMatches = found;
+        updateMatchesString();
+    }
+
+    public void setText(String text) {
+        mEditText.setText(text);
+        findAll();
+    }
+
+    private void updateMatchesString() {
+        // Note: updateMatchesString is only called by methods that have already
+        // checked mWebView for null.
         String template = mBrowserActivity.getResources().
-                getQuantityString(R.plurals.matches_found, found, found);
+                getQuantityString(R.plurals.matches_found, mNumberOfMatches,
+                mWebView.findIndex() + 1, mNumberOfMatches);
 
         mMatches.setText(template);
     }
diff --git a/src/com/android/browser/SelectDialog.java b/src/com/android/browser/SelectDialog.java
new file mode 100644
index 0000000..461127a
--- /dev/null
+++ b/src/com/android/browser/SelectDialog.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2010 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.provider.Browser;
+import android.view.LayoutInflater;
+import android.view.View;
+
+/* package */ class SelectDialog extends WebDialog {
+    private View mCopyButton;
+    private View mSelectAllButton;
+    private View mShareButton;
+    private View mFindButton;
+
+    SelectDialog(BrowserActivity context) {
+        super(context);
+        LayoutInflater factory = LayoutInflater.from(context);
+        factory.inflate(R.layout.browser_select, this);
+        addCancel();
+
+        mCopyButton = findViewById(R.id.copy);
+        mCopyButton.setOnClickListener(mCopyListener);
+        mSelectAllButton = findViewById(R.id.select_all);
+        mSelectAllButton.setOnClickListener(mSelectAllListener);
+        mShareButton = findViewById(R.id.share);
+        mShareButton.setOnClickListener(mShareListener);
+        mFindButton = findViewById(R.id.find);
+        mFindButton.setOnClickListener(mFindListener);
+    }
+
+    private View.OnClickListener mCopyListener = new View.OnClickListener() {
+        public void onClick(View v) {
+            mWebView.copySelection();
+            mBrowserActivity.closeDialogs();
+        }
+    };
+
+    private View.OnClickListener mSelectAllListener = new View.OnClickListener() {
+        public void onClick(View v) {
+            mWebView.selectAll();
+        }
+    };
+
+    private View.OnClickListener mShareListener = new View.OnClickListener() {
+        public void onClick(View v) {
+            String selection = mWebView.getSelection();
+            Browser.sendString(mBrowserActivity, selection);
+            mBrowserActivity.closeDialogs();
+        }
+    };
+
+    private View.OnClickListener mFindListener = new View.OnClickListener() {
+        public void onClick(View v) {
+            String selection = mWebView.getSelection();
+            mBrowserActivity.closeDialogs();
+            mBrowserActivity.showFindDialog();
+            mBrowserActivity.setFindDialogText(selection);
+        }
+    };
+
+    /**
+     * Called by BrowserActivity.closeDialog.  Start the animation to hide
+     * the dialog, and inform the WebView that the dialog is being dismissed.
+     */
+    @Override
+    public void dismiss() {
+        super.dismiss();
+        mWebView.notifySelectDialogDismissed();
+    }
+
+}
diff --git a/src/com/android/browser/Tab.java b/src/com/android/browser/Tab.java
index 5795b05..167c7b3 100644
--- a/src/com/android/browser/Tab.java
+++ b/src/com/android/browser/Tab.java
@@ -87,7 +87,7 @@
     // The Geolocation permissions prompt
     private GeolocationPermissionsPrompt mGeolocationPermissionsPrompt;
     // Main WebView wrapper
-    private View mContainer;
+    private LinearLayout mContainer;
     // Main WebView
     private WebView mMainView;
     // Subwindow container
@@ -1041,6 +1041,16 @@
         }
 
         @Override
+        public void onSelectionDone(WebView view) {
+            if (mInForeground) mActivity.closeDialogs();
+        }
+
+        @Override
+        public void onSelectionStart(WebView view) {
+            if (mInForeground) mActivity.showSelectDialog();
+        }
+
+        @Override
         public void onShowCustomView(View view,
                 WebChromeClient.CustomViewCallback callback) {
             if (mInForeground) mActivity.onShowCustomView(view, callback);
@@ -1220,9 +1230,18 @@
     private static class SubWindowClient extends WebViewClient {
         // The main WebViewClient.
         private final WebViewClient mClient;
+        private final BrowserActivity mBrowserActivity;
 
-        SubWindowClient(WebViewClient client) {
+        SubWindowClient(WebViewClient client, BrowserActivity activity) {
             mClient = client;
+            mBrowserActivity = activity;
+        }
+        @Override
+        public void onPageStarted(WebView view, String url, Bitmap favicon) {
+            // Unlike the others, do not call mClient's version, which would
+            // change the progress bar.  However, we do want to remove the
+            // find or select dialog.
+            mBrowserActivity.closeDialogs();
         }
         @Override
         public void doUpdateVisitedHistory(WebView view, String url,
@@ -1312,7 +1331,7 @@
 
         // The tab consists of a container view, which contains the main
         // WebView, as well as any other UI elements associated with the tab.
-        mContainer = mInflateService.inflate(R.layout.tab, null);
+        mContainer = (LinearLayout) mInflateService.inflate(R.layout.tab, null);
 
         mDownloadListener = new DownloadListener() {
             public void onDownloadStart(String url, String userAgent,
@@ -1423,6 +1442,7 @@
      */
     boolean createSubWindow() {
         if (mSubView == null) {
+            mActivity.closeDialogs();
             mSubViewContainer = mInflateService.inflate(
                     R.layout.browser_subwindow, null);
             mSubView = (WebView) mSubViewContainer.findViewById(R.id.webview);
@@ -1431,7 +1451,8 @@
             mSubView.setMapTrackballToArrowKeys(false);
             // Enable the built-in zoom
             mSubView.getSettings().setBuiltInZoomControls(true);
-            mSubView.setWebViewClient(new SubWindowClient(mWebViewClient));
+            mSubView.setWebViewClient(new SubWindowClient(mWebViewClient,
+                    mActivity));
             mSubView.setWebChromeClient(new SubWindowChromeClient(
                     mWebChromeClient));
             // Set a different DownloadListener for the mSubView, since it will
@@ -1469,6 +1490,7 @@
      */
     void dismissSubWindow() {
         if (mSubView != null) {
+            mActivity.closeDialogs();
             BrowserSettings.getInstance().deleteObserver(
                     mSubView.getSettings());
             mSubView.destroy();
@@ -1493,6 +1515,7 @@
     void removeSubWindow(ViewGroup content) {
         if (mSubView != null) {
             content.removeView(mSubViewContainer);
+            mActivity.closeDialogs();
         }
     }
 
@@ -1551,6 +1574,7 @@
                 (FrameLayout) mContainer.findViewById(R.id.webview_wrapper);
         wrapper.removeView(mMainView);
         content.removeView(mContainer);
+        mActivity.closeDialogs();
         removeSubWindow(content);
     }
 
@@ -1946,4 +1970,36 @@
         }
         return true;
     }
+
+    /*
+     * Opens the find and select text dialogs.  Called by BrowserActivity.
+     */
+    WebView showDialog(WebDialog dialog) {
+        LinearLayout container;
+        WebView view;
+        if (mSubView != null) {
+            view = mSubView;
+            container = (LinearLayout) mSubViewContainer.findViewById(
+                    R.id.inner_container);
+        } else {
+            view = mMainView;
+            container = mContainer;
+        }
+        dialog.show();
+        container.addView(dialog, 0, new LinearLayout.LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT,
+                ViewGroup.LayoutParams.WRAP_CONTENT));
+        dialog.setWebView(view);
+        return view;
+    }
+
+    /*
+     * Close the find or select dialog. Called by BrowserActivity.closeDialog.
+     */
+    void closeDialog(WebDialog dialog) {
+        // The dialog may be attached to the subwindow.  Ensure that the
+        // correct parent has it removed.
+        LinearLayout parent = (LinearLayout) dialog.getParent();
+        if (parent != null) parent.removeView(dialog);
+    }
 }
diff --git a/src/com/android/browser/WebDialog.java b/src/com/android/browser/WebDialog.java
new file mode 100644
index 0000000..9995e8f
--- /dev/null
+++ b/src/com/android/browser/WebDialog.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2010 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.content.Context;
+import android.view.View;
+import android.view.animation.AnimationUtils;
+import android.view.inputmethod.InputMethodManager;
+import android.webkit.WebView;
+import android.widget.LinearLayout;
+
+/* package */ class WebDialog extends LinearLayout {
+    protected WebView         mWebView;
+    protected BrowserActivity mBrowserActivity;
+    private boolean           mIsVisible;
+
+    /* package */ WebDialog(BrowserActivity context) {
+        super(context);
+        mBrowserActivity = context;
+    }
+
+    /* dialogs that have cancel buttons can optionally share code by including a
+     * view with an id of 'done'.
+     */
+    protected void addCancel() {
+        View button = findViewById(R.id.done);
+        if (button != null) button.setOnClickListener(mCancelListener);
+    }
+
+    private View.OnClickListener mCancelListener = new View.OnClickListener() {
+        public void onClick(View v) {
+            mBrowserActivity.closeDialogs();
+        }
+    };
+
+    protected void dismiss() {
+        startAnimation(AnimationUtils.loadAnimation(mBrowserActivity,
+                R.anim.dialog_exit));
+        mIsVisible = false;
+    }
+
+    /*
+     * Remove the soft keyboard from the screen.
+     */
+    protected void hideSoftInput() {
+        InputMethodManager imm = (InputMethodManager)
+                mBrowserActivity.getSystemService(Context.INPUT_METHOD_SERVICE);
+        imm.hideSoftInputFromWindow(mWebView.getWindowToken(), 0);
+    }
+
+    protected boolean isVisible() {
+        return mIsVisible;
+    }
+
+    /* package */ void setWebView(WebView webview) {
+        mWebView = webview;
+    }
+
+    protected void show() {
+        startAnimation(AnimationUtils.loadAnimation(mBrowserActivity,
+            R.anim.dialog_enter));
+        mIsVisible = true;
+    }
+
+}