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.text.method;
18 
19 import android.view.KeyEvent;
20 import android.view.View;
21 import android.text.Editable;
22 import android.text.InputFilter;
23 import android.text.Selection;
24 import android.text.Spannable;
25 import android.text.SpannableStringBuilder;
26 import android.text.Spanned;
27 
28 /**
29  * For numeric text entry
30  * <p></p>
31  * As for all implementations of {@link KeyListener}, this class is only concerned
32  * with hardware keyboards.  Software input methods have no obligation to trigger
33  * the methods in this class.
34  */
35 public abstract class NumberKeyListener extends BaseKeyListener
36     implements InputFilter
37 {
38     /**
39      * You can say which characters you can accept.
40      */
getAcceptedChars()41     protected abstract char[] getAcceptedChars();
42 
lookup(KeyEvent event, Spannable content)43     protected int lookup(KeyEvent event, Spannable content) {
44         return event.getMatch(getAcceptedChars(), getMetaState(content, event));
45     }
46 
filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend)47     public CharSequence filter(CharSequence source, int start, int end,
48                                Spanned dest, int dstart, int dend) {
49         char[] accept = getAcceptedChars();
50         boolean filter = false;
51 
52         int i;
53         for (i = start; i < end; i++) {
54             if (!ok(accept, source.charAt(i))) {
55                 break;
56             }
57         }
58 
59         if (i == end) {
60             // It was all OK.
61             return null;
62         }
63 
64         if (end - start == 1) {
65             // It was not OK, and there is only one char, so nothing remains.
66             return "";
67         }
68 
69         SpannableStringBuilder filtered =
70             new SpannableStringBuilder(source, start, end);
71         i -= start;
72         end -= start;
73 
74         int len = end - start;
75         // Only count down to i because the chars before that were all OK.
76         for (int j = end - 1; j >= i; j--) {
77             if (!ok(accept, source.charAt(j))) {
78                 filtered.delete(j, j + 1);
79             }
80         }
81 
82         return filtered;
83     }
84 
ok(char[] accept, char c)85     protected static boolean ok(char[] accept, char c) {
86         for (int i = accept.length - 1; i >= 0; i--) {
87             if (accept[i] == c) {
88                 return true;
89             }
90         }
91 
92         return false;
93     }
94 
95     @Override
onKeyDown(View view, Editable content, int keyCode, KeyEvent event)96     public boolean onKeyDown(View view, Editable content,
97                              int keyCode, KeyEvent event) {
98         int selStart, selEnd;
99 
100         {
101             int a = Selection.getSelectionStart(content);
102             int b = Selection.getSelectionEnd(content);
103 
104             selStart = Math.min(a, b);
105             selEnd = Math.max(a, b);
106         }
107 
108         if (selStart < 0 || selEnd < 0) {
109             selStart = selEnd = 0;
110             Selection.setSelection(content, 0);
111         }
112 
113         int i = event != null ? lookup(event, content) : 0;
114         int repeatCount = event != null ? event.getRepeatCount() : 0;
115         if (repeatCount == 0) {
116             if (i != 0) {
117                 if (selStart != selEnd) {
118                     Selection.setSelection(content, selEnd);
119                 }
120 
121                 content.replace(selStart, selEnd, String.valueOf((char) i));
122 
123                 adjustMetaAfterKeypress(content);
124                 return true;
125             }
126         } else if (i == '0' && repeatCount == 1) {
127             // Pretty hackish, it replaces the 0 with the +
128 
129             if (selStart == selEnd && selEnd > 0 &&
130                     content.charAt(selStart - 1) == '0') {
131                 content.replace(selStart - 1, selEnd, String.valueOf('+'));
132                 adjustMetaAfterKeypress(content);
133                 return true;
134             }
135         }
136 
137         adjustMetaAfterKeypress(content);
138         return super.onKeyDown(view, content, keyCode, event);
139     }
140 }
141