Home | History | Annotate | Download | only in widget
      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