blob: f3138935051954b4310878c4664ff14a11b4e7d0 [file] [log] [blame]
Leon Scrogginsfe026bd2010-08-24 14:16:09 -04001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.webkit;
18
Ignacio Solla451e3382014-11-10 10:35:54 +000019import android.annotation.SystemApi;
Leon Scrogginsfe026bd2010-08-24 14:16:09 -040020import android.content.Context;
21import android.content.res.Resources;
John Reckd6ac7272011-11-08 15:00:17 -080022import android.graphics.Point;
23import android.graphics.Rect;
Leon Scrogginsfe026bd2010-08-24 14:16:09 -040024import android.text.Editable;
25import android.text.Selection;
26import android.text.Spannable;
27import android.text.TextWatcher;
Leon Scrogginsfe026bd2010-08-24 14:16:09 -040028import android.view.ActionMode;
29import android.view.LayoutInflater;
30import android.view.Menu;
Leon Scrogginsfe026bd2010-08-24 14:16:09 -040031import android.view.MenuItem;
32import android.view.View;
33import android.view.inputmethod.InputMethodManager;
John Reckfffce6f2011-10-06 20:37:01 -070034import android.widget.EditText;
35import android.widget.TextView;
Leon Scrogginsfe026bd2010-08-24 14:16:09 -040036
Ben Murdoch52c9f7f2013-01-18 00:50:37 +000037/**
38 * @hide
39 */
Ignacio Solla451e3382014-11-10 10:35:54 +000040@SystemApi
Ben Murdoch52c9f7f2013-01-18 00:50:37 +000041public class FindActionModeCallback implements ActionMode.Callback, TextWatcher,
42 View.OnClickListener, WebView.FindListener {
Leon Scrogginsfe026bd2010-08-24 14:16:09 -040043 private View mCustomView;
44 private EditText mEditText;
45 private TextView mMatches;
Ben Murdoch52c9f7f2013-01-18 00:50:37 +000046 private WebView mWebView;
Leon Scrogginsfe026bd2010-08-24 14:16:09 -040047 private InputMethodManager mInput;
48 private Resources mResources;
49 private boolean mMatchesFound;
50 private int mNumberOfMatches;
Victoria Lease47d0ee92012-02-13 15:47:48 -080051 private int mActiveMatchIndex;
Leon Scroggins05919f22010-09-14 17:22:36 -040052 private ActionMode mActionMode;
Leon Scrogginsfe026bd2010-08-24 14:16:09 -040053
Ben Murdoch52c9f7f2013-01-18 00:50:37 +000054 public FindActionModeCallback(Context context) {
Leon Scrogginsfe026bd2010-08-24 14:16:09 -040055 mCustomView = LayoutInflater.from(context).inflate(
56 com.android.internal.R.layout.webview_find, null);
57 mEditText = (EditText) mCustomView.findViewById(
58 com.android.internal.R.id.edit);
George Mount7e9f62e2012-05-23 16:55:46 -070059 mEditText.setCustomSelectionActionModeCallback(new NoAction());
Leon Scroggins7014b122011-01-11 15:17:34 -050060 mEditText.setOnClickListener(this);
Leon Scrogginsfe026bd2010-08-24 14:16:09 -040061 setText("");
62 mMatches = (TextView) mCustomView.findViewById(
63 com.android.internal.R.id.matches);
Yohei Yukawa777ef952015-11-25 20:32:24 -080064 mInput = context.getSystemService(InputMethodManager.class);
Leon Scrogginsfe026bd2010-08-24 14:16:09 -040065 mResources = context.getResources();
66 }
67
Ben Murdoch52c9f7f2013-01-18 00:50:37 +000068 public void finish() {
Leon Scroggins05919f22010-09-14 17:22:36 -040069 mActionMode.finish();
70 }
71
Leon Scrogginsfe026bd2010-08-24 14:16:09 -040072 /*
73 * Place text in the text field so it can be searched for. Need to press
74 * the find next or find previous button to find all of the matches.
75 */
Ben Murdoch52c9f7f2013-01-18 00:50:37 +000076 public void setText(String text) {
Leon Scrogginsfe026bd2010-08-24 14:16:09 -040077 mEditText.setText(text);
78 Spannable span = (Spannable) mEditText.getText();
79 int length = span.length();
80 // Ideally, we would like to set the selection to the whole field,
81 // but this brings up the Text selection CAB, which dismisses this
82 // one.
83 Selection.setSelection(span, length, length);
84 // Necessary each time we set the text, so that this will watch
85 // changes to it.
86 span.setSpan(this, 0, length, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
87 mMatchesFound = false;
88 }
89
90 /*
Ben Murdoch52c9f7f2013-01-18 00:50:37 +000091 * Set the WebView to search. Must be non null.
Leon Scrogginsfe026bd2010-08-24 14:16:09 -040092 */
Ben Murdoch52c9f7f2013-01-18 00:50:37 +000093 public void setWebView(WebView webView) {
Leon Scrogginsfe026bd2010-08-24 14:16:09 -040094 if (null == webView) {
95 throw new AssertionError("WebView supplied to "
96 + "FindActionModeCallback cannot be null");
97 }
98 mWebView = webView;
Ben Murdoch52c9f7f2013-01-18 00:50:37 +000099 mWebView.setFindDialogFindListener(this);
100 }
101
102 @Override
103 public void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches,
104 boolean isDoneCounting) {
105 if (isDoneCounting) {
106 updateMatchCount(activeMatchOrdinal, numberOfMatches, numberOfMatches == 0);
107 }
Leon Scrogginsfe026bd2010-08-24 14:16:09 -0400108 }
109
110 /*
111 * Move the highlight to the next match.
112 * @param next If true, find the next match further down in the document.
113 * If false, find the previous match, up in the document.
114 */
115 private void findNext(boolean next) {
116 if (mWebView == null) {
117 throw new AssertionError(
118 "No WebView for FindActionModeCallback::findNext");
119 }
Leon Scroggins7014b122011-01-11 15:17:34 -0500120 if (!mMatchesFound) {
121 findAll();
122 return;
123 }
124 if (0 == mNumberOfMatches) {
125 // There are no matches, so moving to the next match will not do
126 // anything.
127 return;
128 }
Leon Scrogginsfe026bd2010-08-24 14:16:09 -0400129 mWebView.findNext(next);
Leon Scroggins7014b122011-01-11 15:17:34 -0500130 updateMatchesString();
Leon Scrogginsfe026bd2010-08-24 14:16:09 -0400131 }
132
133 /*
134 * Highlight all the instances of the string from mEditText in mWebView.
135 */
Ben Murdoch52c9f7f2013-01-18 00:50:37 +0000136 public void findAll() {
Leon Scrogginsfe026bd2010-08-24 14:16:09 -0400137 if (mWebView == null) {
138 throw new AssertionError(
139 "No WebView for FindActionModeCallback::findAll");
140 }
141 CharSequence find = mEditText.getText();
142 if (0 == find.length()) {
143 mWebView.clearMatches();
144 mMatches.setVisibility(View.GONE);
145 mMatchesFound = false;
Victoria Leaseabeb6a72012-03-05 16:29:12 -0800146 mWebView.findAll(null);
Leon Scrogginsfe026bd2010-08-24 14:16:09 -0400147 } else {
148 mMatchesFound = true;
Victoria Lease47d0ee92012-02-13 15:47:48 -0800149 mMatches.setVisibility(View.INVISIBLE);
150 mNumberOfMatches = 0;
Victoria Leaseabeb6a72012-03-05 16:29:12 -0800151 mWebView.findAllAsync(find.toString());
Leon Scrogginsfe026bd2010-08-24 14:16:09 -0400152 }
153 }
154
Leon Scroggins571354f2011-01-04 10:12:41 -0500155 public void showSoftInput() {
Yohei Yukawa5f059652015-05-14 22:16:41 -0700156 if (mEditText.requestFocus()) {
157 mInput.showSoftInput(mEditText, 0);
158 }
Leon Scroggins571354f2011-01-04 10:12:41 -0500159 }
160
Victoria Lease0b8413b2012-03-26 13:04:10 -0700161 public void updateMatchCount(int matchIndex, int matchCount, boolean isEmptyFind) {
162 if (!isEmptyFind) {
Victoria Lease47d0ee92012-02-13 15:47:48 -0800163 mNumberOfMatches = matchCount;
164 mActiveMatchIndex = matchIndex;
165 updateMatchesString();
166 } else {
Stefan Wysocki1c15b8d2012-09-18 14:11:20 +0200167 mMatches.setVisibility(View.GONE);
Victoria Lease47d0ee92012-02-13 15:47:48 -0800168 mNumberOfMatches = 0;
169 }
170 }
171
Leon Scrogginsfe026bd2010-08-24 14:16:09 -0400172 /*
173 * Update the string which tells the user how many matches were found, and
174 * which match is currently highlighted.
175 */
176 private void updateMatchesString() {
Victoria Lease47d0ee92012-02-13 15:47:48 -0800177 if (mNumberOfMatches == 0) {
178 mMatches.setText(com.android.internal.R.string.no_matches);
179 } else {
180 mMatches.setText(mResources.getQuantityString(
Leon Scrogginsfe026bd2010-08-24 14:16:09 -0400181 com.android.internal.R.plurals.matches_found, mNumberOfMatches,
Victoria Lease47d0ee92012-02-13 15:47:48 -0800182 mActiveMatchIndex + 1, mNumberOfMatches));
183 }
184 mMatches.setVisibility(View.VISIBLE);
Leon Scrogginsfe026bd2010-08-24 14:16:09 -0400185 }
186
Leon Scroggins7014b122011-01-11 15:17:34 -0500187 // OnClickListener implementation
188
189 @Override
190 public void onClick(View v) {
191 findNext(true);
192 }
193
Leon Scrogginsfe026bd2010-08-24 14:16:09 -0400194 // ActionMode.Callback implementation
195
196 @Override
197 public boolean onCreateActionMode(ActionMode mode, Menu menu) {
Adam Powellf8419a02011-10-03 12:08:54 -0700198 if (!mode.isUiFocusable()) {
199 // If the action mode we're running in is not focusable the user
200 // will not be able to type into the find on page field. This
201 // should only come up when we're running in a dialog which is
202 // already less than ideal; disable the option for now.
203 return false;
204 }
205
Leon Scrogginsfe026bd2010-08-24 14:16:09 -0400206 mode.setCustomView(mCustomView);
207 mode.getMenuInflater().inflate(com.android.internal.R.menu.webview_find,
208 menu);
Leon Scroggins05919f22010-09-14 17:22:36 -0400209 mActionMode = mode;
Leon Scrogginsfe026bd2010-08-24 14:16:09 -0400210 Editable edit = mEditText.getText();
211 Selection.setSelection(edit, edit.length());
212 mMatches.setVisibility(View.GONE);
213 mMatchesFound = false;
214 mMatches.setText("0");
215 mEditText.requestFocus();
Leon Scrogginsfe026bd2010-08-24 14:16:09 -0400216 return true;
217 }
218
219 @Override
220 public void onDestroyActionMode(ActionMode mode) {
John Reckfffce6f2011-10-06 20:37:01 -0700221 mActionMode = null;
Leon Scrogginsfe026bd2010-08-24 14:16:09 -0400222 mWebView.notifyFindDialogDismissed();
Ben Murdoch52c9f7f2013-01-18 00:50:37 +0000223 mWebView.setFindDialogFindListener(null);
224 mInput.hideSoftInputFromWindow(mWebView.getWindowToken(), 0);
Leon Scrogginsfe026bd2010-08-24 14:16:09 -0400225 }
226
227 @Override
228 public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
229 return false;
230 }
231
232 @Override
233 public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
Leon Scroggins261f42d2010-12-09 13:46:28 -0500234 if (mWebView == null) {
235 throw new AssertionError(
236 "No WebView for FindActionModeCallback::onActionItemClicked");
237 }
Ben Murdoch52c9f7f2013-01-18 00:50:37 +0000238 mInput.hideSoftInputFromWindow(mWebView.getWindowToken(), 0);
Leon Scrogginsfe026bd2010-08-24 14:16:09 -0400239 switch(item.getItemId()) {
240 case com.android.internal.R.id.find_prev:
241 findNext(false);
242 break;
243 case com.android.internal.R.id.find_next:
244 findNext(true);
245 break;
246 default:
247 return false;
248 }
Leon Scrogginsfe026bd2010-08-24 14:16:09 -0400249 return true;
250 }
251
252 // TextWatcher implementation
253
254 @Override
255 public void beforeTextChanged(CharSequence s,
256 int start,
257 int count,
258 int after) {
259 // Does nothing. Needed to implement TextWatcher.
260 }
261
262 @Override
263 public void onTextChanged(CharSequence s,
264 int start,
265 int before,
266 int count) {
267 findAll();
268 }
269
270 @Override
271 public void afterTextChanged(Editable s) {
272 // Does nothing. Needed to implement TextWatcher.
273 }
274
John Reckd6ac7272011-11-08 15:00:17 -0800275 private Rect mGlobalVisibleRect = new Rect();
276 private Point mGlobalVisibleOffset = new Point();
277 public int getActionModeGlobalBottom() {
John Reckfffce6f2011-10-06 20:37:01 -0700278 if (mActionMode == null) {
279 return 0;
280 }
John Reckd6ac7272011-11-08 15:00:17 -0800281 View view = (View) mCustomView.getParent();
282 if (view == null) {
283 view = mCustomView;
284 }
285 view.getGlobalVisibleRect(mGlobalVisibleRect, mGlobalVisibleOffset);
286 return mGlobalVisibleRect.bottom;
John Reckfffce6f2011-10-06 20:37:01 -0700287 }
288
George Mount7e9f62e2012-05-23 16:55:46 -0700289 public static class NoAction implements ActionMode.Callback {
290 @Override
291 public boolean onCreateActionMode(ActionMode mode, Menu menu) {
292 return false;
293 }
294
295 @Override
296 public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
297 return false;
298 }
299
300 @Override
301 public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
302 return false;
303 }
304
305 @Override
306 public void onDestroyActionMode(ActionMode mode) {
307 }
308 }
Leon Scrogginsfe026bd2010-08-24 14:16:09 -0400309}