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