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