1
by Tiago Salem Herrmann
initial commit |
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 |
* Original source code available at: http://androidxref.com/4.0.4/xref/frameworks/base/telephony/java/android/telephony/PhoneNumberUtils.java
|
|
17 |
*/
|
|
18 |
||
19 |
#ifndef PHONENUMBERUTILS_H
|
|
20 |
#define PHONENUMBERUTILS_H
|
|
21 |
||
22 |
namespace PhoneNumberUtils |
|
23 |
{
|
|
24 |
||
25 |
/** True if c is ISO-LATIN characters 0-9, *, # , +, WILD, WAIT, PAUSE */
|
|
26 |
bool isNonSeparator(char c) |
|
27 |
{
|
|
28 |
return (c >= '0' && c <= '9') || c == '*' || c == '#' || c == '+' |
|
29 |
|| c == 'N' || c == ';' || c == ','; |
|
30 |
}
|
|
31 |
||
32 |
/** True if c is ISO-LATIN characters 0-9, *, # , +, WILD */
|
|
33 |
bool isDialable(char c) |
|
34 |
{
|
|
35 |
return (c >= '0' && c <= '9') || c == '*' || c == '#' || c == '+' || c == 'N'; |
|
36 |
}
|
|
37 |
||
38 |
/** True if c is ISO-LATIN characters 0-9 */
|
|
39 |
bool isISODigit (char c) { |
|
40 |
return c >= '0' && c <= '9'; |
|
41 |
}
|
|
42 |
||
43 |
/** or -1 if both are negative */
|
|
44 |
int minPositive (int a, int b) |
|
45 |
{
|
|
46 |
if (a >= 0 && b >= 0) { |
|
47 |
return (a < b) ? a : b; |
|
48 |
} else if (a >= 0) { /* && b < 0 */ |
|
49 |
return a; |
|
50 |
} else if (b >= 0) { /* && a < 0 */ |
|
51 |
return b; |
|
52 |
} else { /* a < 0 && b < 0 */ |
|
53 |
return -1; |
|
54 |
}
|
|
55 |
}
|
|
56 |
||
57 |
/** index of the last character of the network portion
|
|
58 |
* (eg anything after is a post-dial string)
|
|
59 |
*/
|
|
60 |
int indexOfLastNetworkChar(const QString &a) |
|
61 |
{
|
|
62 |
int pIndex, wIndex; |
|
63 |
int origLength; |
|
64 |
int trimIndex; |
|
65 |
||
66 |
origLength = a.length(); |
|
67 |
||
68 |
pIndex = a.indexOf(','); |
|
69 |
wIndex = a.indexOf(';'); |
|
70 |
||
71 |
trimIndex = minPositive(pIndex, wIndex); |
|
72 |
||
73 |
if (trimIndex < 0) { |
|
74 |
return origLength - 1; |
|
75 |
} else { |
|
76 |
return trimIndex - 1; |
|
77 |
}
|
|
78 |
}
|
|
79 |
||
80 |
/** all of a up to len must be an international prefix or
|
|
81 |
* separators/non-dialing digits
|
|
82 |
*/
|
|
83 |
bool matchIntlPrefix(const QString &a, int len) |
|
84 |
{
|
|
85 |
/* '([^0-9*#+pwn]\+[^0-9*#+pwn] | [^0-9*#+pwn]0(0|11)[^0-9*#+pwn] )$' */
|
|
86 |
/* 0 1 2 3 45 */
|
|
87 |
||
88 |
int state = 0; |
|
89 |
for (int i = 0 ; i < len ; i++) { |
|
90 |
char c = a.at(i).toLatin1(); |
|
91 |
||
92 |
switch (state) { |
|
93 |
case 0: |
|
94 |
if (c == '+') state = 1; |
|
95 |
else if (c == '0') state = 2; |
|
96 |
else if (isNonSeparator(c)) return false; |
|
97 |
break; |
|
98 |
||
99 |
case 2: |
|
100 |
if (c == '0') state = 3; |
|
101 |
else if (c == '1') state = 4; |
|
102 |
else if (isNonSeparator(c)) return false; |
|
103 |
break; |
|
104 |
||
105 |
case 4: |
|
106 |
if (c == '1') state = 5; |
|
107 |
else if (isNonSeparator(c)) return false; |
|
108 |
break; |
|
109 |
||
110 |
default: |
|
111 |
if (isNonSeparator(c)) return false; |
|
112 |
break; |
|
113 |
||
114 |
}
|
|
115 |
}
|
|
116 |
||
117 |
return state == 1 || state == 3 || state == 5; |
|
118 |
}
|
|
119 |
||
120 |
/** all of 'a' up to len must match non-US trunk prefix ('0') */
|
|
121 |
bool matchTrunkPrefix(const QString &a, int len) { |
|
122 |
bool found; |
|
123 |
||
124 |
found = false; |
|
125 |
||
126 |
for (int i = 0 ; i < len ; i++) { |
|
127 |
char c = a.at(i).toLatin1(); |
|
128 |
||
129 |
if (c == '0' && !found) { |
|
130 |
found = true; |
|
131 |
} else if (isNonSeparator(c)) { |
|
132 |
return false; |
|
133 |
}
|
|
134 |
}
|
|
135 |
||
136 |
return found; |
|
137 |
}
|
|
138 |
||
139 |
/** all of 'a' up to len must be a (+|00|011)country code)
|
|
140 |
* We're fast and loose with the country code. Any \d{1,3} matches */
|
|
141 |
bool matchIntlPrefixAndCC(const QString &a, int len) { |
|
142 |
/* [^0-9*#+pwn]*(\+|0(0|11)\d\d?\d? [^0-9*#+pwn] $ */
|
|
143 |
/* 0 1 2 3 45 6 7 8 */
|
|
144 |
||
145 |
int state = 0; |
|
146 |
for (int i = 0 ; i < len ; i++ ) { |
|
147 |
char c = a.at(i).toLatin1(); |
|
148 |
||
149 |
switch (state) { |
|
150 |
case 0: |
|
151 |
if (c == '+') state = 1; |
|
152 |
else if (c == '0') state = 2; |
|
153 |
else if (isNonSeparator(c)) return false; |
|
154 |
break; |
|
155 |
||
156 |
case 2: |
|
157 |
if (c == '0') state = 3; |
|
158 |
else if (c == '1') state = 4; |
|
159 |
else if (isNonSeparator(c)) return false; |
|
160 |
break; |
|
161 |
||
162 |
case 4: |
|
163 |
if (c == '1') state = 5; |
|
164 |
else if (isNonSeparator(c)) return false; |
|
165 |
break; |
|
166 |
||
167 |
case 1: |
|
168 |
case 3: |
|
169 |
case 5: |
|
170 |
if (isISODigit(c)) state = 6; |
|
171 |
else if (isNonSeparator(c)) return false; |
|
172 |
break; |
|
173 |
||
174 |
case 6: |
|
175 |
case 7: |
|
176 |
if (isISODigit(c)) state++; |
|
177 |
else if (isNonSeparator(c)) return false; |
|
178 |
break; |
|
179 |
||
180 |
default: |
|
181 |
if (isNonSeparator(c)) return false; |
|
182 |
}
|
|
183 |
}
|
|
184 |
||
185 |
return state == 6 || state == 7 || state == 8; |
|
186 |
}
|
|
187 |
||
188 |
||
189 |
/**
|
|
190 |
* Compare phone numbers a and b, return true if they're identical
|
|
191 |
* enough for caller ID purposes.
|
|
192 |
*
|
|
193 |
* - Compares from right to left
|
|
194 |
* - requires MIN_MATCH (7) characters to match
|
|
195 |
* - handles common trunk prefixes and international prefixes
|
|
196 |
* (basically, everything except the Russian trunk prefix)
|
|
197 |
*
|
|
198 |
* Note that this method does not return false even when the two phone numbers
|
|
199 |
* are not exactly same; rather; we can call this method "similar()", not "equals()".
|
|
200 |
*
|
|
201 |
* @hide
|
|
202 |
*/
|
|
203 |
bool compareLoosely(const QString &a, const QString &b) |
|
204 |
{
|
|
205 |
int ia, ib; |
|
206 |
int matched; |
|
207 |
int numNonDialableCharsInA = 0; |
|
208 |
int numNonDialableCharsInB = 0; |
|
209 |
||
210 |
if (a.length() == 0 || b.length() == 0) { |
|
211 |
return false; |
|
212 |
}
|
|
213 |
||
214 |
if (a == b) { |
|
215 |
return true; |
|
216 |
}
|
|
217 |
||
218 |
ia = indexOfLastNetworkChar (a); |
|
219 |
ib = indexOfLastNetworkChar (b); |
|
220 |
matched = 0; |
|
221 |
||
222 |
while (ia >= 0 && ib >=0) { |
|
223 |
char ca, cb; |
|
224 |
bool skipCmp = false; |
|
225 |
||
226 |
ca = a.at(ia).toLatin1(); |
|
227 |
||
228 |
if (!isDialable(ca)) { |
|
229 |
ia--; |
|
230 |
skipCmp = true; |
|
231 |
numNonDialableCharsInA++; |
|
232 |
}
|
|
233 |
||
234 |
cb = b.at(ib).toLatin1(); |
|
235 |
||
236 |
if (!isDialable(cb)) { |
|
237 |
ib--; |
|
238 |
skipCmp = true; |
|
239 |
numNonDialableCharsInB++; |
|
240 |
}
|
|
241 |
||
242 |
if (!skipCmp) { |
|
243 |
if (cb != ca && ca != 'N' && cb != 'N') { |
|
244 |
break; |
|
245 |
}
|
|
246 |
ia--; ib--; matched++; |
|
247 |
}
|
|
248 |
}
|
|
249 |
||
250 |
if (matched < 7) { |
|
251 |
int effectiveALen = a.length() - numNonDialableCharsInA; |
|
252 |
int effectiveBLen = b.length() - numNonDialableCharsInB; |
|
253 |
||
254 |
||
255 |
// if the number of dialable chars in a and b match, but the matched chars < MIN_MATCH,
|
|
256 |
// treat them as equal (i.e. 404-04 and 40404)
|
|
257 |
if (effectiveALen == effectiveBLen && effectiveALen == matched) { |
|
258 |
return true; |
|
259 |
}
|
|
260 |
||
261 |
return false; |
|
262 |
}
|
|
263 |
||
264 |
// At least one string has matched completely;
|
|
265 |
if (matched >= 7 && (ia < 0 || ib < 0)) { |
|
266 |
return true; |
|
267 |
}
|
|
268 |
||
269 |
/*
|
|
270 |
* Now, what remains must be one of the following for a
|
|
271 |
* match:
|
|
272 |
*
|
|
273 |
* - a '+' on one and a '00' or a '011' on the other
|
|
274 |
* - a '0' on one and a (+,00)<country code> on the other
|
|
275 |
* (for this, a '0' and a '00' prefix would have succeeded above)
|
|
276 |
*/
|
|
277 |
||
278 |
if (matchIntlPrefix(a, ia + 1) |
|
279 |
&& matchIntlPrefix (b, ib +1) |
|
280 |
) { |
|
281 |
return true; |
|
282 |
}
|
|
283 |
||
284 |
if (matchTrunkPrefix(a, ia + 1) |
|
285 |
&& matchIntlPrefixAndCC(b, ib +1) |
|
286 |
) { |
|
287 |
return true; |
|
288 |
}
|
|
289 |
||
290 |
if (matchTrunkPrefix(b, ib + 1) |
|
291 |
&& matchIntlPrefixAndCC(a, ia +1) |
|
292 |
) { |
|
293 |
return true; |
|
294 |
}
|
|
295 |
||
296 |
return false; |
|
297 |
}
|
|
298 |
||
299 |
}
|
|
300 |
||
301 |
#endif
|