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