Home | History | Annotate | Download | only in util
      1 /*
      2  * Copyright (C) 2013 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 package com.android.contacts.common.util;
     17 
     18 import android.content.Context;
     19 import android.telephony.PhoneNumberUtils;
     20 import android.text.TextUtils;
     21 import android.util.Log;
     22 
     23 import com.google.i18n.phonenumbers.NumberParseException;
     24 import com.google.i18n.phonenumbers.PhoneNumberUtil;
     25 import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber;
     26 import com.google.i18n.phonenumbers.ShortNumberInfo;
     27 
     28 import java.util.Locale;
     29 
     30 /**
     31  * This class wraps several PhoneNumberUtil calls and TelephonyManager calls. Some of them are
     32  * the same as the ones in the framework's code base. We can remove those once they are part of
     33  * the public API.
     34  */
     35 public class PhoneNumberHelper {
     36 
     37     private static final String LOG_TAG = PhoneNumberHelper.class.getSimpleName();
     38 
     39     /**
     40      * Determines if the specified number is actually a URI (i.e. a SIP address) rather than a
     41      * regular PSTN phone number, based on whether or not the number contains an "@" character.
     42      *
     43      * @param number Phone number
     44      * @return true if number contains @
     45      *
     46      * TODO: Remove if PhoneNumberUtils.isUriNumber(String number) is made public.
     47      */
     48     public static boolean isUriNumber(String number) {
     49         // Note we allow either "@" or "%40" to indicate a URI, in case
     50         // the passed-in string is URI-escaped.  (Neither "@" nor "%40"
     51         // will ever be found in a legal PSTN number.)
     52         return number != null && (number.contains("@") || number.contains("%40"));
     53     }
     54 
     55     /**
     56      * Formats the phone number only if the given number hasn't been formatted.
     57      * <p>
     58      * The number which has only dailable character is treated as not being
     59      * formatted.
     60      *
     61      * @param phoneNumber the number to be formatted.
     62      * @param phoneNumberE164 The E164 format number whose country code is used if the given
     63      * phoneNumber doesn't have the country code.
     64      * @param defaultCountryIso The ISO 3166-1 two letters country code whose convention will
     65      * be used if the phoneNumberE164 is null or invalid, or if phoneNumber contains IDD.
     66      * @return The formatted number if the given number has been formatted, otherwise, return the
     67      * given number.
     68      *
     69      * TODO: Remove if PhoneNumberUtils.formatNumber(String phoneNumber, String phoneNumberE164,
     70      * String defaultCountryIso) is made public.
     71      */
     72     public static String formatNumber(
     73             String phoneNumber, String phoneNumberE164, String defaultCountryIso) {
     74         int len = phoneNumber.length();
     75         for (int i = 0; i < len; i++) {
     76             if (!PhoneNumberUtils.isDialable(phoneNumber.charAt(i))) {
     77                 return phoneNumber;
     78             }
     79         }
     80         PhoneNumberUtil util = PhoneNumberUtil.getInstance();
     81         // Get the country code from phoneNumberE164
     82         if (phoneNumberE164 != null && phoneNumberE164.length() >= 2
     83                 && phoneNumberE164.charAt(0) == '+') {
     84             try {
     85                 // The number to be parsed is in E164 format, so the default region used doesn't
     86                 // matter.
     87                 PhoneNumber pn = util.parse(phoneNumberE164, "ZZ");
     88                 String regionCode = util.getRegionCodeForNumber(pn);
     89                 if (!TextUtils.isEmpty(regionCode) &&
     90                         // This makes sure phoneNumber doesn't contain an IDD
     91                         normalizeNumber(phoneNumber).indexOf(phoneNumberE164.substring(1)) <= 0) {
     92                     defaultCountryIso = regionCode;
     93                 }
     94             } catch (NumberParseException e) {
     95                 Log.w(LOG_TAG, "The number could not be parsed in E164 format!");
     96             }
     97         }
     98 
     99         String result = formatNumber(phoneNumber, defaultCountryIso);
    100         return result == null ? phoneNumber : result;
    101     }
    102 
    103     /**
    104      * Format a phone number.
    105      * <p>
    106      * If the given number doesn't have the country code, the phone will be
    107      * formatted to the default country's convention.
    108      *
    109      * @param phoneNumber The number to be formatted.
    110      * @param defaultCountryIso The ISO 3166-1 two letters country code whose convention will
    111      * be used if the given number doesn't have the country code.
    112      * @return The formatted number, or null if the given number is not valid.
    113      *
    114      * TODO: Remove if PhoneNumberUtils.formatNumber(String phoneNumber, String defaultCountryIso)
    115      * is made public.
    116      */
    117     public static String formatNumber(String phoneNumber, String defaultCountryIso) {
    118         // Do not attempt to format numbers that start with a hash or star symbol.
    119         if (phoneNumber.startsWith("#") || phoneNumber.startsWith("*")) {
    120             return phoneNumber;
    121         }
    122 
    123         final PhoneNumberUtil util = PhoneNumberUtil.getInstance();
    124         String result = null;
    125         try {
    126             PhoneNumber pn = util.parseAndKeepRawInput(phoneNumber, defaultCountryIso);
    127             result = util.formatInOriginalFormat(pn, defaultCountryIso);
    128         } catch (NumberParseException e) {
    129             Log.w(LOG_TAG, "Number could not be parsed with the given country code!");
    130         }
    131         return result;
    132     }
    133 
    134     /**
    135      * Normalize a phone number by removing the characters other than digits. If
    136      * the given number has keypad letters, the letters will be converted to
    137      * digits first.
    138      *
    139      * @param phoneNumber The number to be normalized.
    140      * @return The normalized number.
    141      *
    142      * TODO: Remove if PhoneNumberUtils.normalizeNumber(String phoneNumber) is made public.
    143      */
    144     public static String normalizeNumber(String phoneNumber) {
    145         StringBuilder sb = new StringBuilder();
    146         int len = phoneNumber.length();
    147         for (int i = 0; i < len; i++) {
    148             char c = phoneNumber.charAt(i);
    149             // Character.digit() supports ASCII and Unicode digits (fullwidth, Arabic-Indic, etc.)
    150             int digit = Character.digit(c, 10);
    151             if (digit != -1) {
    152                 sb.append(digit);
    153             } else if (i == 0 && c == '+') {
    154                 sb.append(c);
    155             } else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
    156                 return normalizeNumber(PhoneNumberUtils.convertKeypadLettersToDigits(phoneNumber));
    157             }
    158         }
    159         return sb.toString();
    160     }
    161 
    162     /**
    163      * @return the "username" part of the specified SIP address, i.e. the part before the "@"
    164      * character (or "%40").
    165      *
    166      * @param number SIP address of the form "username@domainname" (or the URI-escaped equivalent
    167      * "username%40domainname")
    168      *
    169      * TODO: Remove if PhoneNumberUtils.getUsernameFromUriNumber(String number) is made public.
    170      */
    171     public static String getUsernameFromUriNumber(String number) {
    172         // The delimiter between username and domain name can be
    173         // either "@" or "%40" (the URI-escaped equivalent.)
    174         int delimiterIndex = number.indexOf('@');
    175         if (delimiterIndex < 0) {
    176             delimiterIndex = number.indexOf("%40");
    177         }
    178         if (delimiterIndex < 0) {
    179             Log.w(LOG_TAG,
    180                     "getUsernameFromUriNumber: no delimiter found in SIP addr '" + number + "'");
    181             return number;
    182         }
    183         return number.substring(0, delimiterIndex);
    184     }
    185 }
    186