1. 程式人生 > >[android] android framework中的 PhoneNumberUtils 類詳解

[android] android framework中的 PhoneNumberUtils 類詳解

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;
            }
        }
    }

相關推薦

[android] android frameworkPhoneNumberUtils

PhoneNumberUtils.java (frameworks\base\telephony\java\android\telephony) package android.telephony; import com.android.i18n.phonenumbe

Java併發(十二):CAS Unsafe Atomic 說一說Java的Unsafe 說一說Java的Unsafe JavaUnsafe Unsafe與CAS

一、Unsafe Java無法直接訪問底層作業系統,而是通過本地(native)方法來訪問。不過儘管如此,JVM還是開了一個後門,JDK中有一個類Unsafe,它提供了硬體級別的原子操作。 這個類儘管裡面的方法都是public的,但是並沒有辦法使用它們,JDK API文件也沒有提供任何關於這個類的方法的解

C++string(轉載)(最下面有程式碼實現)

作者:yzl_rex 來源:CSDN 原文:https://blog.csdn.net/yzl_rex/article/details/7839379 要想使用標準C++中string類,必須要包含 #include < string>// 注意是< string>

JavaUnsafe

java不能直接訪問作業系統底層,而是通過本地方法來訪問。Unsafe類提供了硬體級別的原子操作,主要提供了以下功能: 1、通過Unsafe類可以分配記憶體,可以釋放記憶體; 類中提供的3個本地方法allocateMemory、reallocateMemory、freeM

javaString

String類 String類存在java.lang包中,專門儲存字串。是引用資料型別。 String類的兩種例項化方法 1.直接賦值 String str1= "hello"; 2.傳統賦值 Str

JavaClass、用法以及泛化

在前面我們將類的載入的時候,類載入一共有5步,載入,驗證,準備,解析和初始化。其中載入階段,除了將位元組碼載入到方法區,還生成了這個了的Java.lang.Class物件。那麼這個Class物件到底有什麼用呢? 前面的關於反射的文章,我們多次都用到了Class類,可以用這個

Javadimension

dimension - Java的一個類         dimension是Java的一個類,封裝了一個構件的高度和寬度,這個類與一個構件的許多屬性具有相關性,因此在Component類中定義多個與之有關的方法,LayoutManager介面也與一個Dimension

OpenCV矩陣之一:Mat

Mat::eye 返回一個恆等指定大小和型別矩陣。 C++: static MatExpr Mat::eye(int rows, int cols, inttype) C++: static MatExpr Mat::eye(Size size, int type) 引數 rows –的行數。

Unsafe--JavaUnsafe

java不能直接訪問作業系統底層,而是通過本地方法來訪問。Unsafe類提供了硬體級別的原子操作,主要提供了以下功能: 1、通過Unsafe類可以分配記憶體,可以釋放記憶體; 類中提供的3個本地方法allocateMemory、reallocateMemory、freeMemory分別用於分配記憶體,

JavaNumber

1.資料型別基本簡介 一般情況下我們會使用資料的基本資料型別:byte、int、short、long、double、float、boolean、char; 對應的包裝型別也有八種:Byte、Integ

JavaArrayList

1、什麼是ArrayList  ArrayList就是傳說中的動態陣列,用MSDN中的說法,就是Array的複雜版本,它提供瞭如下一些好處:  動態的增加和減少元素  實現了ICollection和IList介面  靈活的設定陣列的大小 2、如何使用ArrayList

androidwifi原理(轉)

二:Wifi模組的初始化:: 在 SystemServer 啟動的時候,會生成一個ConnectivityService的例項, try { Log.i(TAG, "Starting Connectivity Service."); ServiceManager.addService(Con

AndroidJNI使用(4)---Java與C之間資料型別轉換

Jni中基本型別轉換對應的表格 Java型別 本地型別 說明 boolean jboolean 無符號,8位 byte jbyte

AndroidJNI使用(3)---Android StudioSO檔案生成

Android中JNI使用詳解(2)---Android Studio中SO檔案生成 上一篇寫到過在Android Studio中配置NDK環境地址:Android Studio中NDK環境配置 這篇文章講解在Android Studio中

AndroidJNI使用(2)---Android StudioNDK環境配置

Android Studio中的NDK環境配置 1、下載NKD 在Android Studio中選擇File----Settings----Appearance&Behavior---System Settings----Andr

AndroidJNI使用(1)---EclipseNDK配置So檔案生成

1、NDK下載和配置 NDK下載地址:http://www.androiddevtools.cn/ NDK下載完成後,選擇Eclipse上方Window選單Preferences - Android - NDK 在NDK&nb

Android的Service

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

android studio使用svn

安裝SVN svn下載:https://tortoisesvn.net/downloads.html svn安裝: 需注意,需安裝 command line功能。 svn注意點:3.1. 需要重啟電腦,右鍵才會出現svn的各種選單。 3.2. 若是直接開啟Totos

androidwifi原理

                二:Wifi模組的初始化::在 SystemServer 啟動的時候,會生成一個ConnectivityService的例項,try {Log.i(TAG, "Starting Connectivity Service.");ServiceManager.addService(

AndroidSQLite應用

上次我向大家介紹了SQLite的基本資訊和使用過程,相信朋友們對SQLite已經有所瞭解了,那今天呢,我就和大家分享一下在Android中如何使用SQLite。 現在的主流移動裝置像Android、iPhone等都使用SQLite作為複雜資料的儲存引擎,在我們為移動裝置開發