DialerFilter.java revision 54b6cfa9a9e5b861a9930af873580d6dc20f773c
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.util.Log;
32import android.view.KeyCharacterMap;
33import android.view.View;
34import android.graphics.Rect;
35
36
37
38public 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