1 /* 2 * Copyright (C) 2007 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 17 package com.android.browser; 18 19 import android.content.Context; 20 import android.text.Editable; 21 import android.text.Selection; 22 import android.text.Spannable; 23 import android.text.TextWatcher; 24 import android.view.Gravity; 25 import android.view.KeyEvent; 26 import android.view.LayoutInflater; 27 import android.view.View; 28 import android.view.ViewGroup; 29 import android.view.animation.AnimationUtils; 30 import android.view.inputmethod.InputMethodManager; 31 import android.webkit.WebView; 32 import android.widget.EditText; 33 import android.widget.LinearLayout; 34 import android.widget.TextView; 35 36 /* package */ class FindDialog extends WebDialog implements TextWatcher { 37 private TextView mMatches; 38 39 // Views with which the user can interact. 40 private EditText mEditText; 41 private View mNextButton; 42 private View mPrevButton; 43 private View mMatchesView; 44 45 // When the dialog is opened up with old text, enter needs to be pressed 46 // (or the text needs to be changed) before WebView.findAll can be called. 47 // Once it has been called, enter should move to the next match. 48 private boolean mMatchesFound; 49 private int mNumberOfMatches; 50 51 private View.OnClickListener mFindListener = new View.OnClickListener() { 52 public void onClick(View v) { 53 findNext(); 54 } 55 }; 56 57 private View.OnClickListener mFindPreviousListener = 58 new View.OnClickListener() { 59 public void onClick(View v) { 60 if (mWebView == null) { 61 throw new AssertionError("No WebView for FindDialog::onClick"); 62 } 63 mWebView.findNext(false); 64 updateMatchesString(); 65 hideSoftInput(); 66 } 67 }; 68 69 private void disableButtons() { 70 mPrevButton.setEnabled(false); 71 mNextButton.setEnabled(false); 72 mPrevButton.setFocusable(false); 73 mNextButton.setFocusable(false); 74 } 75 76 /* package */ FindDialog(BrowserActivity context) { 77 super(context); 78 79 LayoutInflater factory = LayoutInflater.from(context); 80 factory.inflate(R.layout.browser_find, this); 81 82 addCancel(); 83 mEditText = (EditText) findViewById(R.id.edit); 84 85 View button = findViewById(R.id.next); 86 button.setOnClickListener(mFindListener); 87 mNextButton = button; 88 89 button = findViewById(R.id.previous); 90 button.setOnClickListener(mFindPreviousListener); 91 mPrevButton = button; 92 93 mMatches = (TextView) findViewById(R.id.matches); 94 mMatchesView = findViewById(R.id.matches_view); 95 disableButtons(); 96 97 } 98 99 /** 100 * Called by BrowserActivity.closeDialog. Start the animation to hide 101 * the dialog, inform the WebView that the dialog is being dismissed, 102 * and hide the soft keyboard. 103 */ 104 public void dismiss() { 105 super.dismiss(); 106 mWebView.notifyFindDialogDismissed(); 107 hideSoftInput(); 108 } 109 110 @Override 111 public boolean dispatchKeyEventPreIme(KeyEvent event) { 112 if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) { 113 KeyEvent.DispatcherState state = getKeyDispatcherState(); 114 if (state != null) { 115 int action = event.getAction(); 116 if (KeyEvent.ACTION_DOWN == action 117 && event.getRepeatCount() == 0) { 118 state.startTracking(event, this); 119 return true; 120 } else if (KeyEvent.ACTION_UP == action 121 && !event.isCanceled() && state.isTracking(event)) { 122 mBrowserActivity.closeDialogs(); 123 return true; 124 } 125 } 126 } 127 return super.dispatchKeyEventPreIme(event); 128 } 129 130 @Override 131 public boolean dispatchKeyEvent(KeyEvent event) { 132 int keyCode = event.getKeyCode(); 133 if (event.getAction() == KeyEvent.ACTION_UP) { 134 if (keyCode == KeyEvent.KEYCODE_ENTER 135 && mEditText.hasFocus()) { 136 if (mMatchesFound) { 137 findNext(); 138 } else { 139 findAll(); 140 // Set the selection to the end. 141 Spannable span = (Spannable) mEditText.getText(); 142 Selection.setSelection(span, span.length()); 143 } 144 return true; 145 } 146 } 147 return super.dispatchKeyEvent(event); 148 } 149 150 private void findNext() { 151 if (mWebView == null) { 152 throw new AssertionError("No WebView for FindDialog::findNext"); 153 } 154 mWebView.findNext(true); 155 updateMatchesString(); 156 hideSoftInput(); 157 } 158 159 public void show() { 160 super.show(); 161 // In case the matches view is showing from a previous search 162 mMatchesView.setVisibility(View.INVISIBLE); 163 mMatchesFound = false; 164 // This text is only here to ensure that mMatches has a height. 165 mMatches.setText("0"); 166 mEditText.requestFocus(); 167 Spannable span = (Spannable) mEditText.getText(); 168 int length = span.length(); 169 Selection.setSelection(span, 0, length); 170 span.setSpan(this, 0, length, Spannable.SPAN_INCLUSIVE_INCLUSIVE); 171 disableButtons(); 172 InputMethodManager imm = (InputMethodManager) 173 mBrowserActivity.getSystemService(Context.INPUT_METHOD_SERVICE); 174 imm.showSoftInput(mEditText, 0); 175 } 176 177 // TextWatcher methods 178 public void beforeTextChanged(CharSequence s, 179 int start, 180 int count, 181 int after) { 182 } 183 184 public void onTextChanged(CharSequence s, 185 int start, 186 int before, 187 int count) { 188 findAll(); 189 } 190 191 private void findAll() { 192 if (mWebView == null) { 193 throw new AssertionError( 194 "No WebView for FindDialog::findAll"); 195 } 196 CharSequence find = mEditText.getText(); 197 if (0 == find.length()) { 198 disableButtons(); 199 mWebView.clearMatches(); 200 mMatchesView.setVisibility(View.INVISIBLE); 201 } else { 202 mMatchesView.setVisibility(View.VISIBLE); 203 int found = mWebView.findAll(find.toString()); 204 mMatchesFound = true; 205 setMatchesFound(found); 206 if (found < 2) { 207 disableButtons(); 208 if (found == 0) { 209 // Cannot use getQuantityString, which ignores the "zero" 210 // quantity. 211 // FIXME: is this fix is beyond the scope 212 // of adding touch selection to gingerbread? 213 // mMatches.setText(mBrowserActivity.getResources().getString( 214 // R.string.no_matches)); 215 } 216 } else { 217 mPrevButton.setFocusable(true); 218 mNextButton.setFocusable(true); 219 mPrevButton.setEnabled(true); 220 mNextButton.setEnabled(true); 221 } 222 } 223 } 224 225 private void setMatchesFound(int found) { 226 mNumberOfMatches = found; 227 updateMatchesString(); 228 } 229 230 public void setText(String text) { 231 mEditText.setText(text); 232 findAll(); 233 } 234 235 private void updateMatchesString() { 236 // Note: updateMatchesString is only called by methods that have already 237 // checked mWebView for null. 238 String template = mBrowserActivity.getResources(). 239 getQuantityString(R.plurals.matches_found, mNumberOfMatches, 240 mWebView.findIndex() + 1, mNumberOfMatches); 241 242 mMatches.setText(template); 243 } 244 245 public void afterTextChanged(Editable s) { 246 } 247 } 248