FindActionModeCallback.java revision 73254be0ddfc075521a63a069c6c30d200dca557
1/* 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 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 ActionMode mActionMode; 47 48 FindActionModeCallback(Context context) { 49 mCustomView = LayoutInflater.from(context).inflate( 50 com.android.internal.R.layout.webview_find, null); 51 mEditText = (EditText) mCustomView.findViewById( 52 com.android.internal.R.id.edit); 53 // Override long click so that select ActionMode is not opened, which 54 // would exit find ActionMode. 55 mEditText.setOnLongClickListener(this); 56 setText(""); 57 mMatches = (TextView) mCustomView.findViewById( 58 com.android.internal.R.id.matches); 59 mInput = (InputMethodManager) 60 context.getSystemService(Context.INPUT_METHOD_SERVICE); 61 mResources = context.getResources(); 62 } 63 64 void finish() { 65 mActionMode.finish(); 66 } 67 68 /* 69 * Place text in the text field so it can be searched for. Need to press 70 * the find next or find previous button to find all of the matches. 71 */ 72 void setText(String text) { 73 mEditText.setText(text); 74 Spannable span = (Spannable) mEditText.getText(); 75 int length = span.length(); 76 // Ideally, we would like to set the selection to the whole field, 77 // but this brings up the Text selection CAB, which dismisses this 78 // one. 79 Selection.setSelection(span, length, length); 80 // Necessary each time we set the text, so that this will watch 81 // changes to it. 82 span.setSpan(this, 0, length, Spannable.SPAN_INCLUSIVE_INCLUSIVE); 83 mMatchesFound = false; 84 } 85 86 /* 87 * Set the WebView to search. Must be non null, and set before calling 88 * startActionMode. 89 */ 90 void setWebView(WebView webView) { 91 if (null == webView) { 92 throw new AssertionError("WebView supplied to " 93 + "FindActionModeCallback cannot be null"); 94 } 95 mWebView = webView; 96 } 97 98 /* 99 * Move the highlight to the next match. 100 * @param next If true, find the next match further down in the document. 101 * If false, find the previous match, up in the document. 102 */ 103 private void findNext(boolean next) { 104 if (mWebView == null) { 105 throw new AssertionError( 106 "No WebView for FindActionModeCallback::findNext"); 107 } 108 mWebView.findNext(next); 109 } 110 111 /* 112 * Highlight all the instances of the string from mEditText in mWebView. 113 */ 114 void findAll() { 115 if (mWebView == null) { 116 throw new AssertionError( 117 "No WebView for FindActionModeCallback::findAll"); 118 } 119 CharSequence find = mEditText.getText(); 120 if (0 == find.length()) { 121 mWebView.clearMatches(); 122 mMatches.setVisibility(View.GONE); 123 mMatchesFound = false; 124 } else { 125 mMatchesFound = true; 126 mMatches.setVisibility(View.VISIBLE); 127 mNumberOfMatches = mWebView.findAll(find.toString()); 128 if (0 == mNumberOfMatches) { 129 mMatches.setText(mResources.getString( 130 com.android.internal.R.string.no_matches)); 131 } else { 132 updateMatchesString(); 133 } 134 } 135 } 136 137 public void showSoftInput() { 138 mInput.startGettingWindowFocus(mEditText.getRootView()); 139 mInput.focusIn(mEditText); 140 mInput.showSoftInput(mEditText, 0); 141 } 142 143 /* 144 * Update the string which tells the user how many matches were found, and 145 * which match is currently highlighted. 146 * Not to be called when mNumberOfMatches is 0. 147 */ 148 private void updateMatchesString() { 149 String template = mResources.getQuantityString( 150 com.android.internal.R.plurals.matches_found, mNumberOfMatches, 151 mWebView.findIndex() + 1, mNumberOfMatches); 152 153 mMatches.setText(template); 154 } 155 156 // OnLongClickListener implementation 157 158 @Override 159 public boolean onLongClick(View v) { return true; } 160 161 // ActionMode.Callback implementation 162 163 @Override 164 public boolean onCreateActionMode(ActionMode mode, Menu menu) { 165 mode.setCustomView(mCustomView); 166 mode.getMenuInflater().inflate(com.android.internal.R.menu.webview_find, 167 menu); 168 mActionMode = mode; 169 Editable edit = mEditText.getText(); 170 Selection.setSelection(edit, edit.length()); 171 mMatches.setVisibility(View.GONE); 172 mMatchesFound = false; 173 mMatches.setText("0"); 174 mEditText.requestFocus(); 175 return true; 176 } 177 178 @Override 179 public void onDestroyActionMode(ActionMode mode) { 180 mWebView.notifyFindDialogDismissed(); 181 mInput.hideSoftInputFromWindow(mWebView.getWindowToken(), 0); 182 } 183 184 @Override 185 public boolean onPrepareActionMode(ActionMode mode, Menu menu) { 186 return false; 187 } 188 189 @Override 190 public boolean onActionItemClicked(ActionMode mode, MenuItem item) { 191 if (mWebView == null) { 192 throw new AssertionError( 193 "No WebView for FindActionModeCallback::onActionItemClicked"); 194 } 195 mInput.hideSoftInputFromWindow(mWebView.getWindowToken(), 0); 196 if (!mMatchesFound) { 197 findAll(); 198 return true; 199 } 200 if (0 == mNumberOfMatches) { 201 // There are no matches, so moving to the next match will not do 202 // anything. 203 return true; 204 } 205 switch(item.getItemId()) { 206 case com.android.internal.R.id.find_prev: 207 findNext(false); 208 break; 209 case com.android.internal.R.id.find_next: 210 findNext(true); 211 break; 212 default: 213 return false; 214 } 215 updateMatchesString(); 216 return true; 217 } 218 219 // TextWatcher implementation 220 221 @Override 222 public void beforeTextChanged(CharSequence s, 223 int start, 224 int count, 225 int after) { 226 // Does nothing. Needed to implement TextWatcher. 227 } 228 229 @Override 230 public void onTextChanged(CharSequence s, 231 int start, 232 int before, 233 int count) { 234 findAll(); 235 } 236 237 @Override 238 public void afterTextChanged(Editable s) { 239 // Does nothing. Needed to implement TextWatcher. 240 } 241 242} 243