• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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     private static final String KOREA_ISO_COUNTRY_CODE = "KR";
40     /**
41      * Determines if the specified number is actually a URI (i.e. a SIP address) rather than a
42      * regular PSTN phone number, based on whether or not the number contains an "@" character.
43      *
44      * @param number Phone number
45      * @return true if number contains @
46      *
47      * TODO: Remove if PhoneNumberUtils.isUriNumber(String number) is made public.
48      */
isUriNumber(String number)49     public static boolean isUriNumber(String number) {
50         // Note we allow either "@" or "%40" to indicate a URI, in case
51         // the passed-in string is URI-escaped.  (Neither "@" nor "%40"
52         // will ever be found in a legal PSTN number.)
53         return number != null && (number.contains("@") || number.contains("%40"));
54     }
55 
56     /**
57      * Formats the phone number only if the given number hasn't been formatted.
58      * <p>
59      * The number which has only dailable character is treated as not being
60      * formatted.
61      *
62      * @param phoneNumber the number to be formatted.
63      * @param phoneNumberE164 The E164 format number whose country code is used if the given
64      * phoneNumber doesn't have the country code.
65      * @param defaultCountryIso The ISO 3166-1 two letters country code whose convention will
66      * be used if the phoneNumberE164 is null or invalid, or if phoneNumber contains IDD.
67      * @return The formatted number if the given number has been formatted, otherwise, return the
68      * given number.
69      *
70      * TODO: Remove if PhoneNumberUtils.formatNumber(String phoneNumber, String phoneNumberE164,
71      * String defaultCountryIso) is made public.
72      */
formatNumber( String phoneNumber, String phoneNumberE164, String defaultCountryIso)73     public static String formatNumber(
74             String phoneNumber, String phoneNumberE164, String defaultCountryIso) {
75         int len = phoneNumber.length();
76         for (int i = 0; i < len; i++) {
77             if (!PhoneNumberUtils.isDialable(phoneNumber.charAt(i))) {
78                 return phoneNumber;
79             }
80         }
81         PhoneNumberUtil util = PhoneNumberUtil.getInstance();
82         // Get the country code from phoneNumberE164
83         if (phoneNumberE164 != null && phoneNumberE164.length() >= 2
84                 && phoneNumberE164.charAt(0) == '+') {
85             try {
86                 // The number to be parsed is in E164 format, so the default region used doesn't
87                 // matter.
88                 PhoneNumber pn = util.parse(phoneNumberE164, "ZZ");
89                 String regionCode = util.getRegionCodeForNumber(pn);
90                 if (!TextUtils.isEmpty(regionCode) &&
91                         // This makes sure phoneNumber doesn't contain an IDD
92                         normalizeNumber(phoneNumber).indexOf(phoneNumberE164.substring(1)) <= 0) {
93                     defaultCountryIso = regionCode;
94                 }
95             } catch (NumberParseException e) {
96                 Log.w(LOG_TAG, "The number could not be parsed in E164 format!");
97             }
98         }
99 
100         String result = formatNumber(phoneNumber, defaultCountryIso);
101         return result == null ? phoneNumber : result;
102     }
103 
104     /**
105      * Format a phone number.
106      * <p>
107      * If the given number doesn't have the country code, the phone will be
108      * formatted to the default country's convention.
109      *
110      * @param phoneNumber The number to be formatted.
111      * @param defaultCountryIso The ISO 3166-1 two letters country code whose convention will
112      * be used if the given number doesn't have the country code.
113      * @return The formatted number, or null if the given number is not valid.
114      *
115      * TODO: Remove if PhoneNumberUtils.formatNumber(String phoneNumber, String defaultCountryIso)
116      * is made public.
117      */
formatNumber(String phoneNumber, String defaultCountryIso)118     public static String formatNumber(String phoneNumber, String defaultCountryIso) {
119         // Do not attempt to format numbers that start with a hash or star symbol.
120         if (phoneNumber.startsWith("#") || phoneNumber.startsWith("*")) {
121             return phoneNumber;
122         }
123 
124         final PhoneNumberUtil util = PhoneNumberUtil.getInstance();
125         String result = null;
126         try {
127             PhoneNumber pn = util.parseAndKeepRawInput(phoneNumber, defaultCountryIso);
128             /**
129              * Need to reformat any local Korean phone numbers (when the user is in Korea) with
130              * country code to corresponding national format which would replace the leading
131              * +82 with 0.
132              */
133             if (KOREA_ISO_COUNTRY_CODE.equals(defaultCountryIso) &&
134                     (pn.getCountryCode() == util.getCountryCodeForRegion(KOREA_ISO_COUNTRY_CODE)) &&
135                     (pn.getCountryCodeSource() ==
136                             PhoneNumber.CountryCodeSource.FROM_NUMBER_WITH_PLUS_SIGN)) {
137                 result = util.format(pn, PhoneNumberUtil.PhoneNumberFormat.NATIONAL);
138             } else {
139                 result = util.formatInOriginalFormat(pn, defaultCountryIso);
140             }
141         } catch (NumberParseException e) {
142             Log.w(LOG_TAG, "Number could not be parsed with the given country code!");
143         }
144         return result;
145     }
146 
147     /**
148      * Normalize a phone number by removing the characters other than digits. If
149      * the given number has keypad letters, the letters will be converted to
150      * digits first.
151      *
152      * @param phoneNumber The number to be normalized.
153      * @return The normalized number.
154      *
155      * TODO: Remove if PhoneNumberUtils.normalizeNumber(String phoneNumber) is made public.
156      */
normalizeNumber(String phoneNumber)157     public static String normalizeNumber(String phoneNumber) {
158         StringBuilder sb = new StringBuilder();
159         int len = phoneNumber.length();
160         for (int i = 0; i < len; i++) {
161             char c = phoneNumber.charAt(i);
162             // Character.digit() supports ASCII and Unicode digits (fullwidth, Arabic-Indic, etc.)
163             int digit = Character.digit(c, 10);
164             if (digit != -1) {
165                 sb.append(digit);
166             } else if (i == 0 && c == '+') {
167                 sb.append(c);
168             } else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
169                 return normalizeNumber(PhoneNumberUtils.convertKeypadLettersToDigits(phoneNumber));
170             }
171         }
172         return sb.toString();
173     }
174 
175     /**
176      * @return the "username" part of the specified SIP address, i.e. the part before the "@"
177      * character (or "%40").
178      *
179      * @param number SIP address of the form "username@domainname" (or the URI-escaped equivalent
180      * "username%40domainname")
181      *
182      * TODO: Remove if PhoneNumberUtils.getUsernameFromUriNumber(String number) is made public.
183      */
getUsernameFromUriNumber(String number)184     public static String getUsernameFromUriNumber(String number) {
185         // The delimiter between username and domain name can be
186         // either "@" or "%40" (the URI-escaped equivalent.)
187         int delimiterIndex = number.indexOf('@');
188         if (delimiterIndex < 0) {
189             delimiterIndex = number.indexOf("%40");
190         }
191         if (delimiterIndex < 0) {
192             Log.w(LOG_TAG,
193                     "getUsernameFromUriNumber: no delimiter found in SIP addr '" + number + "'");
194             return number;
195         }
196         return number.substring(0, delimiterIndex);
197     }
198 }
199