• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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