1 /* GENERATED SOURCE. DO NOT MODIFY. */ 2 /* 3 * Copyright (C) 2011 The Libphonenumber Authors 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package com.android.i18n.phonenumbers.geocoding; 19 20 import com.android.i18n.phonenumbers.NumberParseException; 21 import com.android.i18n.phonenumbers.PhoneNumberUtil; 22 import com.android.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberType; 23 import com.android.i18n.phonenumbers.Phonenumber.PhoneNumber; 24 import com.android.i18n.phonenumbers.prefixmapper.PrefixFileReader; 25 26 import java.util.List; 27 import java.util.Locale; 28 29 /** 30 * An offline geocoder which provides geographical information related to a phone number. 31 * 32 * @author Shaopeng Jia 33 * @hide This class is not part of the Android public SDK API 34 */ 35 public class PhoneNumberOfflineGeocoder { 36 private static PhoneNumberOfflineGeocoder instance = null; 37 private static final String MAPPING_DATA_DIRECTORY = 38 "/com/android/i18n/phonenumbers/geocoding/data/"; 39 private PrefixFileReader prefixFileReader = null; 40 41 private final PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance(); 42 43 // @VisibleForTesting PhoneNumberOfflineGeocoder(String phonePrefixDataDirectory)44 PhoneNumberOfflineGeocoder(String phonePrefixDataDirectory) { 45 prefixFileReader = new PrefixFileReader(phonePrefixDataDirectory); 46 } 47 48 /** 49 * Gets a {@link PhoneNumberOfflineGeocoder} instance to carry out international phone number 50 * geocoding. 51 * 52 * <p> The {@link PhoneNumberOfflineGeocoder} is implemented as a singleton. Therefore, calling 53 * this method multiple times will only result in one instance being created. 54 * 55 * @return a {@link PhoneNumberOfflineGeocoder} instance 56 */ 57 @android.compat.annotation.UnsupportedAppUsage getInstance()58 public static synchronized PhoneNumberOfflineGeocoder getInstance() { 59 if (instance == null) { 60 instance = new PhoneNumberOfflineGeocoder(MAPPING_DATA_DIRECTORY); 61 } 62 return instance; 63 } 64 65 /** 66 * Returns the customary display name in the given language for the given territory the phone 67 * number is from. If it could be from many territories, nothing is returned. 68 */ getCountryNameForNumber(PhoneNumber number, Locale language)69 private String getCountryNameForNumber(PhoneNumber number, Locale language) { 70 List<String> regionCodes = 71 phoneUtil.getRegionCodesForCountryCode(number.getCountryCode()); 72 if (regionCodes.size() == 1) { 73 return getRegionDisplayName(regionCodes.get(0), language); 74 } else { 75 String regionWhereNumberIsValid = "ZZ"; 76 for (String regionCode : regionCodes) { 77 if (phoneUtil.isValidNumberForRegion(number, regionCode)) { 78 // If the number has already been found valid for one region, then we don't know which 79 // region it belongs to so we return nothing. 80 if (!regionWhereNumberIsValid.equals("ZZ")) { 81 return ""; 82 } 83 regionWhereNumberIsValid = regionCode; 84 } 85 } 86 return getRegionDisplayName(regionWhereNumberIsValid, language); 87 } 88 } 89 90 /** 91 * Returns the customary display name in the given language for the given region. 92 */ getRegionDisplayName(String regionCode, Locale language)93 private String getRegionDisplayName(String regionCode, Locale language) { 94 return (regionCode == null || regionCode.equals("ZZ") 95 || regionCode.equals(PhoneNumberUtil.REGION_CODE_FOR_NON_GEO_ENTITY)) 96 ? "" : new Locale("", regionCode).getDisplayCountry(language); 97 } 98 99 /** 100 * Returns a text description for the given phone number, in the language provided. The 101 * description might consist of the name of the country where the phone number is from, or the 102 * name of the geographical area the phone number is from if more detailed information is 103 * available. 104 * 105 * <p>This method assumes the validity of the number passed in has already been checked, and that 106 * the number is suitable for geocoding. We consider fixed-line and mobile numbers possible 107 * candidates for geocoding. 108 * 109 * @param number a valid phone number for which we want to get a text description 110 * @param languageCode the language code for which the description should be written 111 * @return a text description for the given language code for the given phone number, or an 112 * empty string if the number could come from multiple countries, or the country code is 113 * in fact invalid 114 */ getDescriptionForValidNumber(PhoneNumber number, Locale languageCode)115 public String getDescriptionForValidNumber(PhoneNumber number, Locale languageCode) { 116 String langStr = languageCode.getLanguage(); 117 String scriptStr = ""; // No script is specified 118 String regionStr = languageCode.getCountry(); 119 120 String areaDescription; 121 String mobileToken = PhoneNumberUtil.getCountryMobileToken(number.getCountryCode()); 122 String nationalNumber = phoneUtil.getNationalSignificantNumber(number); 123 if (!mobileToken.equals("") && nationalNumber.startsWith(mobileToken)) { 124 // In some countries, eg. Argentina, mobile numbers have a mobile token before the national 125 // destination code, this should be removed before geocoding. 126 nationalNumber = nationalNumber.substring(mobileToken.length()); 127 String region = phoneUtil.getRegionCodeForCountryCode(number.getCountryCode()); 128 PhoneNumber copiedNumber; 129 try { 130 copiedNumber = phoneUtil.parse(nationalNumber, region); 131 } catch (NumberParseException e) { 132 // If this happens, just reuse what we had. 133 copiedNumber = number; 134 } 135 areaDescription = prefixFileReader.getDescriptionForNumber(copiedNumber, langStr, scriptStr, 136 regionStr); 137 } else { 138 areaDescription = prefixFileReader.getDescriptionForNumber(number, langStr, scriptStr, 139 regionStr); 140 } 141 return (areaDescription.length() > 0) 142 ? areaDescription : getCountryNameForNumber(number, languageCode); 143 } 144 145 /** 146 * As per {@link #getDescriptionForValidNumber(PhoneNumber, Locale)} but also considers the 147 * region of the user. If the phone number is from the same region as the user, only a lower-level 148 * description will be returned, if one exists. Otherwise, the phone number's region will be 149 * returned, with optionally some more detailed information. 150 * 151 * <p>For example, for a user from the region "US" (United States), we would show "Mountain View, 152 * CA" for a particular number, omitting the United States from the description. For a user from 153 * the United Kingdom (region "GB"), for the same number we may show "Mountain View, CA, United 154 * States" or even just "United States". 155 * 156 * <p>This method assumes the validity of the number passed in has already been checked. 157 * 158 * @param number the phone number for which we want to get a text description 159 * @param languageCode the language code for which the description should be written 160 * @param userRegion the region code for a given user. This region will be omitted from the 161 * description if the phone number comes from this region. It should be a two-letter 162 * upper-case CLDR region code. 163 * @return a text description for the given language code for the given phone number, or an 164 * empty string if the number could come from multiple countries, or the country code is 165 * in fact invalid 166 */ getDescriptionForValidNumber(PhoneNumber number, Locale languageCode, String userRegion)167 public String getDescriptionForValidNumber(PhoneNumber number, Locale languageCode, 168 String userRegion) { 169 // If the user region matches the number's region, then we just show the lower-level 170 // description, if one exists - if no description exists, we will show the region(country) name 171 // for the number. 172 String regionCode = phoneUtil.getRegionCodeForNumber(number); 173 if (userRegion.equals(regionCode)) { 174 return getDescriptionForValidNumber(number, languageCode); 175 } 176 // Otherwise, we just show the region(country) name for now. 177 return getRegionDisplayName(regionCode, languageCode); 178 // TODO: Concatenate the lower-level and country-name information in an appropriate 179 // way for each language. 180 } 181 182 /** 183 * As per {@link #getDescriptionForValidNumber(PhoneNumber, Locale)} but explicitly checks 184 * the validity of the number passed in. 185 * 186 * @param number the phone number for which we want to get a text description 187 * @param languageCode the language code for which the description should be written 188 * @return a text description for the given language code for the given phone number, or empty 189 * string if the number passed in is invalid or could belong to multiple countries 190 */ 191 @android.compat.annotation.UnsupportedAppUsage getDescriptionForNumber(PhoneNumber number, Locale languageCode)192 public String getDescriptionForNumber(PhoneNumber number, Locale languageCode) { 193 PhoneNumberType numberType = phoneUtil.getNumberType(number); 194 if (numberType == PhoneNumberType.UNKNOWN) { 195 return ""; 196 } else if (!phoneUtil.isNumberGeographical(numberType, number.getCountryCode())) { 197 return getCountryNameForNumber(number, languageCode); 198 } 199 return getDescriptionForValidNumber(number, languageCode); 200 } 201 202 /** 203 * As per {@link #getDescriptionForValidNumber(PhoneNumber, Locale, String)} but 204 * explicitly checks the validity of the number passed in. 205 * 206 * @param number the phone number for which we want to get a text description 207 * @param languageCode the language code for which the description should be written 208 * @param userRegion the region code for a given user. This region will be omitted from the 209 * description if the phone number comes from this region. It should be a two-letter 210 * upper-case CLDR region code. 211 * @return a text description for the given language code for the given phone number, or empty 212 * string if the number passed in is invalid or could belong to multiple countries 213 */ getDescriptionForNumber(PhoneNumber number, Locale languageCode, String userRegion)214 public String getDescriptionForNumber(PhoneNumber number, Locale languageCode, 215 String userRegion) { 216 PhoneNumberType numberType = phoneUtil.getNumberType(number); 217 if (numberType == PhoneNumberType.UNKNOWN) { 218 return ""; 219 } else if (!phoneUtil.isNumberGeographical(numberType, number.getCountryCode())) { 220 return getCountryNameForNumber(number, languageCode); 221 } 222 return getDescriptionForValidNumber(number, languageCode, userRegion); 223 } 224 } 225