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