1/* 2 * Copyright (C) 2010 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 17package com.android.providers.contacts; 18 19import android.provider.ContactsContract.FullNameStyle; 20import android.test.AndroidTestCase; 21import android.test.suitebuilder.annotation.SmallTest; 22import android.util.Log; 23 24import java.text.Collator; 25import java.util.ArrayList; 26import java.util.Arrays; 27import java.util.HashSet; 28import java.util.Iterator; 29import java.util.List; 30import java.util.Locale; 31 32@SmallTest 33public class ContactLocaleUtilsTest extends AndroidTestCase { 34 private static final String TAG = "ContactLocaleUtilsTest"; 35 36 private static final String PHONE_NUMBER_1 = "+1 (650) 555-1212"; 37 private static final String PHONE_NUMBER_2 = "650-555-1212"; 38 private static final String LATIN_NAME = "John Smith"; 39 private static final String LATIN_NAME_2 = "John Paul Jones"; 40 private static final String KANJI_NAME = "\u65e5"; 41 private static final String ARABIC_NAME = "\u0646\u0648\u0631"; /* Noor */ 42 private static final String CHINESE_NAME = "\u675C\u9D51"; 43 private static final String SERBIAN_NAME = "\u0408\u0435\u043B\u0435\u043D\u0430"; 44 private static final String UKRAINIAN_NAME = "\u0406"; 45 private static final String UKRAINIAN_NAME_2 = "\u0407"; 46 private static final String UKRAINIAN_NAME_3 = "\u0490"; 47 private static final String CHINESE_LATIN_MIX_NAME_1 = "D\u675C\u9D51"; 48 private static final String CHINESE_LATIN_MIX_NAME_2 = "MARY \u675C\u9D51"; 49 private static final String[] CHINESE_NAME_KEY = {"\u9D51", "\u675C\u9D51", "JUAN", "DUJUAN", 50 "J", "DJ"}; 51 private static final String[] CHINESE_LATIN_MIX_NAME_1_KEY = {"\u9D51", "\u675C\u9D51", 52 "D \u675C\u9D51", "JUAN", "DUJUAN", "J", "DJ", "D DUJUAN", "DDJ"}; 53 private static final String[] CHINESE_LATIN_MIX_NAME_2_KEY = {"\u9D51", "\u675C\u9D51", 54 "MARY \u675C\u9D51", "JUAN", "DUJUAN", "MARY DUJUAN", "J", "DJ", "MDJ"}; 55 private static final String[] LATIN_NAME_KEY = {"John Smith", "Smith", "JS", "S"}; 56 private static final String[] LATIN_NAME_KEY_2 = { 57 "John Paul Jones", "Paul Jones", "Jones", "JPJ", "PJ", "J"}; 58 private static final String[] LABELS_EN_US = { 59 "", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", 60 "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", 61 "#", ""}; 62 private static final String[] LABELS_JA_JP = { 63 "", "\u3042", "\u304B", "\u3055", "\u305F", "\u306A", "\u306F", 64 "\u307E", "\u3084", "\u3089", "\u308F", "\u4ED6", 65 "", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", 66 "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", 67 "#", ""}; 68 private static final String[] LABELS_ZH_TW = { 69 "", "1\u5283", "2\u5283", "3\u5283", "4\u5283", "5\u5283", "6\u5283", 70 "7\u5283", "8\u5283", "9\u5283", "10\u5283", "11\u5283", "12\u5283", 71 "13\u5283", "14\u5283", "15\u5283", "16\u5283", "17\u5283", "18\u5283", 72 "19\u5283", "20\u5283", "21\u5283", "22\u5283", "23\u5283", "24\u5283", 73 "25\u5283", "26\u5283", "27\u5283", "28\u5283", "29\u5283", "30\u5283", 74 "31\u5283", "32\u5283", "33\u5283", 75 "35\u5283", "36\u5283", "39\u5283", "48\u5283", 76 "", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", 77 "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", 78 "#", ""}; 79 private static final String[] LABELS_KO = { 80 "", "\u3131", "\u3134", "\u3137", "\u3139", "\u3141", "\u3142", 81 "\u3145", "\u3147", "\u3148", "\u314A", "\u314B", "\u314C", "\u314D", 82 "\u314E", 83 "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", 84 "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", 85 "#", ""}; 86 private static final String[] LABELS_AR = { 87 "", "\u0627", "\u0628", "\u062a", "\u062b", "\u062c", "\u062d", 88 "\u062e", "\u062f", "\u0630", "\u0631", "\u0632", "\u0633", "\u0634", 89 "\u0635", "\u0636", "\u0637", "\u0638", "\u0639", "\u063a", "\u0641", 90 "\u0642", "\u0643", "\u0644", "\u0645", "\u0646", "\u0647", "\u0648", 91 "\u064a", 92 "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", 93 "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", 94 "#", ""}; 95 96 private static final String JAPANESE_MISC = "\u4ed6"; 97 98 private static final Locale LOCALE_ARABIC = new Locale("ar"); 99 private static final Locale LOCALE_SERBIAN = new Locale("sr"); 100 private static final Locale LOCALE_UKRAINIAN = new Locale("uk"); 101 private boolean hasSimplifiedChineseCollator; 102 private boolean hasTraditionalChineseCollator; 103 private boolean hasJapaneseCollator; 104 private boolean hasKoreanCollator; 105 private boolean hasArabicCollator; 106 private boolean hasGermanCollator; 107 private boolean hasSerbianCollator; 108 private boolean hasUkrainianCollator; 109 110 @Override 111 protected void setUp() throws Exception { 112 super.setUp(); 113 final Locale locale[] = Collator.getAvailableLocales(); 114 for (int i = 0; i < locale.length; i++) { 115 if (LocaleSet.isLocaleSimplifiedChinese(locale[i])) { 116 hasSimplifiedChineseCollator = true; 117 } else if (LocaleSet.isLocaleTraditionalChinese(locale[i])) { 118 hasTraditionalChineseCollator = true; 119 } else if (locale[i].equals(Locale.JAPAN)) { 120 hasJapaneseCollator = true; 121 } else if (locale[i].equals(Locale.KOREA)) { 122 hasKoreanCollator = true; 123 } else if (locale[i].equals(LOCALE_ARABIC)) { 124 hasArabicCollator = true; 125 } else if (locale[i].equals(Locale.GERMANY)) { 126 hasGermanCollator = true; 127 } else if (locale[i].equals(LOCALE_SERBIAN)) { 128 hasSerbianCollator = true; 129 } else if (locale[i].equals(LOCALE_UKRAINIAN)) { 130 hasUkrainianCollator = true; 131 } 132 } 133 } 134 135 private String getLabel(String name) { 136 ContactLocaleUtils utils = ContactLocaleUtils.getInstance(); 137 int bucketIndex = utils.getBucketIndex(name); 138 return utils.getBucketLabel(bucketIndex); 139 } 140 141 private Iterator<String> getNameLookupKeys(String name, int nameStyle) { 142 ContactLocaleUtils utils = ContactLocaleUtils.getInstance(); 143 return utils.getNameLookupKeys(name, nameStyle); 144 } 145 146 private ArrayList<String> getLabels() { 147 ContactLocaleUtils utils = ContactLocaleUtils.getInstance(); 148 return utils.getLabels(); 149 } 150 151 public void testEnglishContactLocaleUtils() throws Exception { 152 ContactLocaleUtils.setLocale(Locale.ENGLISH); 153 assertEquals("#", getLabel(PHONE_NUMBER_1)); 154 assertEquals("#", getLabel(PHONE_NUMBER_2)); 155 assertEquals("J", getLabel(LATIN_NAME)); 156 assertEquals("", getLabel(CHINESE_NAME)); 157 assertEquals("D", getLabel(CHINESE_LATIN_MIX_NAME_1)); 158 assertEquals("B", getLabel("Bob Smith")); 159 160 if (hasArabicCollator) { 161 assertEquals("\u0646", getLabel(ARABIC_NAME)); 162 } 163 if (hasSerbianCollator) { 164 assertEquals("\u0408", getLabel(SERBIAN_NAME)); 165 } 166 if (hasUkrainianCollator) { 167 assertEquals("\u0406", getLabel(UKRAINIAN_NAME)); 168 } 169 170 assertNull(getNameLookupKeys(LATIN_NAME, FullNameStyle.UNDEFINED)); 171 verifyLabels(getLabels(), LABELS_EN_US); 172 } 173 174 public void testJapaneseContactLocaleUtils() throws Exception { 175 if (!hasJapaneseCollator) { 176 Log.w(TAG, "Japanese collator not found; skipping test"); 177 return; 178 } 179 180 ContactLocaleUtils.setLocale(Locale.JAPAN); 181 assertEquals("#", getLabel(PHONE_NUMBER_1)); 182 assertEquals("#", getLabel(PHONE_NUMBER_2)); 183 assertEquals(JAPANESE_MISC, getLabel(KANJI_NAME)); 184 assertEquals("J", getLabel(LATIN_NAME)); 185 assertEquals(JAPANESE_MISC, getLabel(CHINESE_NAME)); 186 assertEquals("D", getLabel(CHINESE_LATIN_MIX_NAME_1)); 187 188 assertNull(getNameLookupKeys(CHINESE_NAME, FullNameStyle.CJK)); 189 assertNull(getNameLookupKeys(CHINESE_NAME, FullNameStyle.CHINESE)); 190 191 assertEquals("B", getLabel("Bob Smith")); 192 verifyLabels(getLabels(), LABELS_JA_JP); 193 } 194 195 public void testChineseContactLocaleUtils() throws Exception { 196 if (!hasSimplifiedChineseCollator) { 197 Log.w(TAG, "Simplified Chinese collator not found; skipping test"); 198 return; 199 } 200 201 ContactLocaleUtils.setLocale(Locale.SIMPLIFIED_CHINESE); 202 assertEquals("#", getLabel(PHONE_NUMBER_1)); 203 assertEquals("#", getLabel(PHONE_NUMBER_2)); 204 assertEquals("J", getLabel(LATIN_NAME)); 205 assertEquals("D", getLabel(CHINESE_NAME)); 206 assertEquals("D", getLabel(CHINESE_LATIN_MIX_NAME_1)); 207 assertEquals("B", getLabel("Bob Smith")); 208 verifyLabels(getLabels(), LABELS_EN_US); 209 210 if (hasTraditionalChineseCollator) { 211 ContactLocaleUtils.setLocale(Locale.TRADITIONAL_CHINESE); 212 assertEquals("#", getLabel(PHONE_NUMBER_1)); 213 assertEquals("#", getLabel(PHONE_NUMBER_2)); 214 assertEquals("J", getLabel(LATIN_NAME)); 215 assertEquals("7\u5283", getLabel(CHINESE_NAME)); 216 assertEquals("D", getLabel(CHINESE_LATIN_MIX_NAME_1)); 217 } else { 218 Log.w(TAG, "Traditional Chinese collator not found"); 219 } 220 221 ContactLocaleUtils.setLocale(Locale.SIMPLIFIED_CHINESE); 222 Iterator<String> keys = getNameLookupKeys(CHINESE_NAME, 223 FullNameStyle.CHINESE); 224 verifyKeys(keys, CHINESE_NAME_KEY); 225 226 keys = getNameLookupKeys(CHINESE_LATIN_MIX_NAME_1, FullNameStyle.CHINESE); 227 verifyKeys(keys, CHINESE_LATIN_MIX_NAME_1_KEY); 228 229 keys = getNameLookupKeys(CHINESE_LATIN_MIX_NAME_2, FullNameStyle.CHINESE); 230 verifyKeys(keys, CHINESE_LATIN_MIX_NAME_2_KEY); 231 232 if (hasTraditionalChineseCollator) { 233 ContactLocaleUtils.setLocale(Locale.TRADITIONAL_CHINESE); 234 assertEquals("B", getLabel("Bob Smith")); 235 verifyLabels(getLabels(), LABELS_ZH_TW); 236 } 237 } 238 239 public void testPinyinEnabledSecondaryLocale() throws Exception { 240 if (!hasSimplifiedChineseCollator) { 241 Log.w(TAG, "Simplified Chinese collator not found; skipping test"); 242 return; 243 } 244 245 ContactLocaleUtils.setLocales( 246 new LocaleSet(Locale.ENGLISH, Locale.SIMPLIFIED_CHINESE)); 247 assertEquals("D", getLabel(CHINESE_NAME)); 248 249 Iterator<String> keys = getNameLookupKeys(CHINESE_NAME, 250 FullNameStyle.CHINESE); 251 verifyKeys(keys, CHINESE_NAME_KEY); 252 } 253 254 public void testPinyinDisabledSecondaryLocale() throws Exception { 255 if (!hasSimplifiedChineseCollator) { 256 Log.w(TAG, "Simplified Chinese collator not found; skipping test"); 257 return; 258 } 259 260 ContactLocaleUtils.setLocales( 261 new LocaleSet(Locale.ENGLISH, Locale.JAPAN)); 262 assertEquals("", getLabel(CHINESE_NAME)); 263 264 assertNull(getNameLookupKeys(CHINESE_NAME, FullNameStyle.CHINESE)); 265 assertNull(getNameLookupKeys(CHINESE_NAME, FullNameStyle.CJK)); 266 } 267 268 public void testChineseStyleNameWithDifferentLocale() throws Exception { 269 if (!hasSimplifiedChineseCollator) { 270 Log.w(TAG, "Simplified Chinese collator not found; skipping test"); 271 return; 272 } 273 274 ContactLocaleUtils.setLocale(Locale.ENGLISH); 275 assertNull(getNameLookupKeys(CHINESE_NAME, FullNameStyle.CHINESE)); 276 assertNull(getNameLookupKeys(CHINESE_NAME, FullNameStyle.CJK)); 277 278 ContactLocaleUtils.setLocale(Locale.SIMPLIFIED_CHINESE); 279 Iterator<String> keys = getNameLookupKeys(CHINESE_NAME, 280 FullNameStyle.CJK); 281 verifyKeys(keys, CHINESE_NAME_KEY); 282 keys = getNameLookupKeys(LATIN_NAME, FullNameStyle.WESTERN); 283 verifyKeys(keys, LATIN_NAME_KEY); 284 keys = getNameLookupKeys(LATIN_NAME_2, FullNameStyle.WESTERN); 285 verifyKeys(keys, LATIN_NAME_KEY_2); 286 287 if (hasTraditionalChineseCollator) { 288 ContactLocaleUtils.setLocale(Locale.TRADITIONAL_CHINESE); 289 assertNull(getNameLookupKeys(CHINESE_NAME, FullNameStyle.CJK)); 290 } 291 } 292 293 public void testKoreanContactLocaleUtils() throws Exception { 294 if (!hasKoreanCollator) { 295 Log.w(TAG, "Korean collator not found; skipping test"); 296 return; 297 } 298 299 ContactLocaleUtils.setLocale(Locale.KOREA); 300 assertEquals("\u3131", getLabel("\u1100")); 301 assertEquals("\u3131", getLabel("\u3131")); 302 assertEquals("\u3131", getLabel("\u1101")); 303 assertEquals("\u314e", getLabel("\u1161")); 304 assertEquals("B", getLabel("Bob Smith")); 305 verifyLabels(getLabels(), LABELS_KO); 306 } 307 308 public void testArabicContactLocaleUtils() throws Exception { 309 if (!hasArabicCollator) { 310 Log.w(TAG, "Arabic collator not found; skipping test"); 311 return; 312 } 313 314 ContactLocaleUtils.setLocale(LOCALE_ARABIC); 315 assertEquals("\u0646", getLabel(ARABIC_NAME)); 316 assertEquals("B", getLabel("Bob Smith")); 317 verifyLabels(getLabels(), LABELS_AR); 318 } 319 320 public void testSerbianContactLocaleUtils() throws Exception { 321 if (!hasSerbianCollator) { 322 Log.w(TAG, "Serbian collator not found; skipping test"); 323 return; 324 } 325 326 ContactLocaleUtils.setLocale(LOCALE_SERBIAN); 327 assertEquals("\u0408", getLabel(SERBIAN_NAME)); 328 assertEquals("B", getLabel("Bob Smith")); 329 } 330 331 public void testUkrainianContactLocaleUtils() throws Exception { 332 if (!hasUkrainianCollator) { 333 Log.w(TAG, "Ukrainian collator not found; skipping test"); 334 return; 335 } 336 337 ContactLocaleUtils.setLocale(LOCALE_UKRAINIAN); 338 assertEquals("\u0406", getLabel(UKRAINIAN_NAME)); 339 // ICU 55 has a bug whereby these letters have buckets created only if 340 // Ukrainian is the primary language. Once this is fixed also test 341 // these labels when in English locale. 342 assertEquals("\u0407", getLabel(UKRAINIAN_NAME_2)); 343 assertEquals("\u0490", getLabel(UKRAINIAN_NAME_3)); 344 assertEquals("B", getLabel("Bob Smith")); 345 } 346 347 public void testGermanContactLocaleUtils() throws Exception { 348 if (!hasGermanCollator) { 349 return; 350 } 351 352 ContactLocaleUtils.setLocale(Locale.GERMANY); 353 assertEquals("S", getLabel("Sacher")); 354 355 // ICU 51 has labels Sch and St. These were removed in ICU 52 356 assertEquals("S", getLabel("Schiller")); 357 assertEquals("S", getLabel("Steiff")); 358 verifyLabels(getLabels(), LABELS_EN_US); 359 } 360 361 private void verifyKeys(final Iterator<String> resultKeys, final String[] expectedKeys) 362 throws Exception { 363 HashSet<String> allKeys = new HashSet<String>(); 364 while (resultKeys.hasNext()) { 365 allKeys.add(resultKeys.next()); 366 } 367 assertEquals(new HashSet<String>(Arrays.asList(expectedKeys)), allKeys); 368 } 369 370 // Verify that the initial set of resultLabels matches the expectedLabels. 371 // Ignore the (large) number of secondary locale labels that make up the 372 // tail labels in the result set right before the final "#" and "" buckets. 373 private void verifyLabels(final ArrayList<String> resultLabels, 374 final String[] expectedLabels) throws Exception { 375 final List<String> expectedLabelList = Arrays.asList(expectedLabels); 376 final int numLabels = expectedLabelList.size() - 2; 377 assertEquals(expectedLabelList.subList(0, numLabels), 378 resultLabels.subList(0, numLabels)); 379 } 380} 381