您的位置:首页 > 移动开发 > Android开发

[android] android framework中的 PhoneNumberUtils 类详解

2013-07-25 23:53 519 查看
PhoneNumberUtils.java (frameworks\base\telephony\java\android\telephony)



package android.telephony;

import com.android.i18n.phonenumbers.NumberParseException;

import com.android.i18n.phonenumbers.PhoneNumberUtil;

import com.android.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat;

import com.android.i18n.phonenumbers.Phonenumber.PhoneNumber;

import com.android.i18n.phonenumbers.ShortNumberUtil;

import com.android.i18n.phonenumbers.Phonemetadata.PhoneMetadata;

import android.content.Context;

import android.content.Intent;

import android.database.Cursor;

import android.location.CountryDetector;

import android.net.Uri;

import android.os.SystemProperties;

import android.provider.Contacts;

import android.provider.ContactsContract;

import android.text.Editable;

import android.text.SpannableStringBuilder;

import android.text.TextUtils;

import android.util.Log;

import android.util.SparseIntArray;

import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY;

import static com.android.internal.telephony.TelephonyProperties.PROPERTY_IDP_STRING;

import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY;

import java.util.Locale;

import java.util.regex.Matcher;

import java.util.regex.Pattern;

/// M : [mtk04070][111116][ALPS00093395]Use Gemini object @{

import static com.android.internal.telephony.PhoneConstants.GEMINI_SIM_1;

import static com.android.internal.telephony.PhoneConstants.GEMINI_DEFAULT_SIM_PROP;

import static com.android.internal.telephony.PhoneConstants.GEMINI_SIM_ID_KEY;

import java.util.regex.Pattern;

import java.util.regex.Matcher;

import com.mediatek.common.featureoption.FeatureOption;

import com.mediatek.common.MediatekClassFactory;

import com.mediatek.common.telephony.IPhoneNumberExt;

import com.android.internal.telephony.PhoneConstants;

/// @}

import android.os.ServiceManager;

import com.android.internal.telephony.ITelephony;

/**

* Various utilities for dealing with phone number strings.

*/

public class PhoneNumberUtils









/**

* Returns the last numDigits of the reversed phone number

* Returns null if np == null

*/

private static String

internalGetStrippedReversed(String np, int numDigits) {

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();

}

/**

* Basically: makes sure there's a + in front of a

* TOA_International number

*

* Returns null if s == null

*/

public static String

stringFromStringAndTOA(String s, int TOA) {

if (s == null) return null;

if (TOA == TOA_International && s.length() > 0 && s.charAt(0) != '+') {

return "+" + s;

}

return s;

}

/**

* Returns the TOA for the given dial string

* Basically, returns TOA_International if there's a + prefix

*/

public static int

toaFromString(String s) {

if (s != null && s.length() > 0 && s.charAt(0) == '+') {

return TOA_International;

}

return TOA_Unknown;

}

/**

* 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)"

*

* @param bytes the data buffer

* @param offset should point to the TOA (aka. TON/NPI) octet after the length byte

* @param length is the number of bytes including TOA byte

* and must be at least 2

*

* @return partial string on invalid decode

*

* FIXME(mkf) support alphanumeric address type

* currently implemented in SMSMessage.getAddress()

*/

public static String calledPartyBCDToString
(byte[] bytes, int offset, int length) {

boolean prependPlus = false;

StringBuilder ret = new StringBuilder(1 + length * 2);

if (length < 2) {

return "";

}

//Only TON field should be taken in consideration

if ((bytes[offset] & 0xf0) == (TOA_International & 0xf0)) {

prependPlus = true;

}

// 解析 byte中的 字符 取出BCD ,编码的秘密就在这个函数里面

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) {

/// M: [mtk04070][111116][ALPS00093395]Replace origin codes with prependPlusToNumber method. @{

ret = new StringBuilder(mPhoneNumberExt.prependPlusToNumber(ret.toString()));

/// @}

}

return ret.toString(); // 返回解析得到的字符串

}



private static void internalCalledPartyBCDFragmentToString( 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 commence 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);

}

}

/**

* Like calledPartyBCDToString, but field does not start with a

* TOA byte. For example: SIM ADN extension fields

*/

public static String

calledPartyBCDFragmentToString(byte [] bytes, int offset, int length) {

StringBuilder ret = new StringBuilder(length * 2);

internalCalledPartyBCDFragmentToString(ret, bytes, offset, length);

return ret.toString();

}

/** returns 0 on invalid value */

private static char

bcdToChar(byte b) {

if (b < 0xa) {

return (char)('0' + b);

} else switch (b) {

case 0xa: return '*';

case 0xb: return '#';

case 0xc: return PAUSE;

case 0xd: return WILD;

/// M: add wait for ANR @{

case 0xe: return WAIT;

/// @}

default: return 0;

}

}

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;

/// M: add wait for ANR @{

} else if (c == WAIT) {

return 0xe;

/// @}

} else {

throw new RuntimeException ("invalid char for BCD " + c);

}

}

/**

* Return true iff the network portion of <code>address</code> is,

* as far as we can tell on the device, suitable for use as an SMS

* destination address.

*/

public static boolean isWellFormedSmsAddress(String address) {

/// M: [mtk04070][120104][ALPS00109412]Solve "can't send MMS with MSISDN in international format". @{

//Merge from ALPS00089029

//if (!isDialable(address)) {

// return false;

//}

/// @}

String networkPortion =

PhoneNumberUtils.extractNetworkPortion(address);

return (!(networkPortion.equals("+")

|| TextUtils.isEmpty(networkPortion)))

&& isDialable(networkPortion);

}

public static boolean isGlobalPhoneNumber(String phoneNumber) {

if (TextUtils.isEmpty(phoneNumber)) {

return false;

}

Matcher match = GLOBAL_PHONE_NUMBER_PATTERN.matcher(phoneNumber);

return match.matches();

}

private static boolean isDialable(String address) {

for (int i = 0, count = address.length(); i < count; i++) {

if (!isDialable(address.charAt(i))) {

return false;

}

}

return true;

}

private static boolean isNonSeparator(String address) {

for (int i = 0, count = address.length(); i < count; i++) {

if (!isNonSeparator(address.charAt(i))) {

return false;

}

}

return true;

}

/**

* Note: calls extractNetworkPortion(), so do not use for

* SIM EF[ADN] style records

*

* Returns null if network portion is empty.

*/

public static byte[]

networkPortionToCalledPartyBCD(String s) {

String networkPortion = extractNetworkPortion(s);

return numberToCalledPartyBCDHelper(networkPortion, false);

}

/**

* Same as {@link #networkPortionToCalledPartyBCD}, but includes a

* one-byte length prefix.

*/

public static byte[]

networkPortionToCalledPartyBCDWithLength(String s) {

String networkPortion = extractNetworkPortion(s);

return numberToCalledPartyBCDHelper(networkPortion, true);

}

/**

* Convert a dialing number to BCD byte array

*

* @param number dialing number string

* if the dialing number starts with '+', set to international TOA

* @return BCD byte array

*/

public static byte[]

numberToCalledPartyBCD(String number) {

return numberToCalledPartyBCDHelper(number, false);

}

/**

* If includeLength is true, prepend a one-byte length value to

* the return array.

*/

private static byte[]

numberToCalledPartyBCDHelper(String number, boolean includeLength) {

int numberLenReal = number.length();

int numberLenEffective = numberLenReal;

boolean hasPlus = number.indexOf('+') != -1;

if (hasPlus) numberLenEffective--;

if (numberLenEffective == 0) return null;

int resultLen = (numberLenEffective + 1) / 2; // Encoded numbers require only 4 bits each.

int extraBytes = 1; // Prepended TOA byte.

if (includeLength) extraBytes++; // Optional prepended length byte.

resultLen += extraBytes;

byte[] result = new byte[resultLen];

int digitCount = 0;

for (int i = 0; i < numberLenReal; i++) {

char c = number.charAt(i);

if (c == '+') continue;

int shift = ((digitCount & 0x01) == 1) ? 4 : 0;

result[extraBytes + (digitCount >> 1)] |= (byte)((charToBCD(c) & 0x0F) << shift);

digitCount++;

}

// 1-fill any trailing odd nibble/quartet.

if ((digitCount & 0x01) == 1) result[extraBytes + (digitCount >> 1)] |= 0xF0;

int offset = 0;

if (includeLength) result[offset++] = (byte)(resultLen - 1);

result[offset] = (byte)(hasPlus ? TOA_International : TOA_Unknown);

return result;

}

//================ Number formatting =========================

/** The current locale is unknown, look for a country code or don't format */

public static final int FORMAT_UNKNOWN = 0;

/** NANP formatting */

public static final int FORMAT_NANP = 1;

/** Japanese formatting */

public static final int FORMAT_JAPAN = 2;

/** List of country codes for countries that use the NANP */

private static final String[] NANP_COUNTRIES = new String[] {

"US", // United States

"CA", // Canada

"AS", // American Samoa

"AI", // Anguilla

"AG", // Antigua and Barbuda

"BS", // Bahamas

"BB", // Barbados

"BM", // Bermuda

"VG", // British Virgin Islands

"KY", // Cayman Islands

"DM", // Dominica

"DO", // Dominican Republic

"GD", // Grenada

"GU", // Guam

"JM", // Jamaica

"PR", // Puerto Rico

"MS", // Montserrat

"MP", // Northern Mariana Islands

"KN", // Saint Kitts and Nevis

"LC", // Saint Lucia

"VC", // Saint Vincent and the Grenadines

"TT", // Trinidad and Tobago

"TC", // Turks and Caicos Islands

"VI", // U.S. Virgin Islands

};

/**

* Breaks the given number down and formats it according to the rules

* for the country the number is from.

*

* @param source The phone number to format

* @return A locally acceptable formatting of the input, or the raw input if

* formatting rules aren't known for the number

*/

public static String formatNumber(String source) {

SpannableStringBuilder text = new SpannableStringBuilder(source);

formatNumber(text, getFormatTypeForLocale(Locale.getDefault()));

return text.toString();

}

/**

* Formats the given number with the given formatting type. Currently

* {@link #FORMAT_NANP} and
{@link #FORMAT_JAPAN} are supported as a formating type.

*

* @param source the phone number to format

* @param defaultFormattingType The default formatting rules to apply if the number does

* not begin with +[country_code]

* @return The phone number formatted with the given formatting type.

*

* @hide TODO: Should be unhidden.

*/

public static String formatNumber(String source, int defaultFormattingType) {

SpannableStringBuilder text = new SpannableStringBuilder(source);

formatNumber(text, defaultFormattingType);

return text.toString();

}

/**

* Returns the phone number formatting type for the given locale.

*

* @param locale The locale of interest, usually {@link Locale#getDefault()}

* @return The formatting type for the given locale, or FORMAT_UNKNOWN if the formatting

* rules are not known for the given locale

*/

public static int getFormatTypeForLocale(Locale locale) {

String country = locale.getCountry();

return getFormatTypeFromCountryCode(country);

}

/**

* Formats a phone number in-place. Currently {@link #FORMAT_JAPAN} and{@link #FORMAT_NANP}

* is supported as a second argument.

*

* @param text The number to be formatted, will be modified with the formatting

* @param defaultFormattingType The default formatting rules to apply if the number does

* not begin with +[country_code]

*/

public static void formatNumber(Editable text, int defaultFormattingType) {

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 {

formatType = FORMAT_UNKNOWN;

}

}

switch (formatType) {

case FORMAT_NANP:

formatNanpNumber(text);

return;

case FORMAT_JAPAN:

formatJapaneseNumber(text);

return;

case FORMAT_UNKNOWN:

removeDashes(text);

return;

}

}

private static final int NANP_STATE_DIGIT = 1;

private static final int NANP_STATE_PLUS = 2;

private static final int NANP_STATE_ONE = 3;

private static final int NANP_STATE_DASH = 4;

/**

* Formats a phone number in-place using the NANP formatting rules. Numbers will be formatted

* as:

*

* <p><code>

* xxxxx

* xxx-xxxx

* xxx-xxx-xxxx

* 1-xxx-xxx-xxxx

* +1-xxx-xxx-xxxx

* </code></p>

*

* @param text the number to be formatted, will be modified with the formatting

*/

public static void formatNanpNumber(Editable text) {

int length = text.length();

if (length > "+1-nnn-nnn-nnnn".length()) {

// The string is too long to be formatted

return;

} else if (length <= 5) {

// The string is either a shortcode or too short to be formatted

return;

}

CharSequence saved = text.subSequence(0, length);

// Strip the dashes first, as we're going to add them back

removeDashes(text);

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;

}

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: