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         // 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