blob: 0b0fdaeb699e2d3a51914970443a207ea95a0dac [file] [log] [blame]
Leon Scroggins III8e4fbf12010-08-17 16:58:15 -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 com.android.browser;
18
19import android.content.Context;
20import android.content.res.Resources;
21import android.text.Editable;
22import android.text.Selection;
23import android.text.Spannable;
24import android.text.TextWatcher;
25import android.webkit.WebView;
26import android.widget.EditText;
27import android.widget.TextView;
28import android.view.ActionMode;
29import android.view.LayoutInflater;
30import android.view.Menu;
31import android.view.MenuInflater;
32import android.view.MenuItem;
33import android.view.View;
34import android.view.inputmethod.InputMethodManager;
35
36class FindActionModeCallback implements ActionMode.Callback, TextWatcher,
37 View.OnLongClickListener {
38 private View mCustomView;
39 private EditText mEditText;
40 private TextView mMatches;
41 private WebView mWebView;
42 private InputMethodManager mInput;
43 private Resources mResources;
44 private boolean mMatchesFound;
45 private int mNumberOfMatches;
46 private BrowserActivity mBrowserActivity;
47
48 FindActionModeCallback(BrowserActivity context) {
49 mCustomView = LayoutInflater.from(context).inflate(
50 R.layout.browser_find, null);
51 mEditText = (EditText) mCustomView.findViewById(R.id.edit);
52 // Override long click so that select ActionMode is not opened, which
53 // would exit find ActionMode.
54 mEditText.setOnLongClickListener(this);
55 Spannable span = (Spannable) mEditText.getText();
56 int length = span.length();
57 span.setSpan(this, 0, length, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
58 mMatches = (TextView) mCustomView.findViewById(R.id.matches);
59 mInput = (InputMethodManager)
60 context.getSystemService(Context.INPUT_METHOD_SERVICE);
61 mResources = context.getResources();
62 mBrowserActivity = context;
63 }
64
65 /*
66 * Place text in the text field so it can be searched for. Need to press
67 * the find next or find previous button to find all of the matches.
68 */
69 void setText(String text) {
70 mEditText.setText(text);
71 Spannable span = (Spannable) mEditText.getText();
72 int length = span.length();
73 // Ideally, we would like to set the selection to the whole field,
74 // but this brings up the Text selection CAB, which dismisses this
75 // one.
76 Selection.setSelection(span, length, length);
77 // Necessary each time we set the text, so that this will watch
78 // changes to it.
79 span.setSpan(this, 0, length, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
80 mMatchesFound = false;
81 }
82
83 /*
84 * Set the WebView to search. Must be non null, and set before calling
85 * startActionMode.
86 */
87 void setWebView(WebView webView) {
88 if (null == webView) {
89 throw new AssertionError("WebView supplied to "
90 + "FindActionModeCallback cannot be null");
91 }
92 mWebView = webView;
93 }
94
95 /*
96 * Move the highlight to the next match.
97 * @param next If true, find the next match further down in the document.
98 * If false, find the previous match, up in the document.
99 */
100 private void findNext(boolean next) {
101 if (mWebView == null) {
102 throw new AssertionError(
103 "No WebView for FindActionModeCallback::findNext");
104 }
105 mWebView.findNext(next);
106 }
107
108 /*
109 * Highlight all the instances of the string from mEditText in mWebView.
110 */
111 private void findAll() {
112 if (mWebView == null) {
113 throw new AssertionError(
114 "No WebView for FindActionModeCallback::findAll");
115 }
116 CharSequence find = mEditText.getText();
117 if (0 == find.length()) {
118 mWebView.clearMatches();
119 mMatches.setVisibility(View.INVISIBLE);
120 mMatchesFound = false;
121 } else {
122 mMatchesFound = true;
123 mMatches.setVisibility(View.VISIBLE);
124 mNumberOfMatches = mWebView.findAll(find.toString());
125 if (0 == mNumberOfMatches) {
126 mMatches.setText(mResources.getString(R.string.no_matches));
127 } else {
128 updateMatchesString();
129 }
130 }
131 }
132
133 /*
134 * Update the string which tells the user how many matches were found, and
135 * which match is currently highlighted.
136 */
137 private void updateMatchesString() {
138 String template = mResources.getQuantityString(R.plurals.matches_found,
139 mNumberOfMatches, mWebView.findIndex() + 1, mNumberOfMatches);
140
141 mMatches.setText(template);
142 }
143
144 // OnLongClickListener implementation
145
146 @Override
147 public boolean onLongClick(View v) { return true; }
148
149 // ActionMode.Callback implementation
150
151 @Override
152 public boolean onCreateActionMode(ActionMode mode, Menu menu) {
153 mode.setCustomView(mCustomView);
154 mode.getMenuInflater().inflate(R.menu.find, menu);
155 // Ideally, we would like to preserve the old find text, but it
156 // brings up the Text selection CAB, and therefore dismisses
157 // find
158 setText("");
159 mMatches.setVisibility(View.INVISIBLE);
160 mMatchesFound = false;
161 mMatches.setText("0");
162 mEditText.requestFocus();
163 mInput.showSoftInput(mEditText, 0);
164 return true;
165 }
166
167 @Override
168 public void onDestroyActionMode(ActionMode mode) {
169 mBrowserActivity.onEndActionMode();
170 mWebView.notifyFindDialogDismissed();
171 mInput.hideSoftInputFromWindow(mWebView.getWindowToken(), 0);
172 }
173
174 @Override
175 public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
176 return false;
177 }
178
179 @Override
180 public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
181 if (!mMatchesFound) {
182 findAll();
183 return true;
184 }
185 switch(item.getItemId()) {
186 case R.id.find_prev:
187 findNext(false);
188 break;
189 case R.id.find_next:
190 findNext(true);
191 break;
192 default:
193 return false;
194 }
195 updateMatchesString();
196 return true;
197 }
198
199 // TextWatcher methods
200
201 @Override
202 public void beforeTextChanged(CharSequence s,
203 int start,
204 int count,
205 int after) {
206 }
207
208 @Override
209 public void onTextChanged(CharSequence s,
210 int start,
211 int before,
212 int count) {
213 findAll();
214 }
215
216 @Override
217 public void afterTextChanged(Editable s) { }
218
219}