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