FindActionModeCallback.java revision 571354fd29db3e4855e2f179c2c6ad47f4eefd77
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.showSoftInput(mEditText, 0); 139 } 140 141 /* 142 * Update the string which tells the user how many matches were found, and 143 * which match is currently highlighted. 144 */ 145 private void updateMatchesString() { 146 String template = mResources.getQuantityString( 147 com.android.internal.R.plurals.matches_found, mNumberOfMatches, 148 mWebView.findIndex() + 1, mNumberOfMatches); 149 150 mMatches.setText(template); 151 } 152 153 // OnLongClickListener implementation 154 155 @Override 156 public boolean onLongClick(View v) { return true; } 157 158 // ActionMode.Callback implementation 159 160 @Override 161 public boolean onCreateActionMode(ActionMode mode, Menu menu) { 162 mode.setCustomView(mCustomView); 163 mode.getMenuInflater().inflate(com.android.internal.R.menu.webview_find, 164 menu); 165 mActionMode = mode; 166 Editable edit = mEditText.getText(); 167 Selection.setSelection(edit, edit.length()); 168 mMatches.setVisibility(View.GONE); 169 mMatchesFound = false; 170 mMatches.setText("0"); 171 mEditText.requestFocus(); 172 return true; 173 } 174 175 @Override 176 public void onDestroyActionMode(ActionMode mode) { 177 mWebView.notifyFindDialogDismissed(); 178 mInput.hideSoftInputFromWindow(mWebView.getWindowToken(), 0); 179 } 180 181 @Override 182 public boolean onPrepareActionMode(ActionMode mode, Menu menu) { 183 return false; 184 } 185 186 @Override 187 public boolean onActionItemClicked(ActionMode mode, MenuItem item) { 188 if (mWebView == null) { 189 throw new AssertionError( 190 "No WebView for FindActionModeCallback::onActionItemClicked"); 191 } 192 mInput.hideSoftInputFromWindow(mWebView.getWindowToken(), 0); 193 if (!mMatchesFound) { 194 findAll(); 195 return true; 196 } 197 switch(item.getItemId()) { 198 case com.android.internal.R.id.find_prev: 199 findNext(false); 200 break; 201 case com.android.internal.R.id.find_next: 202 findNext(true); 203 break; 204 default: 205 return false; 206 } 207 updateMatchesString(); 208 return true; 209 } 210 211 // TextWatcher implementation 212 213 @Override 214 public void beforeTextChanged(CharSequence s, 215 int start, 216 int count, 217 int after) { 218 // Does nothing. Needed to implement TextWatcher. 219 } 220 221 @Override 222 public void onTextChanged(CharSequence s, 223 int start, 224 int before, 225 int count) { 226 findAll(); 227 } 228 229 @Override 230 public void afterTextChanged(Editable s) { 231 // Does nothing. Needed to implement TextWatcher. 232 } 233 234} 235