Methods Summary |
---|
private static char | bcdToChar(byte b)returns 0 on invalid value
if (b < 0xa) {
return (char)('0" + b);
} else switch (b) {
case 0xa: return '*";
case 0xb: return '#";
case 0xc: return PAUSE;
case 0xd: return WILD;
default: return 0;
}
|
public static java.lang.String | calledPartyBCDFragmentToString(byte[] bytes, int offset, int length)Like calledPartyBCDToString, but field does not start with a
TOA byte. For example: SIM ADN extension fields
StringBuilder ret = new StringBuilder(length * 2);
internalCalledPartyBCDFragmentToString(ret, bytes, offset, length);
return ret.toString();
|
public static java.lang.String | calledPartyBCDToString(byte[] bytes, int offset, int length)3GPP TS 24.008 10.5.4.7
Called Party BCD Number
See Also TS 51.011 10.5.1 "dialing number/ssc string"
and TS 11.11 "10.3.1 EF adn (Abbreviated dialing numbers)"
boolean prependPlus = false;
StringBuilder ret = new StringBuilder(1 + length * 2);
if (length < 2) {
return "";
}
if ((bytes[offset] & 0xff) == TOA_International) {
prependPlus = true;
}
internalCalledPartyBCDFragmentToString(
ret, bytes, offset + 1, length - 1);
if (prependPlus && ret.length() == 0) {
// If the only thing there is a prepended plus, return ""
return "";
}
if (prependPlus) {
// This is an "international number" and should have
// a plus prepended to the dialing number. But there
// can also be Gsm MMI codes as defined in TS 22.030 6.5.2
// so we need to handle those also.
//
// http://web.telia.com/~u47904776/gsmkode.htm is a
// has a nice list of some of these GSM codes.
//
// Examples are:
// **21*+886988171479#
// **21*8311234567#
// *21#
// #21#
// *#21#
// *31#+11234567890
// #31#+18311234567
// #31#8311234567
// 18311234567
// +18311234567#
// +18311234567
// Odd ball cases that some phones handled
// where there is no dialing number so they
// append the "+"
// *21#+
// **21#+
String retString = ret.toString();
Pattern p = Pattern.compile("(^[#*])(.*)([#*])(.*)(#)$");
Matcher m = p.matcher(retString);
if (m.matches()) {
if ("".equals(m.group(2))) {
// Started with two [#*] ends with #
// So no dialing number and we'll just
// append a +, this handles **21#+
ret = new StringBuilder();
ret.append(m.group(1));
ret.append(m.group(3));
ret.append(m.group(4));
ret.append(m.group(5));
ret.append("+");
} else {
// Starts with [#*] and ends with #
// Assume group 4 is a dialing number
// such as *21*+1234554#
ret = new StringBuilder();
ret.append(m.group(1));
ret.append(m.group(2));
ret.append(m.group(3));
ret.append("+");
ret.append(m.group(4));
ret.append(m.group(5));
}
} else {
p = Pattern.compile("(^[#*])(.*)([#*])(.*)");
m = p.matcher(retString);
if (m.matches()) {
// Starts with [#*] and only one other [#*]
// Assume the data after last [#*] is dialing
// number (i.e. group 4) such as *31#+11234567890.
// This also includes the odd ball *21#+
ret = new StringBuilder();
ret.append(m.group(1));
ret.append(m.group(2));
ret.append(m.group(3));
ret.append("+");
ret.append(m.group(4));
} else {
// Does NOT start with [#*] just prepend '+'
ret = new StringBuilder();
ret.append('+");
ret.append(retString);
}
}
}
return ret.toString();
|
private static int | charToBCD(char c)
if (c >= '0" && c <= '9") {
return c - '0";
} else if (c == '*") {
return 0xa;
} else if (c == '#") {
return 0xb;
} else if (c == PAUSE) {
return 0xc;
} else if (c == WILD) {
return 0xd;
} else {
throw new RuntimeException ("invalid char for BCD " + c);
}
|
public static boolean | compare(java.lang.String a, java.lang.String b)Compare phone numbers a and b, return true if they're identical
enough for caller ID purposes.
- Compares from right to left
- requires MIN_MATCH (5) characters to match
- handles common trunk prefixes and international prefixes
(basically, everything except the Russian trunk prefix)
Tolerates nulls
int ia, ib;
int matched;
if (a == null || b == null) return a == b;
if (a.length() == 0 || b.length() == 0) {
return false;
}
ia = indexOfLastNetworkChar (a);
ib = indexOfLastNetworkChar (b);
matched = 0;
while (ia >= 0 && ib >=0) {
char ca, cb;
boolean skipCmp = false;
ca = a.charAt(ia);
if (!isDialable(ca)) {
ia--;
skipCmp = true;
}
cb = b.charAt(ib);
if (!isDialable(cb)) {
ib--;
skipCmp = true;
}
if (!skipCmp) {
if (cb != ca && ca != WILD && cb != WILD) {
break;
}
ia--; ib--; matched++;
}
}
if (matched < MIN_MATCH) {
int aLen = a.length();
// if the input strings match, but their lengths < MIN_MATCH,
// treat them as equal.
if (aLen == b.length() && aLen == matched) {
return true;
}
return false;
}
// At least one string has matched completely;
if (matched >= MIN_MATCH && (ia < 0 || ib < 0)) {
return true;
}
/*
* Now, what remains must be one of the following for a
* match:
*
* - a '+' on one and a '00' or a '011' on the other
* - a '0' on one and a (+,00)<country code> on the other
* (for this, a '0' and a '00' prefix would have succeeded above)
*/
if (matchIntlPrefix(a, ia + 1)
&& matchIntlPrefix (b, ib +1)
) {
return true;
}
if (matchTrunkPrefix(a, ia + 1)
&& matchIntlPrefixAndCC(b, ib +1)
) {
return true;
}
if (matchTrunkPrefix(b, ib + 1)
&& matchIntlPrefixAndCC(a, ia +1)
) {
return true;
}
return false;
|
public static java.lang.String | convertKeypadLettersToDigits(java.lang.String input)Translates any alphabetic letters (i.e. [A-Za-z]) in the
specified phone number into the equivalent numeric digits,
according to the phone keypad letter mapping described in
ITU E.161 and ISO/IEC 9995-8.
if (input == null) {
return input;
}
int len = input.length();
if (len == 0) {
return input;
}
char[] out = input.toCharArray();
for (int i = 0; i < len; i++) {
char c = out[i];
// If this char isn't in KEYPAD_MAP at all, just leave it alone.
out[i] = (char) KEYPAD_MAP.get(c, c);
}
return new String(out);
|
public static java.lang.String | extractNetworkPortion(java.lang.String phoneNumber)Extracts the network address portion and canonicalizes
(filters out separators.)
Network address portion is everything up to DTMF control digit
separators (pause or wait), but without non-dialable characters.
Please note that the GSM wild character is allowed in the result.
This must be resolved before dialing.
Allows + only in the first position in the result string.
Returns null if phoneNumber == null
if (phoneNumber == null) {
return null;
}
int len = phoneNumber.length();
StringBuilder ret = new StringBuilder(len);
boolean firstCharAdded = false;
for (int i = 0; i < len; i++) {
char c = phoneNumber.charAt(i);
if (isDialable(c) && (c != '+" || !firstCharAdded)) {
firstCharAdded = true;
ret.append(c);
} else if (isStartsPostDial (c)) {
break;
}
}
return ret.toString();
|
public static java.lang.String | extractPostDialPortion(java.lang.String phoneNumber)Extracts the post-dial sequence of DTMF control digits, pauses, and
waits. Strips separators. This string may be empty, but will not be null
unless phoneNumber == null.
Returns null if phoneNumber == null
if (phoneNumber == null) return null;
int trimIndex;
StringBuilder ret = new StringBuilder();
trimIndex = indexOfLastNetworkChar (phoneNumber);
for (int i = trimIndex + 1, s = phoneNumber.length()
; i < s; i++
) {
char c = phoneNumber.charAt(i);
if (isNonSeparator(c)) {
ret.append(c);
}
}
return ret.toString();
|
public static void | formatJapaneseNumber(android.text.Editable text)Formats a phone number in-place using the Japanese formatting rules.
Numbers will be formatted as:
03-xxxx-xxxx
090-xxxx-xxxx
0120-xxx-xxx
+81-3-xxxx-xxxx
+81-90-xxxx-xxxx
JapanesePhoneNumberFormatter.format(text);
|
public static void | formatNanpNumber(android.text.Editable text)Formats a phone number in-place using the NANP formatting rules. Numbers will be formatted
as:
xxx-xxxx
xxx-xxx-xxxx
1-xxx-xxx-xxxx
+1-xxx-xxx-xxxx
int length = text.length();
if (length > "+1-nnn-nnn-nnnn".length()) {
// The string is too long to be formatted
return;
}
CharSequence saved = text.subSequence(0, length);
// Strip the dashes first, as we're going to add them back
int p = 0;
while (p < text.length()) {
if (text.charAt(p) == '-") {
text.delete(p, p + 1);
} else {
p++;
}
}
length = text.length();
// When scanning the number we record where dashes need to be added,
// if they're non-0 at the end of the scan the dashes will be added in
// the proper places.
int dashPositions[] = new int[3];
int numDashes = 0;
int state = NANP_STATE_DIGIT;
int numDigits = 0;
for (int i = 0; i < length; i++) {
char c = text.charAt(i);
switch (c) {
case '1":
if (numDigits == 0 || state == NANP_STATE_PLUS) {
state = NANP_STATE_ONE;
break;
}
// fall through
case '2":
case '3":
case '4":
case '5":
case '6":
case '7":
case '8":
case '9":
case '0":
if (state == NANP_STATE_PLUS) {
// Only NANP number supported for now
text.replace(0, length, saved);
return;
} else if (state == NANP_STATE_ONE) {
// Found either +1 or 1, follow it up with a dash
dashPositions[numDashes++] = i;
} else if (state != NANP_STATE_DASH && (numDigits == 3 || numDigits == 6)) {
// Found a digit that should be after a dash that isn't
dashPositions[numDashes++] = i;
}
state = NANP_STATE_DIGIT;
numDigits++;
break;
case '-":
state = NANP_STATE_DASH;
break;
case '+":
if (i == 0) {
// Plus is only allowed as the first character
state = NANP_STATE_PLUS;
break;
}
// Fall through
default:
// Unknown character, bail on formatting
text.replace(0, length, saved);
return;
}
}
if (numDigits == 7) {
// With 7 digits we want xxx-xxxx, not xxx-xxx-x
numDashes--;
}
// Actually put the dashes in place
for (int i = 0; i < numDashes; i++) {
int pos = dashPositions[i];
text.replace(pos + i, pos + i, "-");
}
// Remove trailing dashes
int len = text.length();
while (len > 0) {
if (text.charAt(len - 1) == '-") {
text.delete(len - 1, len);
len--;
} else {
break;
}
}
|
public static java.lang.String | formatNumber(java.lang.String source)Breaks the given number down and formats it according to the rules
for the country the number is from.
SpannableStringBuilder text = new SpannableStringBuilder(source);
formatNumber(text, getFormatTypeForLocale(Locale.getDefault()));
return text.toString();
|
public static void | formatNumber(android.text.Editable text, int defaultFormattingType)Formats a phone number in-place. Currently only supports NANP formatting.
int formatType = defaultFormattingType;
if (text.length() > 2 && text.charAt(0) == '+") {
if (text.charAt(1) == '1") {
formatType = FORMAT_NANP;
} else if (text.length() >= 3 && text.charAt(1) == '8"
&& text.charAt(2) == '1") {
formatType = FORMAT_JAPAN;
} else {
return;
}
}
switch (formatType) {
case FORMAT_NANP:
formatNanpNumber(text);
return;
case FORMAT_JAPAN:
formatJapaneseNumber(text);
return;
}
|
public static int | getFormatTypeForLocale(java.util.Locale locale)Returns the phone number formatting type for the given locale.
String country = locale.getCountry();
// Check for the NANP countries
int length = NANP_COUNTRIES.length;
for (int i = 0; i < length; i++) {
if (NANP_COUNTRIES[i].equals(country)) {
return FORMAT_NANP;
}
}
if (locale.equals(Locale.JAPAN)) {
return FORMAT_JAPAN;
}
return FORMAT_UNKNOWN;
|
public static java.lang.String | getNumberFromIntent(android.content.Intent intent, android.content.Context context)Extracts the phone number from an Intent.
String number = null;
Uri uri = intent.getData();
String scheme = uri.getScheme();
if (scheme.equals("tel")) {
return uri.getSchemeSpecificPart();
}
if (scheme.equals("voicemail")) {
return TelephonyManager.getDefault().getVoiceMailNumber();
}
if (context == null) {
return null;
}
String type = intent.resolveType(context);
Cursor c = context.getContentResolver().query(
uri, new String[]{ Contacts.People.Phones.NUMBER },
null, null, null);
if (c != null) {
try {
if (c.moveToFirst()) {
number = c.getString(
c.getColumnIndex(Contacts.People.Phones.NUMBER));
}
} finally {
c.close();
}
}
return number;
|
public static java.lang.String | getStrippedReversed(java.lang.String phoneNumber)Returns the network portion reversed.
This string is intended to go into an index column for a
database lookup.
Returns null if phoneNumber == null
String np = extractNetworkPortion(phoneNumber);
if (np == null) return null;
return internalGetStrippedReversed(np, np.length());
|
private static boolean | hasPlus(java.lang.String s)
return s.indexOf('+") >= 0;
|
private static int | indexOfLastNetworkChar(java.lang.String a)index of the last character of the network portion
(eg anything after is a post-dial string)
int pIndex, wIndex;
int origLength;
int trimIndex;
origLength = a.length();
pIndex = a.indexOf(PAUSE);
wIndex = a.indexOf(WAIT);
trimIndex = minPositive(pIndex, wIndex);
if (trimIndex < 0) {
return origLength - 1;
} else {
return trimIndex - 1;
}
|
private static void | internalCalledPartyBCDFragmentToString(java.lang.StringBuilder sb, byte[] bytes, int offset, int length)
for (int i = offset ; i < length + offset ; i++) {
byte b;
char c;
c = bcdToChar((byte)(bytes[i] & 0xf));
if (c == 0) {
return;
}
sb.append(c);
// FIXME(mkf) TS 23.040 9.1.2.3 says
// "if a mobile receives 1111 in a position prior to
// the last semi-octet then processing shall commense with
// the next semi-octet and the intervening
// semi-octet shall be ignored"
// How does this jive with 24,008 10.5.4.7
b = (byte)((bytes[i] >> 4) & 0xf);
if (b == 0xf && i + 1 == length + offset) {
//ignore final 0xf
break;
}
c = bcdToChar(b);
if (c == 0) {
return;
}
sb.append(c);
}
|
private static java.lang.String | internalGetStrippedReversed(java.lang.String np, int numDigits)Returns the last numDigits of the reversed phone number
Returns null if np == null
if (np == null) return null;
StringBuilder ret = new StringBuilder(numDigits);
int length = np.length();
for (int i = length - 1, s = length
; i >= 0 && (s - i) <= numDigits ; i--
) {
char c = np.charAt(i);
ret.append(c);
}
return ret.toString();
|
public static final boolean | is12Key(char c)True if c is ISO-LATIN characters 0-9, *, #
return (c >= '0" && c <= '9") || c == '*" || c == '#";
|
private static boolean | isDialable(java.lang.String address)
for (int i = 0, count = address.length(); i < count; i++) {
if (!isDialable(address.charAt(i))) {
return false;
}
}
return true;
|
public static final boolean | isDialable(char c)True if c is ISO-LATIN characters 0-9, *, # , +, WILD
return (c >= '0" && c <= '9") || c == '*" || c == '#" || c == '+" || c == WILD;
|
public static boolean | isEmergencyNumber(java.lang.String number)isEmergencyNumber: checks a given number against the list of
emergency numbers provided by the RIL and SIM card.
// Strip the separators from the number before comparing it
// to the list.
number = extractNetworkPortion(number);
// retrieve the list of emergency numbers
String numbers = SystemProperties.get("ro.ril.ecclist");
if (!TextUtils.isEmpty(numbers)) {
// searches through the comma-separated list for a match,
// return true if one is found.
for (String emergencyNum : numbers.split(",")) {
if (emergencyNum.equals(number)) {
return true;
}
}
// no matches found against the list!
return false;
}
//no ecclist system property, so use our own list.
return (number.equals("112") || number.equals("911"));
|
public static boolean | isGlobalPhoneNumber(java.lang.String phoneNumber)
if (TextUtils.isEmpty(phoneNumber)) {
return false;
}
Matcher match = GLOBAL_PHONE_NUMBER_PATTERN.matcher(phoneNumber);
return match.matches();
|
public static boolean | isISODigit(char c)True if c is ISO-LATIN characters 0-9
return c >= '0" && c <= '9";
|
public static final boolean | isNonSeparator(char c)True if c is ISO-LATIN characters 0-9, *, # , +, WILD, WAIT, PAUSE
return (c >= '0" && c <= '9") || c == '*" || c == '#" || c == '+"
|| c == WILD || c == WAIT || c == PAUSE;
|
public static final boolean | isReallyDialable(char c)True if c is ISO-LATIN characters 0-9, *, # , + (no WILD)
return (c >= '0" && c <= '9") || c == '*" || c == '#" || c == '+";
|
public static final boolean | isStartsPostDial(char c)This any anything to the right of this char is part of the
post-dial string (eg this is PAUSE or WAIT)
return c == PAUSE || c == WAIT;
|
public static boolean | isWellFormedSmsAddress(java.lang.String address)Return true iff the network portion of address is,
as far as we can tell on the device, suitable for use as an SMS
destination address.
String networkPortion =
PhoneNumberUtils.extractNetworkPortion(address);
return (!(networkPortion.equals("+")
|| TextUtils.isEmpty(networkPortion)))
&& isDialable(networkPortion);
|
private static boolean | matchIntlPrefix(java.lang.String a, int len)all of a up to len must be an international prefix or
separators/non-dialing digits
/* '([^0-9*#+pwn]\+[^0-9*#+pwn] | [^0-9*#+pwn]0(0|11)[^0-9*#+pwn] )$' */
/* 0 1 2 3 45 */
int state = 0;
for (int i = 0 ; i < len ; i++) {
char c = a.charAt(i);
switch (state) {
case 0:
if (c == '+") state = 1;
else if (c == '0") state = 2;
else if (isNonSeparator(c)) return false;
break;
case 2:
if (c == '0") state = 3;
else if (c == '1") state = 4;
else if (isNonSeparator(c)) return false;
break;
case 4:
if (c == '1") state = 5;
else if (isNonSeparator(c)) return false;
break;
default:
if (isNonSeparator(c)) return false;
break;
}
}
return state == 1 || state == 3 || state == 5;
|
private static boolean | matchIntlPrefixAndCC(java.lang.String a, int len)all of 'a' up to len must be a (+|00|011)country code)
We're fast and loose with the country code. Any \d{1,3} matches
/* [^0-9*#+pwn]*(\+|0(0|11)\d\d?\d? [^0-9*#+pwn] $ */
/* 0 1 2 3 45 6 7 8 */
int state = 0;
for (int i = 0 ; i < len ; i++ ) {
char c = a.charAt(i);
switch (state) {
case 0:
if (c == '+") state = 1;
else if (c == '0") state = 2;
else if (isNonSeparator(c)) return false;
break;
case 2:
if (c == '0") state = 3;
else if (c == '1") state = 4;
else if (isNonSeparator(c)) return false;
break;
case 4:
if (c == '1") state = 5;
else if (isNonSeparator(c)) return false;
break;
case 1:
case 3:
case 5:
if (isISODigit(c)) state = 6;
else if (isNonSeparator(c)) return false;
break;
case 6:
case 7:
if (isISODigit(c)) state++;
else if (isNonSeparator(c)) return false;
break;
default:
if (isNonSeparator(c)) return false;
}
}
return state == 6 || state == 7 || state == 8;
|
private static boolean | matchTrunkPrefix(java.lang.String a, int len)all of 'a' up to len must match non-US trunk prefix ('0')
boolean found;
found = false;
for (int i = 0 ; i < len ; i++) {
char c = a.charAt(i);
if (c == '0" && !found) {
found = true;
} else if (isNonSeparator(c)) {
return false;
}
}
return found;
|
private static int | minPositive(int a, int b)or -1 if both are negative
if (a >= 0 && b >= 0) {
return (a < b) ? a : b;
} else if (a >= 0) { /* && b < 0 */
return a;
} else if (b >= 0) { /* && a < 0 */
return b;
} else { /* a < 0 && b < 0 */
return -1;
}
|
public static byte[] | networkPortionToCalledPartyBCD(java.lang.String s)Note: calls extractNetworkPortion(), so do not use for
SIM EF[ADN] style records
Exceptions thrown if extractNetworkPortion(s).length() == 0
return numberToCalledPartyBCD(extractNetworkPortion(s));
|
public static byte[] | networkPortionToCalledPartyBCDWithLength(java.lang.String s)Same as {@link #networkPortionToCalledPartyBCD}, but includes a
one-byte length prefix.
return numberToCalledPartyBCDWithLength(extractNetworkPortion(s));
|
public static byte[] | numberToCalledPartyBCD(java.lang.String number)Convert a dialing number to BCD byte array
// The extra byte required for '+' is taken into consideration while calculating
// length of ret.
int size = (hasPlus(number) ? number.length() - 1 : number.length());
byte[] ret = new byte[(size + 1) / 2 + 1];
return numberToCalledPartyBCDHelper(ret, 0, number);
|
private static byte[] | numberToCalledPartyBCDHelper(byte[] ret, int offset, java.lang.String number)
if (hasPlus(number)) {
number = number.replaceAll("\\+", "");
ret[offset] = (byte) TOA_International;
} else {
ret[offset] = (byte) TOA_Unknown;
}
int size = number.length();
int curChar = 0;
int countFullBytes = ret.length - offset - 1 - ((size - curChar) & 1);
for (int i = 1; i < 1 + countFullBytes; i++) {
ret[offset + i]
= (byte) ((charToBCD(number.charAt(curChar++)))
| (charToBCD(number.charAt(curChar++))) << 4);
}
// The left-over octet for odd-length phone numbers should be
// filled with 0xf.
if (countFullBytes + offset < ret.length - 1) {
ret[ret.length - 1]
= (byte) (charToBCD(number.charAt(curChar))
| (0xf << 4));
}
return ret;
|
private static byte[] | numberToCalledPartyBCDWithLength(java.lang.String number)Same as {@link #numberToCalledPartyBCD}, but includes a
one-byte length prefix.
// The extra byte required for '+' is taken into consideration while calculating
// length of ret.
int size = (hasPlus(number) ? number.length() - 1 : number.length());
int length = (size + 1) / 2 + 1;
byte[] ret = new byte[length + 1];
ret[0] = (byte) (length & 0xff);
return numberToCalledPartyBCDHelper(ret, 1, number);
|
public static java.lang.String | stringFromStringAndTOA(java.lang.String s, int TOA)Basically: makes sure there's a + in front of a
TOA_International number
Returns null if s == null
if (s == null) return null;
if (TOA == TOA_International && s.length() > 0 && s.charAt(0) != '+") {
return "+" + s;
}
return s;
|
public static java.lang.String | stripSeparators(java.lang.String phoneNumber)Strips separators from a phone number string.
if (phoneNumber == null) {
return null;
}
int len = phoneNumber.length();
StringBuilder ret = new StringBuilder(len);
for (int i = 0; i < len; i++) {
char c = phoneNumber.charAt(i);
if (isNonSeparator(c)) {
ret.append(c);
}
}
return ret.toString();
|
public static java.lang.String | toCallerIDMinMatch(java.lang.String phoneNumber)Returns the rightmost MIN_MATCH (5) characters in the network portion
in *reversed* order
This can be used to do a database lookup against the column
that stores getStrippedReversed()
Returns null if phoneNumber == null
String np = extractNetworkPortion(phoneNumber);
return internalGetStrippedReversed(np, MIN_MATCH);
|
public static int | toaFromString(java.lang.String s)Returns the TOA for the given dial string
Basically, returns TOA_International if there's a + prefix
if (s != null && s.length() > 0 && s.charAt(0) == '+") {
return TOA_International;
}
return TOA_Unknown;
|