diff options
author | Leon Scroggins III <scroggo@google.com> | 2010-08-17 16:58:15 -0400 |
---|---|---|
committer | Leon Scroggins III <scroggo@google.com> | 2010-08-18 10:36:12 -0400 |
commit | 8e4fbf1b26c2cc05a56ba2d4e7d1eda7d1574e91 (patch) | |
tree | 2692061cd11645619483d651d8d17f18a9accb81 | |
parent | f9f87cde1af2a93b14a9ea0045f7b1c905a1a8f4 (diff) | |
download | packages_apps_Browser-8e4fbf1b26c2cc05a56ba2d4e7d1eda7d1574e91.zip packages_apps_Browser-8e4fbf1b26c2cc05a56ba2d4e7d1eda7d1574e91.tar.gz packages_apps_Browser-8e4fbf1b26c2cc05a56ba2d4e7d1eda7d1574e91.tar.bz2 |
Use ActionMode for Find-on-page and Copy.
Bug: 2641352
Change-Id: Ib5c0dd5997457a8d7b9a5c3e5a3727acc6a2f367
-rw-r--r-- | res/anim/dialog_exit.xml | 22 | ||||
-rw-r--r-- | res/layout/browser_find.xml | 68 | ||||
-rw-r--r-- | res/layout/browser_select.xml | 67 | ||||
-rw-r--r-- | res/menu/copy.xml | 35 | ||||
-rw-r--r-- | res/menu/find.xml (renamed from res/anim/dialog_enter.xml) | 17 | ||||
-rw-r--r-- | res/values/strings.xml | 3 | ||||
-rw-r--r-- | src/com/android/browser/BrowserActivity.java | 117 | ||||
-rw-r--r-- | src/com/android/browser/FindActionModeCallback.java | 219 | ||||
-rw-r--r-- | src/com/android/browser/FindDialog.java | 245 | ||||
-rw-r--r-- | src/com/android/browser/SelectActionModeCallback.java | 89 | ||||
-rw-r--r-- | src/com/android/browser/SelectDialog.java | 85 | ||||
-rw-r--r-- | src/com/android/browser/Tab.java | 44 | ||||
-rw-r--r-- | src/com/android/browser/WebDialog.java | 79 |
13 files changed, 451 insertions, 639 deletions
diff --git a/res/anim/dialog_exit.xml b/res/anim/dialog_exit.xml deleted file mode 100644 index 9845849..0000000 --- a/res/anim/dialog_exit.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2007 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. ---> - -<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" /> -</set> - diff --git a/res/layout/browser_find.xml b/res/layout/browser_find.xml index e2f0cf6..bb7a36f 100644 --- a/res/layout/browser_find.xml +++ b/res/layout/browser_find.xml @@ -4,9 +4,9 @@ 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. @@ -15,62 +15,26 @@ --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/findControls" 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_find_prev" - android:id="@+id/previous" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - /> - - <ImageButton - android:src="@drawable/ic_btn_find_next" - android:id="@+id/next" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - /> - - <LinearLayout - android:layout_height="wrap_content" + android:orientation="horizontal" + > + <EditText android:id="@+id/edit" android:layout_width="0dip" android:layout_weight="1" - android:orientation="vertical" - android:layout_marginRight="6dip" - > - <EditText android:id="@+id/edit" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:scrollHorizontally="true" - android:inputType="text" - android:hint="@string/find_dot" - /> - <LinearLayout android:id="@+id/matches_view" - android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:orientation="horizontal" - android:layout_gravity="right" - android:visibility="invisible" - > - <TextView android:id="@+id/matches" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="right" - android:textAppearance="?android:attr/textAppearanceSmall" - android:textColor="?android:attr/textColorSecondaryInverse" - /> - </LinearLayout> - </LinearLayout> - <ImageButton android:id="@+id/done" + android:layout_height="wrap_content" + android:scrollHorizontally="true" + android:inputType="text" + android:hint="@string/find_dot" + android:imeOptions="actionDone" + android:layout_marginRight="10dip" + /> + <TextView android:id="@+id/matches" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:src="@drawable/ic_btn_close_panel" + android:layout_gravity="center_vertical" + android:textAppearance="?android:attr/textAppearanceSmall" + android:textColor="?android:attr/textColorSecondaryInverse" /> </LinearLayout> diff --git a/res/layout/browser_select.xml b/res/layout/browser_select.xml deleted file mode 100644 index b30be8d..0000000 --- a/res/layout/browser_select.xml +++ /dev/null @@ -1,67 +0,0 @@ -<?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/menu/copy.xml b/res/menu/copy.xml new file mode 100644 index 0000000..018be25 --- /dev/null +++ b/res/menu/copy.xml @@ -0,0 +1,35 @@ +<?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. +--> + +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+id/copy" + android:icon="@drawable/ic_btn_copy" + android:showAsAction="always" + /> + <item android:id="@+id/share" + android:icon="@drawable/ic_btn_share" + android:showAsAction="always" + /> + <item android:id="@+id/select_all" + android:icon="@drawable/ic_btn_select_all" + android:showAsAction="always" + /> + <item android:id="@+id/find" + android:icon="@drawable/ic_btn_find" + android:showAsAction="always" + /> +</menu> + diff --git a/res/anim/dialog_enter.xml b/res/menu/find.xml index 6fbcb9e..74a40aa 100644 --- a/res/anim/dialog_enter.xml +++ b/res/menu/find.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2007 The Android Open Source Project +<!-- 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. @@ -14,8 +14,13 @@ limitations under the License. --> -<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" /> -</set> +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+id/find_prev" + android:icon="@drawable/ic_btn_find_prev" + android:showAsAction="always" + /> + <item android:id="@+id/find_next" + android:icon="@drawable/ic_btn_find_next" + android:showAsAction="always" + /> +</menu> diff --git a/res/values/strings.xml b/res/values/strings.xml index 68fcc52..00e2008 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -46,7 +46,8 @@ <string name="action">Sign in</string> <!-- The name of the bookmarks and history search suggestion source. --> <string name="bookmarks_search">Browser</string> - + <!-- Title for text selection contextual mode, displayed in the contextual action bar [CHAR LIMIT=NONE]--> + <string name="text_selection_title">Text selection</string> <!-- Label for a cancel button. It is used for multiple cancel buttons in different contexts --> <string name="cancel">Cancel</string> <!-- Label for a confirm button. Used in multiple contexts. --> diff --git a/src/com/android/browser/BrowserActivity.java b/src/com/android/browser/BrowserActivity.java index a2c2f32..36be5e4 100644 --- a/src/com/android/browser/BrowserActivity.java +++ b/src/com/android/browser/BrowserActivity.java @@ -71,6 +71,7 @@ import android.text.TextUtils; import android.text.format.DateFormat; import android.util.Log; import android.util.Patterns; +import android.view.ActionMode; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; import android.view.Gravity; @@ -862,9 +863,9 @@ public class BrowserActivity extends Activity } // 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. + if (isInCustomActionMode()) { + // Do not show the fake title bar, while a custom ActionMode + // (i.e. find or select) is showing. return; } if (mXLargeScreenSize) { @@ -1322,20 +1323,30 @@ public class BrowserActivity extends Activity 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); + @Override + public ActionMode onStartActionMode(ActionMode.Callback callback) { + ActionMode mode = super.onStartActionMode(callback); + if (callback instanceof FindActionModeCallback + || callback instanceof SelectActionModeCallback) { + // For find and select, hide extra title bars. They will + // be replaced in onEndActionMode. + 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(); + mActionMode = mode; + } else { + // Do not store other ActionModes, since we are unable to determine + // when they finish. + mActionMode = null; } - hideFakeTitleBar(); - mMenuState = EMPTY_MENU; - return tab.showDialog(dialog); + return mode; } @Override @@ -1431,7 +1442,7 @@ public class BrowserActivity extends Activity break; case R.id.find_menu_id: - showFindDialog(); + showFindDialog(null); break; case R.id.save_webarchive_menu_id: @@ -1539,24 +1550,28 @@ public class BrowserActivity extends Activity startActivity(i); } - private boolean dialogIsUp() { - return null != mFindDialog && mFindDialog.isVisible() || - null != mSelectDialog && mSelectDialog.isVisible(); + /* + * True if a custom ActionMode (i.e. find or select) is in use. + */ + private boolean isInCustomActionMode() { + return mActionMode != null; } - private boolean closeDialog(WebDialog dialog) { - if (null == dialog || !dialog.isVisible()) return false; - Tab currentTab = mTabControl.getCurrentTab(); - currentTab.closeDialog(dialog); - dialog.dismiss(); - return true; + /* + * End the current ActionMode. Only works for find and select. + */ + void endActionMode() { + if (mActionMode != null) { + mActionMode.finish(); + } } /* - * Remove the find dialog or select dialog. + * Called by find and select when they are finished. Replace title bars + * as necessary. */ - public void closeDialogs() { - if (!(closeDialog(mFindDialog) || closeDialog(mSelectDialog))) return; + public void onEndActionMode() { + if (!isInCustomActionMode()) return; // If the Find was being performed in the main WebView, replace the // embedded title bar. Tab currentTab = mTabControl.getCurrentTab(); @@ -1566,32 +1581,48 @@ public class BrowserActivity extends Activity 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(); } + mActionMode = null; } - public void showFindDialog() { - if (null == mFindDialog) { - mFindDialog = new FindDialog(this); - } - showDialog(mFindDialog).setFindIsUp(true); - } + private FindActionModeCallback mFindCallback; + private SelectActionModeCallback mSelectCallback; - public void setFindDialogText(String text) { - mFindDialog.setText(text); + // For select and find, we keep track of the ActionMode so that + // finish() can be called as desired. + private ActionMode mActionMode; + + /* + * Open the find ActionMode. + * @param text If non null, will be placed in find to be searched for. + */ + public void showFindDialog(String text) { + if (null == mFindCallback) { + mFindCallback = new FindActionModeCallback(this); + } + WebView webView = getTopWindow(); + webView.setFindIsUp(true); + mFindCallback.setWebView(webView); + startActionMode(mFindCallback); + if (text != null) mFindCallback.setText(text); } + /* + * Show the select ActionMode. + */ public void showSelectDialog() { - if (null == mSelectDialog) { - mSelectDialog = new SelectDialog(this); + if (null == mSelectCallback) { + mSelectCallback = new SelectActionModeCallback(this); } - showDialog(mSelectDialog).setUpSelect(); - mSelectDialog.hideSoftInput(); + WebView webView = getTopWindow(); + webView.setUpSelect(); + mSelectCallback.setWebView(webView); + startActionMode(mSelectCallback); } @Override @@ -2601,7 +2632,7 @@ public class BrowserActivity extends Activity onProgressChanged(view, INITIAL_PROGRESS); mDidStopLoad = false; if (!mIsNetworkUp) createAndShowNetworkDialog(); - closeDialogs(); + endActionMode(); if (mSettings.isTracing()) { String host; try { @@ -4158,8 +4189,6 @@ public class BrowserActivity extends Activity private static final int EMPTY_MENU = -1; 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/FindActionModeCallback.java b/src/com/android/browser/FindActionModeCallback.java new file mode 100644 index 0000000..0b0fdae --- /dev/null +++ b/src/com/android/browser/FindActionModeCallback.java @@ -0,0 +1,219 @@ +/* + * 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.content.res.Resources; +import android.text.Editable; +import android.text.Selection; +import android.text.Spannable; +import android.text.TextWatcher; +import android.webkit.WebView; +import android.widget.EditText; +import android.widget.TextView; +import android.view.ActionMode; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.inputmethod.InputMethodManager; + +class FindActionModeCallback implements ActionMode.Callback, TextWatcher, + View.OnLongClickListener { + private View mCustomView; + private EditText mEditText; + private TextView mMatches; + private WebView mWebView; + private InputMethodManager mInput; + private Resources mResources; + private boolean mMatchesFound; + private int mNumberOfMatches; + private BrowserActivity mBrowserActivity; + + FindActionModeCallback(BrowserActivity context) { + mCustomView = LayoutInflater.from(context).inflate( + R.layout.browser_find, null); + mEditText = (EditText) mCustomView.findViewById(R.id.edit); + // Override long click so that select ActionMode is not opened, which + // would exit find ActionMode. + mEditText.setOnLongClickListener(this); + Spannable span = (Spannable) mEditText.getText(); + int length = span.length(); + span.setSpan(this, 0, length, Spannable.SPAN_INCLUSIVE_INCLUSIVE); + mMatches = (TextView) mCustomView.findViewById(R.id.matches); + mInput = (InputMethodManager) + context.getSystemService(Context.INPUT_METHOD_SERVICE); + mResources = context.getResources(); + mBrowserActivity = context; + } + + /* + * Place text in the text field so it can be searched for. Need to press + * the find next or find previous button to find all of the matches. + */ + void setText(String text) { + mEditText.setText(text); + Spannable span = (Spannable) mEditText.getText(); + int length = span.length(); + // Ideally, we would like to set the selection to the whole field, + // but this brings up the Text selection CAB, which dismisses this + // one. + Selection.setSelection(span, length, length); + // Necessary each time we set the text, so that this will watch + // changes to it. + span.setSpan(this, 0, length, Spannable.SPAN_INCLUSIVE_INCLUSIVE); + mMatchesFound = false; + } + + /* + * Set the WebView to search. Must be non null, and set before calling + * startActionMode. + */ + void setWebView(WebView webView) { + if (null == webView) { + throw new AssertionError("WebView supplied to " + + "FindActionModeCallback cannot be null"); + } + mWebView = webView; + } + + /* + * Move the highlight to the next match. + * @param next If true, find the next match further down in the document. + * If false, find the previous match, up in the document. + */ + private void findNext(boolean next) { + if (mWebView == null) { + throw new AssertionError( + "No WebView for FindActionModeCallback::findNext"); + } + mWebView.findNext(next); + } + + /* + * Highlight all the instances of the string from mEditText in mWebView. + */ + private void findAll() { + if (mWebView == null) { + throw new AssertionError( + "No WebView for FindActionModeCallback::findAll"); + } + CharSequence find = mEditText.getText(); + if (0 == find.length()) { + mWebView.clearMatches(); + mMatches.setVisibility(View.INVISIBLE); + mMatchesFound = false; + } else { + mMatchesFound = true; + mMatches.setVisibility(View.VISIBLE); + mNumberOfMatches = mWebView.findAll(find.toString()); + if (0 == mNumberOfMatches) { + mMatches.setText(mResources.getString(R.string.no_matches)); + } else { + updateMatchesString(); + } + } + } + + /* + * Update the string which tells the user how many matches were found, and + * which match is currently highlighted. + */ + private void updateMatchesString() { + String template = mResources.getQuantityString(R.plurals.matches_found, + mNumberOfMatches, mWebView.findIndex() + 1, mNumberOfMatches); + + mMatches.setText(template); + } + + // OnLongClickListener implementation + + @Override + public boolean onLongClick(View v) { return true; } + + // ActionMode.Callback implementation + + @Override + public boolean onCreateActionMode(ActionMode mode, Menu menu) { + mode.setCustomView(mCustomView); + mode.getMenuInflater().inflate(R.menu.find, menu); + // Ideally, we would like to preserve the old find text, but it + // brings up the Text selection CAB, and therefore dismisses + // find + setText(""); + mMatches.setVisibility(View.INVISIBLE); + mMatchesFound = false; + mMatches.setText("0"); + mEditText.requestFocus(); + mInput.showSoftInput(mEditText, 0); + return true; + } + + @Override + public void onDestroyActionMode(ActionMode mode) { + mBrowserActivity.onEndActionMode(); + mWebView.notifyFindDialogDismissed(); + mInput.hideSoftInputFromWindow(mWebView.getWindowToken(), 0); + } + + @Override + public boolean onPrepareActionMode(ActionMode mode, Menu menu) { + return false; + } + + @Override + public boolean onActionItemClicked(ActionMode mode, MenuItem item) { + if (!mMatchesFound) { + findAll(); + return true; + } + switch(item.getItemId()) { + case R.id.find_prev: + findNext(false); + break; + case R.id.find_next: + findNext(true); + break; + default: + return false; + } + updateMatchesString(); + return true; + } + + // TextWatcher methods + + @Override + public void beforeTextChanged(CharSequence s, + int start, + int count, + int after) { + } + + @Override + public void onTextChanged(CharSequence s, + int start, + int before, + int count) { + findAll(); + } + + @Override + public void afterTextChanged(Editable s) { } + +} diff --git a/src/com/android/browser/FindDialog.java b/src/com/android/browser/FindDialog.java deleted file mode 100644 index 9d0ac4b..0000000 --- a/src/com/android/browser/FindDialog.java +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright (C) 2007 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.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.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 WebDialog implements TextWatcher { - private TextView mMatches; - - // Views with which the user can interact. - private EditText mEditText; - private View mNextButton; - 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 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(); - } - }; - - private void disableButtons() { - mPrevButton.setEnabled(false); - mNextButton.setEnabled(false); - mPrevButton.setFocusable(false); - mNextButton.setFocusable(false); - } - - /* package */ FindDialog(BrowserActivity context) { - super(context); - - LayoutInflater factory = LayoutInflater.from(context); - factory.inflate(R.layout.browser_find, this); - - addCancel(); - mEditText = (EditText) findViewById(R.id.edit); - - View button = findViewById(R.id.next); - button.setOnClickListener(mFindListener); - mNextButton = button; - - button = findViewById(R.id.previous); - button.setOnClickListener(mFindPreviousListener); - mPrevButton = button; - - mMatches = (TextView) findViewById(R.id.matches); - mMatchesView = findViewById(R.id.matches_view); - disableButtons(); - - } - - /** - * 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(); - 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) { - 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); - } - - private void findNext() { - if (mWebView == null) { - 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(); - Spannable span = (Spannable) mEditText.getText(); - 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 - public void beforeTextChanged(CharSequence s, - int start, - int count, - int after) { - } - - public void onTextChanged(CharSequence s, - int start, - int before, - int count) { - findAll(); - } - - private void findAll() { - if (mWebView == null) { - throw new AssertionError( - "No WebView for FindDialog::findAll"); - } - CharSequence find = mEditText.getText(); - if (0 == find.length()) { - disableButtons(); - mWebView.clearMatches(); - mMatchesView.setVisibility(View.INVISIBLE); - } else { - mMatchesView.setVisibility(View.VISIBLE); - int found = mWebView.findAll(find.toString()); - mMatchesFound = true; - setMatchesFound(found); - if (found < 2) { - disableButtons(); - if (found == 0) { - // Cannot use getQuantityString, which ignores the "zero" - // quantity. - mMatches.setText(mBrowserActivity.getResources().getString( - R.string.no_matches)); - } - } else { - mPrevButton.setFocusable(true); - mNextButton.setFocusable(true); - mPrevButton.setEnabled(true); - mNextButton.setEnabled(true); - } - } - } - - 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, mNumberOfMatches, - mWebView.findIndex() + 1, mNumberOfMatches); - - mMatches.setText(template); - } - - public void afterTextChanged(Editable s) { - } -} diff --git a/src/com/android/browser/SelectActionModeCallback.java b/src/com/android/browser/SelectActionModeCallback.java new file mode 100644 index 0000000..4c2554f --- /dev/null +++ b/src/com/android/browser/SelectActionModeCallback.java @@ -0,0 +1,89 @@ +/* + * 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.webkit.WebView; +import android.view.ActionMode; +import android.view.Menu; +import android.view.MenuItem; + +class SelectActionModeCallback implements ActionMode.Callback { + private WebView mWebView; + private BrowserActivity mBrowserActivity; + + SelectActionModeCallback(BrowserActivity context) { + mBrowserActivity = context; + } + + /* + * Set the WebView to be copied from. + */ + void setWebView(WebView webView) { + mWebView = webView; + } + + // ActionMode.Callback implementation + + @Override + public boolean onCreateActionMode(ActionMode mode, Menu menu) { + mode.getMenuInflater().inflate(R.menu.copy, menu); + mode.setTitle(R.string.text_selection_title); + return true; + } + + @Override + public boolean onPrepareActionMode(ActionMode mode, Menu menu) { + return true; + } + + @Override + public boolean onActionItemClicked(ActionMode mode, MenuItem item) { + switch(item.getItemId()) { + case R.id.copy: + mWebView.copySelection(); + mode.finish(); + break; + + case R.id.share: + String selection = mWebView.getSelection(); + Browser.sendString(mBrowserActivity, selection); + mode.finish(); + break; + + case R.id.select_all: + mWebView.selectAll(); + break; + + case R.id.find: + String sel= mWebView.getSelection(); + mode.finish(); + mBrowserActivity.showFindDialog(sel); + break; + + default: + return false; + } + return true; + } + + @Override + public void onDestroyActionMode(ActionMode mode) { + mBrowserActivity.onEndActionMode(); + mWebView.notifySelectDialogDismissed(); + } +} diff --git a/src/com/android/browser/SelectDialog.java b/src/com/android/browser/SelectDialog.java deleted file mode 100644 index 461127a..0000000 --- a/src/com/android/browser/SelectDialog.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * 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 b45b8cb..9abf32f 100644 --- a/src/com/android/browser/Tab.java +++ b/src/com/android/browser/Tab.java @@ -1060,7 +1060,7 @@ class Tab { @Override public void onSelectionDone(WebView view) { - if (mInForeground) mActivity.closeDialogs(); + if (mInForeground) mActivity.endActionMode(); } @Override @@ -1261,7 +1261,7 @@ class Tab { // 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(); + mBrowserActivity.endActionMode(); } @Override public void doUpdateVisitedHistory(WebView view, String url, @@ -1462,7 +1462,7 @@ class Tab { */ boolean createSubWindow() { if (mSubView == null) { - mActivity.closeDialogs(); + mActivity.endActionMode(); mSubViewContainer = mInflateService.inflate( R.layout.browser_subwindow, null); mSubView = (WebView) mSubViewContainer.findViewById(R.id.webview); @@ -1510,7 +1510,7 @@ class Tab { */ void dismissSubWindow() { if (mSubView != null) { - mActivity.closeDialogs(); + mActivity.endActionMode(); BrowserSettings.getInstance().deleteObserver( mSubView.getSettings()); mSubView.destroy(); @@ -1535,7 +1535,7 @@ class Tab { void removeSubWindow(ViewGroup content) { if (mSubView != null) { content.removeView(mSubViewContainer); - mActivity.closeDialogs(); + mActivity.endActionMode(); } } @@ -1594,7 +1594,7 @@ class Tab { (FrameLayout) mContainer.findViewById(R.id.webview_wrapper); wrapper.removeView(mMainView); content.removeView(mContainer); - mActivity.closeDialogs(); + mActivity.endActionMode(); removeSubWindow(content); } @@ -1980,38 +1980,6 @@ class Tab { 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); - } - /** * always get the TabChangeListener form the tab control * @return the TabControl change listener diff --git a/src/com/android/browser/WebDialog.java b/src/com/android/browser/WebDialog.java deleted file mode 100644 index 9995e8f..0000000 --- a/src/com/android/browser/WebDialog.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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; - } - -} |