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