1/* 2 * Copyright (C) 2006 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.widget; 18 19import android.content.Context; 20import android.view.KeyEvent; 21import android.text.Editable; 22import android.text.InputFilter; 23import android.text.Selection; 24import android.text.Spannable; 25import android.text.Spanned; 26import android.text.TextWatcher; 27import android.text.method.DialerKeyListener; 28import android.text.method.KeyListener; 29import android.text.method.TextKeyListener; 30import android.util.AttributeSet; 31import android.view.View; 32import android.graphics.Rect; 33 34 35 36public class DialerFilter extends RelativeLayout 37{ 38 public DialerFilter(Context context) { 39 super(context); 40 } 41 42 public DialerFilter(Context context, AttributeSet attrs) { 43 super(context, attrs); 44 } 45 46 @Override 47 protected void onFinishInflate() { 48 super.onFinishInflate(); 49 50 // Setup the filter view 51 mInputFilters = new InputFilter[] { new InputFilter.AllCaps() }; 52 53 mHint = (EditText) findViewById(com.android.internal.R.id.hint); 54 if (mHint == null) { 55 throw new IllegalStateException("DialerFilter must have a child EditText named hint"); 56 } 57 mHint.setFilters(mInputFilters); 58 59 mLetters = mHint; 60 mLetters.setKeyListener(TextKeyListener.getInstance()); 61 mLetters.setMovementMethod(null); 62 mLetters.setFocusable(false); 63 64 // Setup the digits view 65 mPrimary = (EditText) findViewById(com.android.internal.R.id.primary); 66 if (mPrimary == null) { 67 throw new IllegalStateException("DialerFilter must have a child EditText named primary"); 68 } 69 mPrimary.setFilters(mInputFilters); 70 71 mDigits = mPrimary; 72 mDigits.setKeyListener(DialerKeyListener.getInstance()); 73 mDigits.setMovementMethod(null); 74 mDigits.setFocusable(false); 75 76 // Look for an icon 77 mIcon = (ImageView) findViewById(com.android.internal.R.id.icon); 78 79 // Setup focus & highlight for this view 80 setFocusable(true); 81 82 // XXX Force the mode to QWERTY for now, since 12-key isn't supported 83 mIsQwerty = true; 84 setMode(DIGITS_AND_LETTERS); 85 } 86 87 /** 88 * Only show the icon view when focused, if there is one. 89 */ 90 @Override 91 protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) { 92 super.onFocusChanged(focused, direction, previouslyFocusedRect); 93 94 if (mIcon != null) { 95 mIcon.setVisibility(focused ? View.VISIBLE : View.GONE); 96 } 97 } 98 99 100 public boolean isQwertyKeyboard() { 101 return mIsQwerty; 102 } 103 104 @Override 105 public boolean onKeyDown(int keyCode, KeyEvent event) { 106 boolean handled = false; 107 108 switch (keyCode) { 109 case KeyEvent.KEYCODE_DPAD_UP: 110 case KeyEvent.KEYCODE_DPAD_DOWN: 111 case KeyEvent.KEYCODE_DPAD_LEFT: 112 case KeyEvent.KEYCODE_DPAD_RIGHT: 113 case KeyEvent.KEYCODE_ENTER: 114 case KeyEvent.KEYCODE_DPAD_CENTER: 115 break; 116 117 case KeyEvent.KEYCODE_DEL: 118 switch (mMode) { 119 case DIGITS_AND_LETTERS: 120 handled = mDigits.onKeyDown(keyCode, event); 121 handled &= mLetters.onKeyDown(keyCode, event); 122 break; 123 124 case DIGITS_AND_LETTERS_NO_DIGITS: 125 handled = mLetters.onKeyDown(keyCode, event); 126 if (mLetters.getText().length() == mDigits.getText().length()) { 127 setMode(DIGITS_AND_LETTERS); 128 } 129 break; 130 131 case DIGITS_AND_LETTERS_NO_LETTERS: 132 if (mDigits.getText().length() == mLetters.getText().length()) { 133 mLetters.onKeyDown(keyCode, event); 134 setMode(DIGITS_AND_LETTERS); 135 } 136 handled = mDigits.onKeyDown(keyCode, event); 137 break; 138 139 case DIGITS_ONLY: 140 handled = mDigits.onKeyDown(keyCode, event); 141 break; 142 143 case LETTERS_ONLY: 144 handled = mLetters.onKeyDown(keyCode, event); 145 break; 146 } 147 break; 148 149 default: 150 //mIsQwerty = msg.getKeyIsQwertyKeyboard(); 151 152 switch (mMode) { 153 case DIGITS_AND_LETTERS: 154 handled = mLetters.onKeyDown(keyCode, event); 155 156 // pass this throw so the shift state is correct (for example, 157 // on a standard QWERTY keyboard, * and 8 are on the same key) 158 if (KeyEvent.isModifierKey(keyCode)) { 159 mDigits.onKeyDown(keyCode, event); 160 handled = true; 161 break; 162 } 163 164 // Only check to see if the digit is valid if the key is a printing key 165 // in the TextKeyListener. This prevents us from hiding the digits 166 // line when keys like UP and DOWN are hit. 167 // XXX note that KEYCODE_TAB is special-cased here for 168 // devices that share tab and 0 on a single key. 169 boolean isPrint = event.isPrintingKey(); 170 if (isPrint || keyCode == KeyEvent.KEYCODE_SPACE 171 || keyCode == KeyEvent.KEYCODE_TAB) { 172 char c = event.getMatch(DialerKeyListener.CHARACTERS); 173 if (c != 0) { 174 handled &= mDigits.onKeyDown(keyCode, event); 175 } else { 176 setMode(DIGITS_AND_LETTERS_NO_DIGITS); 177 } 178 } 179 break; 180 181 case DIGITS_AND_LETTERS_NO_LETTERS: 182 case DIGITS_ONLY: 183 handled = mDigits.onKeyDown(keyCode, event); 184 break; 185 186 case DIGITS_AND_LETTERS_NO_DIGITS: 187 case LETTERS_ONLY: 188 handled = mLetters.onKeyDown(keyCode, event); 189 break; 190 } 191 } 192 193 if (!handled) { 194 return super.onKeyDown(keyCode, event); 195 } else { 196 return true; 197 } 198 } 199 200 @Override 201 public boolean onKeyUp(int keyCode, KeyEvent event) { 202 boolean a = mLetters.onKeyUp(keyCode, event); 203 boolean b = mDigits.onKeyUp(keyCode, event); 204 return a || b; 205 } 206 207 public int getMode() { 208 return mMode; 209 } 210 211 /** 212 * Change the mode of the widget. 213 * 214 * @param newMode The mode to switch to. 215 */ 216 public void setMode(int newMode) { 217 switch (newMode) { 218 case DIGITS_AND_LETTERS: 219 makeDigitsPrimary(); 220 mLetters.setVisibility(View.VISIBLE); 221 mDigits.setVisibility(View.VISIBLE); 222 break; 223 224 case DIGITS_ONLY: 225 makeDigitsPrimary(); 226 mLetters.setVisibility(View.GONE); 227 mDigits.setVisibility(View.VISIBLE); 228 break; 229 230 case LETTERS_ONLY: 231 makeLettersPrimary(); 232 mLetters.setVisibility(View.VISIBLE); 233 mDigits.setVisibility(View.GONE); 234 break; 235 236 case DIGITS_AND_LETTERS_NO_LETTERS: 237 makeDigitsPrimary(); 238 mLetters.setVisibility(View.INVISIBLE); 239 mDigits.setVisibility(View.VISIBLE); 240 break; 241 242 case DIGITS_AND_LETTERS_NO_DIGITS: 243 makeLettersPrimary(); 244 mLetters.setVisibility(View.VISIBLE); 245 mDigits.setVisibility(View.INVISIBLE); 246 break; 247 248 } 249 int oldMode = mMode; 250 mMode = newMode; 251 onModeChange(oldMode, newMode); 252 } 253 254 private void makeLettersPrimary() { 255 if (mPrimary == mDigits) { 256 swapPrimaryAndHint(true); 257 } 258 } 259 260 private void makeDigitsPrimary() { 261 if (mPrimary == mLetters) { 262 swapPrimaryAndHint(false); 263 } 264 } 265 266 private void swapPrimaryAndHint(boolean makeLettersPrimary) { 267 Editable lettersText = mLetters.getText(); 268 Editable digitsText = mDigits.getText(); 269 KeyListener lettersInput = mLetters.getKeyListener(); 270 KeyListener digitsInput = mDigits.getKeyListener(); 271 272 if (makeLettersPrimary) { 273 mLetters = mPrimary; 274 mDigits = mHint; 275 } else { 276 mLetters = mHint; 277 mDigits = mPrimary; 278 } 279 280 mLetters.setKeyListener(lettersInput); 281 mLetters.setText(lettersText); 282 lettersText = mLetters.getText(); 283 Selection.setSelection(lettersText, lettersText.length()); 284 285 mDigits.setKeyListener(digitsInput); 286 mDigits.setText(digitsText); 287 digitsText = mDigits.getText(); 288 Selection.setSelection(digitsText, digitsText.length()); 289 290 // Reset the filters 291 mPrimary.setFilters(mInputFilters); 292 mHint.setFilters(mInputFilters); 293 } 294 295 296 public CharSequence getLetters() { 297 if (mLetters.getVisibility() == View.VISIBLE) { 298 return mLetters.getText(); 299 } else { 300 return ""; 301 } 302 } 303 304 public CharSequence getDigits() { 305 if (mDigits.getVisibility() == View.VISIBLE) { 306 return mDigits.getText(); 307 } else { 308 return ""; 309 } 310 } 311 312 public CharSequence getFilterText() { 313 if (mMode != DIGITS_ONLY) { 314 return getLetters(); 315 } else { 316 return getDigits(); 317 } 318 } 319 320 public void append(String text) { 321 switch (mMode) { 322 case DIGITS_AND_LETTERS: 323 mDigits.getText().append(text); 324 mLetters.getText().append(text); 325 break; 326 327 case DIGITS_AND_LETTERS_NO_LETTERS: 328 case DIGITS_ONLY: 329 mDigits.getText().append(text); 330 break; 331 332 case DIGITS_AND_LETTERS_NO_DIGITS: 333 case LETTERS_ONLY: 334 mLetters.getText().append(text); 335 break; 336 } 337 } 338 339 /** 340 * Clears both the digits and the filter text. 341 */ 342 public void clearText() { 343 Editable text; 344 345 text = mLetters.getText(); 346 text.clear(); 347 348 text = mDigits.getText(); 349 text.clear(); 350 351 // Reset the mode based on the hardware type 352 if (mIsQwerty) { 353 setMode(DIGITS_AND_LETTERS); 354 } else { 355 setMode(DIGITS_ONLY); 356 } 357 } 358 359 public void setLettersWatcher(TextWatcher watcher) { 360 CharSequence text = mLetters.getText(); 361 Spannable span = (Spannable)text; 362 span.setSpan(watcher, 0, text.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); 363 } 364 365 public void setDigitsWatcher(TextWatcher watcher) { 366 CharSequence text = mDigits.getText(); 367 Spannable span = (Spannable)text; 368 span.setSpan(watcher, 0, text.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); 369 } 370 371 public void setFilterWatcher(TextWatcher watcher) { 372 if (mMode != DIGITS_ONLY) { 373 setLettersWatcher(watcher); 374 } else { 375 setDigitsWatcher(watcher); 376 } 377 } 378 379 public void removeFilterWatcher(TextWatcher watcher) { 380 Spannable text; 381 if (mMode != DIGITS_ONLY) { 382 text = mLetters.getText(); 383 } else { 384 text = mDigits.getText(); 385 } 386 text.removeSpan(watcher); 387 } 388 389 /** 390 * Called right after the mode changes to give subclasses the option to 391 * restyle, etc. 392 */ 393 protected void onModeChange(int oldMode, int newMode) { 394 } 395 396 /** This mode has both lines */ 397 public static final int DIGITS_AND_LETTERS = 1; 398 /** This mode is when after starting in {@link #DIGITS_AND_LETTERS} mode the filter 399 * has removed all possibility of the digits matching, leaving only the letters line */ 400 public static final int DIGITS_AND_LETTERS_NO_DIGITS = 2; 401 /** This mode is when after starting in {@link #DIGITS_AND_LETTERS} mode the filter 402 * has removed all possibility of the letters matching, leaving only the digits line */ 403 public static final int DIGITS_AND_LETTERS_NO_LETTERS = 3; 404 /** This mode has only the digits line */ 405 public static final int DIGITS_ONLY = 4; 406 /** This mode has only the letters line */ 407 public static final int LETTERS_ONLY = 5; 408 409 EditText mLetters; 410 EditText mDigits; 411 EditText mPrimary; 412 EditText mHint; 413 InputFilter mInputFilters[]; 414 ImageView mIcon; 415 int mMode; 416 private boolean mIsQwerty; 417} 418