1 /* 2 * Copyright (C) 2009 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 android.pim.vcard; 17 18 import android.content.ContentProviderOperation; 19 import android.content.ContentValues; 20 import android.provider.ContactsContract.Data; 21 import android.provider.ContactsContract.CommonDataKinds.Phone; 22 import android.provider.ContactsContract.CommonDataKinds.StructuredPostal; 23 import android.telephony.PhoneNumberUtils; 24 import android.text.TextUtils; 25 26 import java.util.Collection; 27 import java.util.HashMap; 28 import java.util.HashSet; 29 import java.util.Map; 30 import java.util.Set; 31 32 /** 33 * Utilities for VCard handling codes. 34 */ 35 public class VCardUtils { 36 /* 37 * TODO: some of methods in this class should be placed to the more appropriate place... 38 */ 39 40 // Note that not all types are included in this map/set, since, for example, TYPE_HOME_FAX is 41 // converted to two attribute Strings. These only contain some minor fields valid in both 42 // vCard and current (as of 2009-08-07) Contacts structure. 43 private static final Map<Integer, String> sKnownPhoneTypesMap_ItoS; 44 private static final Set<String> sPhoneTypesSetUnknownToContacts; 45 46 private static final Map<String, Integer> sKnownPhoneTypesMap_StoI; 47 48 static { 49 sKnownPhoneTypesMap_ItoS = new HashMap<Integer, String>(); 50 sKnownPhoneTypesMap_StoI = new HashMap<String, Integer>(); 51 sKnownPhoneTypesMap_ItoS.put(Phone.TYPE_CAR, Constants.ATTR_TYPE_CAR)52 sKnownPhoneTypesMap_ItoS.put(Phone.TYPE_CAR, Constants.ATTR_TYPE_CAR); sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_CAR, Phone.TYPE_CAR)53 sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_CAR, Phone.TYPE_CAR); sKnownPhoneTypesMap_ItoS.put(Phone.TYPE_PAGER, Constants.ATTR_TYPE_PAGER)54 sKnownPhoneTypesMap_ItoS.put(Phone.TYPE_PAGER, Constants.ATTR_TYPE_PAGER); sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_PAGER, Phone.TYPE_PAGER)55 sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_PAGER, Phone.TYPE_PAGER); sKnownPhoneTypesMap_ItoS.put(Phone.TYPE_ISDN, Constants.ATTR_TYPE_ISDN)56 sKnownPhoneTypesMap_ItoS.put(Phone.TYPE_ISDN, Constants.ATTR_TYPE_ISDN); sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_ISDN, Phone.TYPE_ISDN)57 sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_ISDN, Phone.TYPE_ISDN); 58 sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_HOME, Phone.TYPE_HOME)59 sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_HOME, Phone.TYPE_HOME); sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_WORK, Phone.TYPE_WORK)60 sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_WORK, Phone.TYPE_WORK); sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_CELL, Phone.TYPE_MOBILE)61 sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_CELL, Phone.TYPE_MOBILE); 62 sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_PHONE_EXTRA_OTHER, Phone.TYPE_OTHER)63 sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_PHONE_EXTRA_OTHER, Phone.TYPE_OTHER); sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_PHONE_EXTRA_CALLBACK, Phone.TYPE_CALLBACK)64 sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_PHONE_EXTRA_CALLBACK, Phone.TYPE_CALLBACK); sKnownPhoneTypesMap_StoI.put( Constants.ATTR_TYPE_PHONE_EXTRA_COMPANY_MAIN, Phone.TYPE_COMPANY_MAIN)65 sKnownPhoneTypesMap_StoI.put( 66 Constants.ATTR_TYPE_PHONE_EXTRA_COMPANY_MAIN, Phone.TYPE_COMPANY_MAIN); sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_PHONE_EXTRA_RADIO, Phone.TYPE_RADIO)67 sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_PHONE_EXTRA_RADIO, Phone.TYPE_RADIO); sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_PHONE_EXTRA_TELEX, Phone.TYPE_TELEX)68 sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_PHONE_EXTRA_TELEX, Phone.TYPE_TELEX); sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_PHONE_EXTRA_TTY_TDD, Phone.TYPE_TTY_TDD)69 sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_PHONE_EXTRA_TTY_TDD, Phone.TYPE_TTY_TDD); sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_PHONE_EXTRA_ASSISTANT, Phone.TYPE_ASSISTANT)70 sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_PHONE_EXTRA_ASSISTANT, Phone.TYPE_ASSISTANT); 71 72 sPhoneTypesSetUnknownToContacts = new HashSet<String>(); 73 sPhoneTypesSetUnknownToContacts.add(Constants.ATTR_TYPE_MODEM); 74 sPhoneTypesSetUnknownToContacts.add(Constants.ATTR_TYPE_MSG); 75 sPhoneTypesSetUnknownToContacts.add(Constants.ATTR_TYPE_BBS); 76 sPhoneTypesSetUnknownToContacts.add(Constants.ATTR_TYPE_VIDEO); 77 } 78 getPhoneAttributeString(Integer type)79 public static String getPhoneAttributeString(Integer type) { 80 return sKnownPhoneTypesMap_ItoS.get(type); 81 } 82 83 /** 84 * Returns Interger when the given types can be parsed as known type. Returns String object 85 * when not, which should be set to label. 86 */ getPhoneTypeFromStrings(Collection<String> types)87 public static Object getPhoneTypeFromStrings(Collection<String> types) { 88 int type = -1; 89 String label = null; 90 boolean isFax = false; 91 boolean hasPref = false; 92 93 if (types != null) { 94 for (String typeString : types) { 95 typeString = typeString.toUpperCase(); 96 if (typeString.equals(Constants.ATTR_TYPE_PREF)) { 97 hasPref = true; 98 } else if (typeString.equals(Constants.ATTR_TYPE_FAX)) { 99 isFax = true; 100 } else { 101 if (typeString.startsWith("X-") && type < 0) { 102 typeString = typeString.substring(2); 103 } 104 Integer tmp = sKnownPhoneTypesMap_StoI.get(typeString); 105 if (tmp != null) { 106 type = tmp; 107 } else if (type < 0) { 108 type = Phone.TYPE_CUSTOM; 109 label = typeString; 110 } 111 } 112 } 113 } 114 if (type < 0) { 115 if (hasPref) { 116 type = Phone.TYPE_MAIN; 117 } else { 118 // default to TYPE_HOME 119 type = Phone.TYPE_HOME; 120 } 121 } 122 if (isFax) { 123 if (type == Phone.TYPE_HOME) { 124 type = Phone.TYPE_FAX_HOME; 125 } else if (type == Phone.TYPE_WORK) { 126 type = Phone.TYPE_FAX_WORK; 127 } else if (type == Phone.TYPE_OTHER) { 128 type = Phone.TYPE_OTHER_FAX; 129 } 130 } 131 if (type == Phone.TYPE_CUSTOM) { 132 return label; 133 } else { 134 return type; 135 } 136 } 137 isValidPhoneAttribute(String phoneAttribute, int vcardType)138 public static boolean isValidPhoneAttribute(String phoneAttribute, int vcardType) { 139 // TODO: check the following. 140 // - it may violate vCard spec 141 // - it may contain non-ASCII characters 142 // 143 // TODO: use vcardType 144 return (phoneAttribute.startsWith("X-") || phoneAttribute.startsWith("x-") || 145 sPhoneTypesSetUnknownToContacts.contains(phoneAttribute)); 146 } 147 sortNameElements(int vcardType, String familyName, String middleName, String givenName)148 public static String[] sortNameElements(int vcardType, 149 String familyName, String middleName, String givenName) { 150 String[] list = new String[3]; 151 switch (VCardConfig.getNameOrderType(vcardType)) { 152 case VCardConfig.NAME_ORDER_JAPANESE: 153 // TODO: Should handle Ascii case? 154 list[0] = familyName; 155 list[1] = middleName; 156 list[2] = givenName; 157 break; 158 case VCardConfig.NAME_ORDER_EUROPE: 159 list[0] = middleName; 160 list[1] = givenName; 161 list[2] = familyName; 162 break; 163 default: 164 list[0] = givenName; 165 list[1] = middleName; 166 list[2] = familyName; 167 break; 168 } 169 return list; 170 } 171 getPhoneNumberFormat(final int vcardType)172 public static int getPhoneNumberFormat(final int vcardType) { 173 if (VCardConfig.isJapaneseDevice(vcardType)) { 174 return PhoneNumberUtils.FORMAT_JAPAN; 175 } else { 176 return PhoneNumberUtils.FORMAT_NANP; 177 } 178 } 179 180 /** 181 * Inserts postal data into the builder object. 182 * 183 * Note that the data structure of ContactsContract is different from that defined in vCard. 184 * So some conversion may be performed in this method. See also 185 * {{@link #getVCardPostalElements(ContentValues)} 186 */ insertStructuredPostalDataUsingContactsStruct(int vcardType, final ContentProviderOperation.Builder builder, final ContactStruct.PostalData postalData)187 public static void insertStructuredPostalDataUsingContactsStruct(int vcardType, 188 final ContentProviderOperation.Builder builder, 189 final ContactStruct.PostalData postalData) { 190 builder.withValueBackReference(StructuredPostal.RAW_CONTACT_ID, 0); 191 builder.withValue(Data.MIMETYPE, StructuredPostal.CONTENT_ITEM_TYPE); 192 193 builder.withValue(StructuredPostal.TYPE, postalData.type); 194 if (postalData.type == StructuredPostal.TYPE_CUSTOM) { 195 builder.withValue(StructuredPostal.LABEL, postalData.label); 196 } 197 198 builder.withValue(StructuredPostal.POBOX, postalData.pobox); 199 // Extended address is dropped since there's no relevant entry in ContactsContract. 200 builder.withValue(StructuredPostal.STREET, postalData.street); 201 builder.withValue(StructuredPostal.CITY, postalData.localty); 202 builder.withValue(StructuredPostal.REGION, postalData.region); 203 builder.withValue(StructuredPostal.POSTCODE, postalData.postalCode); 204 builder.withValue(StructuredPostal.COUNTRY, postalData.country); 205 206 builder.withValue(StructuredPostal.FORMATTED_ADDRESS, 207 postalData.getFormattedAddress(vcardType)); 208 if (postalData.isPrimary) { 209 builder.withValue(Data.IS_PRIMARY, 1); 210 } 211 } 212 213 /** 214 * Returns String[] containing address information based on vCard spec 215 * (PO Box, Extended Address, Street, Locality, Region, Postal Code, Country Name). 216 * All String objects are non-null ("" is used when the relevant data is empty). 217 * 218 * Note that the data structure of ContactsContract is different from that defined in vCard. 219 * So some conversion may be performed in this method. See also 220 * {{@link #insertStructuredPostalDataUsingContactsStruct(int, 221 * android.content.ContentProviderOperation.Builder, 222 * android.pim.vcard.ContactStruct.PostalData)} 223 */ getVCardPostalElements(ContentValues contentValues)224 public static String[] getVCardPostalElements(ContentValues contentValues) { 225 String[] dataArray = new String[7]; 226 dataArray[0] = contentValues.getAsString(StructuredPostal.POBOX); 227 if (dataArray[0] == null) { 228 dataArray[0] = ""; 229 } 230 // Extended addr. There's no relevant data in ContactsContract. 231 dataArray[1] = ""; 232 dataArray[2] = contentValues.getAsString(StructuredPostal.STREET); 233 if (dataArray[2] == null) { 234 dataArray[2] = ""; 235 } 236 // Assume that localty == city 237 dataArray[3] = contentValues.getAsString(StructuredPostal.CITY); 238 if (dataArray[3] == null) { 239 dataArray[3] = ""; 240 } 241 String region = contentValues.getAsString(StructuredPostal.REGION); 242 if (!TextUtils.isEmpty(region)) { 243 dataArray[4] = region; 244 } else { 245 dataArray[4] = ""; 246 } 247 dataArray[5] = contentValues.getAsString(StructuredPostal.POSTCODE); 248 if (dataArray[5] == null) { 249 dataArray[5] = ""; 250 } 251 dataArray[6] = contentValues.getAsString(StructuredPostal.COUNTRY); 252 if (dataArray[6] == null) { 253 dataArray[6] = ""; 254 } 255 256 return dataArray; 257 } 258 constructNameFromElements(int nameOrderType, String familyName, String middleName, String givenName)259 public static String constructNameFromElements(int nameOrderType, 260 String familyName, String middleName, String givenName) { 261 return constructNameFromElements(nameOrderType, familyName, middleName, givenName, 262 null, null); 263 } 264 constructNameFromElements(int nameOrderType, String familyName, String middleName, String givenName, String prefix, String suffix)265 public static String constructNameFromElements(int nameOrderType, 266 String familyName, String middleName, String givenName, 267 String prefix, String suffix) { 268 StringBuilder builder = new StringBuilder(); 269 String[] nameList = sortNameElements(nameOrderType, 270 familyName, middleName, givenName); 271 boolean first = true; 272 if (!TextUtils.isEmpty(prefix)) { 273 first = false; 274 builder.append(prefix); 275 } 276 for (String namePart : nameList) { 277 if (!TextUtils.isEmpty(namePart)) { 278 if (first) { 279 first = false; 280 } else { 281 builder.append(' '); 282 } 283 builder.append(namePart); 284 } 285 } 286 if (!TextUtils.isEmpty(suffix)) { 287 if (!first) { 288 builder.append(' '); 289 } 290 builder.append(suffix); 291 } 292 return builder.toString(); 293 } 294 containsOnlyPrintableAscii(String str)295 public static boolean containsOnlyPrintableAscii(String str) { 296 if (TextUtils.isEmpty(str)) { 297 return true; 298 } 299 300 final int length = str.length(); 301 final int asciiFirst = 0x20; 302 final int asciiLast = 0x126; 303 for (int i = 0; i < length; i = str.offsetByCodePoints(i, 1)) { 304 int c = str.codePointAt(i); 305 if (c < asciiFirst || asciiLast < c) { 306 return false; 307 } 308 } 309 return true; 310 } 311 312 /** 313 * This is useful when checking the string should be encoded into quoted-printable 314 * or not, which is required by vCard 2.1. 315 * See the definition of "7bit" in vCard 2.1 spec for more information. 316 */ containsOnlyNonCrLfPrintableAscii(String str)317 public static boolean containsOnlyNonCrLfPrintableAscii(String str) { 318 if (TextUtils.isEmpty(str)) { 319 return true; 320 } 321 322 final int length = str.length(); 323 final int asciiFirst = 0x20; 324 final int asciiLast = 0x126; 325 for (int i = 0; i < length; i = str.offsetByCodePoints(i, 1)) { 326 int c = str.codePointAt(i); 327 if (c < asciiFirst || asciiLast < c || c == '\n' || c == '\r') { 328 return false; 329 } 330 } 331 return true; 332 } 333 334 /** 335 * This is useful since vCard 3.0 often requires the ("X-") properties and groups 336 * should contain only alphabets, digits, and hyphen. 337 * 338 * Note: It is already known some devices (wrongly) outputs properties with characters 339 * which should not be in the field. One example is "X-GOOGLE TALK". We accept 340 * such kind of input but must never output it unless the target is very specific 341 * to the device which is able to parse the malformed input. 342 */ containsOnlyAlphaDigitHyphen(String str)343 public static boolean containsOnlyAlphaDigitHyphen(String str) { 344 if (TextUtils.isEmpty(str)) { 345 return true; 346 } 347 348 final int lowerAlphabetFirst = 0x41; // included ('A') 349 final int lowerAlphabetLast = 0x5b; // not included ('[') 350 final int upperAlphabetFirst = 0x61; // included ('a') 351 final int upperAlphabetLast = 0x7b; // included ('{') 352 final int digitFirst = 0x30; // included ('0') 353 final int digitLast = 0x39; // included ('9') 354 final int hyphen = '-'; 355 final int length = str.length(); 356 for (int i = 0; i < length; i = str.offsetByCodePoints(i, 1)) { 357 int codepoint = str.codePointAt(i); 358 if (!((lowerAlphabetFirst <= codepoint && codepoint < lowerAlphabetLast) || 359 (upperAlphabetFirst <= codepoint && codepoint < upperAlphabetLast) || 360 (digitFirst <= codepoint && codepoint < digitLast) || 361 (codepoint == hyphen))) { 362 return false; 363 } 364 } 365 return true; 366 } 367 368 // TODO: Replace wth the method in Base64 class. 369 private static char PAD = '='; 370 private static final char[] ENCODE64 = { 371 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P', 372 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f', 373 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v', 374 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/' 375 }; 376 encodeBase64(byte[] data)377 static public String encodeBase64(byte[] data) { 378 if (data == null) { 379 return ""; 380 } 381 382 char[] charBuffer = new char[(data.length + 2) / 3 * 4]; 383 int position = 0; 384 int _3byte = 0; 385 for (int i=0; i<data.length-2; i+=3) { 386 _3byte = ((data[i] & 0xFF) << 16) + ((data[i+1] & 0xFF) << 8) + (data[i+2] & 0xFF); 387 charBuffer[position++] = ENCODE64[_3byte >> 18]; 388 charBuffer[position++] = ENCODE64[(_3byte >> 12) & 0x3F]; 389 charBuffer[position++] = ENCODE64[(_3byte >> 6) & 0x3F]; 390 charBuffer[position++] = ENCODE64[_3byte & 0x3F]; 391 } 392 switch(data.length % 3) { 393 case 1: // [111111][11 0000][0000 00][000000] 394 _3byte = ((data[data.length-1] & 0xFF) << 16); 395 charBuffer[position++] = ENCODE64[_3byte >> 18]; 396 charBuffer[position++] = ENCODE64[(_3byte >> 12) & 0x3F]; 397 charBuffer[position++] = PAD; 398 charBuffer[position++] = PAD; 399 break; 400 case 2: // [111111][11 1111][1111 00][000000] 401 _3byte = ((data[data.length-2] & 0xFF) << 16) + ((data[data.length-1] & 0xFF) << 8); 402 charBuffer[position++] = ENCODE64[_3byte >> 18]; 403 charBuffer[position++] = ENCODE64[(_3byte >> 12) & 0x3F]; 404 charBuffer[position++] = ENCODE64[(_3byte >> 6) & 0x3F]; 405 charBuffer[position++] = PAD; 406 break; 407 } 408 409 return new String(charBuffer); 410 } 411 toHalfWidthString(String orgString)412 static public String toHalfWidthString(String orgString) { 413 if (TextUtils.isEmpty(orgString)) { 414 return null; 415 } 416 StringBuilder builder = new StringBuilder(); 417 int length = orgString.length(); 418 for (int i = 0; i < length; i++) { 419 // All Japanese character is able to be expressed by char. 420 // Do not need to use String#codepPointAt(). 421 char ch = orgString.charAt(i); 422 CharSequence halfWidthText = JapaneseUtils.tryGetHalfWidthText(ch); 423 if (halfWidthText != null) { 424 builder.append(halfWidthText); 425 } else { 426 builder.append(ch); 427 } 428 } 429 return builder.toString(); 430 } 431 VCardUtils()432 private VCardUtils() { 433 } 434 } 435 436 /** 437 * TextUtils especially for Japanese. 438 * TODO: make this in android.text in the future 439 */ 440 class JapaneseUtils { 441 static private final Map<Character, String> sHalfWidthMap = 442 new HashMap<Character, String>(); 443 444 static { 445 // There's no logical mapping rule in Unicode. Sigh. 446 sHalfWidthMap.put('\u3001', "\uFF64"); 447 sHalfWidthMap.put('\u3002', "\uFF61"); 448 sHalfWidthMap.put('\u300C', "\uFF62"); 449 sHalfWidthMap.put('\u300D', "\uFF63"); 450 sHalfWidthMap.put('\u301C', "~"); 451 sHalfWidthMap.put('\u3041', "\uFF67"); 452 sHalfWidthMap.put('\u3042', "\uFF71"); 453 sHalfWidthMap.put('\u3043', "\uFF68"); 454 sHalfWidthMap.put('\u3044', "\uFF72"); 455 sHalfWidthMap.put('\u3045', "\uFF69"); 456 sHalfWidthMap.put('\u3046', "\uFF73"); 457 sHalfWidthMap.put('\u3047', "\uFF6A"); 458 sHalfWidthMap.put('\u3048', "\uFF74"); 459 sHalfWidthMap.put('\u3049', "\uFF6B"); 460 sHalfWidthMap.put('\u304A', "\uFF75"); 461 sHalfWidthMap.put('\u304B', "\uFF76"); 462 sHalfWidthMap.put('\u304C', "\uFF76\uFF9E"); 463 sHalfWidthMap.put('\u304D', "\uFF77"); 464 sHalfWidthMap.put('\u304E', "\uFF77\uFF9E"); 465 sHalfWidthMap.put('\u304F', "\uFF78"); 466 sHalfWidthMap.put('\u3050', "\uFF78\uFF9E"); 467 sHalfWidthMap.put('\u3051', "\uFF79"); 468 sHalfWidthMap.put('\u3052', "\uFF79\uFF9E"); 469 sHalfWidthMap.put('\u3053', "\uFF7A"); 470 sHalfWidthMap.put('\u3054', "\uFF7A\uFF9E"); 471 sHalfWidthMap.put('\u3055', "\uFF7B"); 472 sHalfWidthMap.put('\u3056', "\uFF7B\uFF9E"); 473 sHalfWidthMap.put('\u3057', "\uFF7C"); 474 sHalfWidthMap.put('\u3058', "\uFF7C\uFF9E"); 475 sHalfWidthMap.put('\u3059', "\uFF7D"); 476 sHalfWidthMap.put('\u305A', "\uFF7D\uFF9E"); 477 sHalfWidthMap.put('\u305B', "\uFF7E"); 478 sHalfWidthMap.put('\u305C', "\uFF7E\uFF9E"); 479 sHalfWidthMap.put('\u305D', "\uFF7F"); 480 sHalfWidthMap.put('\u305E', "\uFF7F\uFF9E"); 481 sHalfWidthMap.put('\u305F', "\uFF80"); 482 sHalfWidthMap.put('\u3060', "\uFF80\uFF9E"); 483 sHalfWidthMap.put('\u3061', "\uFF81"); 484 sHalfWidthMap.put('\u3062', "\uFF81\uFF9E"); 485 sHalfWidthMap.put('\u3063', "\uFF6F"); 486 sHalfWidthMap.put('\u3064', "\uFF82"); 487 sHalfWidthMap.put('\u3065', "\uFF82\uFF9E"); 488 sHalfWidthMap.put('\u3066', "\uFF83"); 489 sHalfWidthMap.put('\u3067', "\uFF83\uFF9E"); 490 sHalfWidthMap.put('\u3068', "\uFF84"); 491 sHalfWidthMap.put('\u3069', "\uFF84\uFF9E"); 492 sHalfWidthMap.put('\u306A', "\uFF85"); 493 sHalfWidthMap.put('\u306B', "\uFF86"); 494 sHalfWidthMap.put('\u306C', "\uFF87"); 495 sHalfWidthMap.put('\u306D', "\uFF88"); 496 sHalfWidthMap.put('\u306E', "\uFF89"); 497 sHalfWidthMap.put('\u306F', "\uFF8A"); 498 sHalfWidthMap.put('\u3070', "\uFF8A\uFF9E"); 499 sHalfWidthMap.put('\u3071', "\uFF8A\uFF9F"); 500 sHalfWidthMap.put('\u3072', "\uFF8B"); 501 sHalfWidthMap.put('\u3073', "\uFF8B\uFF9E"); 502 sHalfWidthMap.put('\u3074', "\uFF8B\uFF9F"); 503 sHalfWidthMap.put('\u3075', "\uFF8C"); 504 sHalfWidthMap.put('\u3076', "\uFF8C\uFF9E"); 505 sHalfWidthMap.put('\u3077', "\uFF8C\uFF9F"); 506 sHalfWidthMap.put('\u3078', "\uFF8D"); 507 sHalfWidthMap.put('\u3079', "\uFF8D\uFF9E"); 508 sHalfWidthMap.put('\u307A', "\uFF8D\uFF9F"); 509 sHalfWidthMap.put('\u307B', "\uFF8E"); 510 sHalfWidthMap.put('\u307C', "\uFF8E\uFF9E"); 511 sHalfWidthMap.put('\u307D', "\uFF8E\uFF9F"); 512 sHalfWidthMap.put('\u307E', "\uFF8F"); 513 sHalfWidthMap.put('\u307F', "\uFF90"); 514 sHalfWidthMap.put('\u3080', "\uFF91"); 515 sHalfWidthMap.put('\u3081', "\uFF92"); 516 sHalfWidthMap.put('\u3082', "\uFF93"); 517 sHalfWidthMap.put('\u3083', "\uFF6C"); 518 sHalfWidthMap.put('\u3084', "\uFF94"); 519 sHalfWidthMap.put('\u3085', "\uFF6D"); 520 sHalfWidthMap.put('\u3086', "\uFF95"); 521 sHalfWidthMap.put('\u3087', "\uFF6E"); 522 sHalfWidthMap.put('\u3088', "\uFF96"); 523 sHalfWidthMap.put('\u3089', "\uFF97"); 524 sHalfWidthMap.put('\u308A', "\uFF98"); 525 sHalfWidthMap.put('\u308B', "\uFF99"); 526 sHalfWidthMap.put('\u308C', "\uFF9A"); 527 sHalfWidthMap.put('\u308D', "\uFF9B"); 528 sHalfWidthMap.put('\u308E', "\uFF9C"); 529 sHalfWidthMap.put('\u308F', "\uFF9C"); 530 sHalfWidthMap.put('\u3090', "\uFF72"); 531 sHalfWidthMap.put('\u3091', "\uFF74"); 532 sHalfWidthMap.put('\u3092', "\uFF66"); 533 sHalfWidthMap.put('\u3093', "\uFF9D"); 534 sHalfWidthMap.put('\u309B', "\uFF9E"); 535 sHalfWidthMap.put('\u309C', "\uFF9F"); 536 sHalfWidthMap.put('\u30A1', "\uFF67"); 537 sHalfWidthMap.put('\u30A2', "\uFF71"); 538 sHalfWidthMap.put('\u30A3', "\uFF68"); 539 sHalfWidthMap.put('\u30A4', "\uFF72"); 540 sHalfWidthMap.put('\u30A5', "\uFF69"); 541 sHalfWidthMap.put('\u30A6', "\uFF73"); 542 sHalfWidthMap.put('\u30A7', "\uFF6A"); 543 sHalfWidthMap.put('\u30A8', "\uFF74"); 544 sHalfWidthMap.put('\u30A9', "\uFF6B"); 545 sHalfWidthMap.put('\u30AA', "\uFF75"); 546 sHalfWidthMap.put('\u30AB', "\uFF76"); 547 sHalfWidthMap.put('\u30AC', "\uFF76\uFF9E"); 548 sHalfWidthMap.put('\u30AD', "\uFF77"); 549 sHalfWidthMap.put('\u30AE', "\uFF77\uFF9E"); 550 sHalfWidthMap.put('\u30AF', "\uFF78"); 551 sHalfWidthMap.put('\u30B0', "\uFF78\uFF9E"); 552 sHalfWidthMap.put('\u30B1', "\uFF79"); 553 sHalfWidthMap.put('\u30B2', "\uFF79\uFF9E"); 554 sHalfWidthMap.put('\u30B3', "\uFF7A"); 555 sHalfWidthMap.put('\u30B4', "\uFF7A\uFF9E"); 556 sHalfWidthMap.put('\u30B5', "\uFF7B"); 557 sHalfWidthMap.put('\u30B6', "\uFF7B\uFF9E"); 558 sHalfWidthMap.put('\u30B7', "\uFF7C"); 559 sHalfWidthMap.put('\u30B8', "\uFF7C\uFF9E"); 560 sHalfWidthMap.put('\u30B9', "\uFF7D"); 561 sHalfWidthMap.put('\u30BA', "\uFF7D\uFF9E"); 562 sHalfWidthMap.put('\u30BB', "\uFF7E"); 563 sHalfWidthMap.put('\u30BC', "\uFF7E\uFF9E"); 564 sHalfWidthMap.put('\u30BD', "\uFF7F"); 565 sHalfWidthMap.put('\u30BE', "\uFF7F\uFF9E"); 566 sHalfWidthMap.put('\u30BF', "\uFF80"); 567 sHalfWidthMap.put('\u30C0', "\uFF80\uFF9E"); 568 sHalfWidthMap.put('\u30C1', "\uFF81"); 569 sHalfWidthMap.put('\u30C2', "\uFF81\uFF9E"); 570 sHalfWidthMap.put('\u30C3', "\uFF6F"); 571 sHalfWidthMap.put('\u30C4', "\uFF82"); 572 sHalfWidthMap.put('\u30C5', "\uFF82\uFF9E"); 573 sHalfWidthMap.put('\u30C6', "\uFF83"); 574 sHalfWidthMap.put('\u30C7', "\uFF83\uFF9E"); 575 sHalfWidthMap.put('\u30C8', "\uFF84"); 576 sHalfWidthMap.put('\u30C9', "\uFF84\uFF9E"); 577 sHalfWidthMap.put('\u30CA', "\uFF85"); 578 sHalfWidthMap.put('\u30CB', "\uFF86"); 579 sHalfWidthMap.put('\u30CC', "\uFF87"); 580 sHalfWidthMap.put('\u30CD', "\uFF88"); 581 sHalfWidthMap.put('\u30CE', "\uFF89"); 582 sHalfWidthMap.put('\u30CF', "\uFF8A"); 583 sHalfWidthMap.put('\u30D0', "\uFF8A\uFF9E"); 584 sHalfWidthMap.put('\u30D1', "\uFF8A\uFF9F"); 585 sHalfWidthMap.put('\u30D2', "\uFF8B"); 586 sHalfWidthMap.put('\u30D3', "\uFF8B\uFF9E"); 587 sHalfWidthMap.put('\u30D4', "\uFF8B\uFF9F"); 588 sHalfWidthMap.put('\u30D5', "\uFF8C"); 589 sHalfWidthMap.put('\u30D6', "\uFF8C\uFF9E"); 590 sHalfWidthMap.put('\u30D7', "\uFF8C\uFF9F"); 591 sHalfWidthMap.put('\u30D8', "\uFF8D"); 592 sHalfWidthMap.put('\u30D9', "\uFF8D\uFF9E"); 593 sHalfWidthMap.put('\u30DA', "\uFF8D\uFF9F"); 594 sHalfWidthMap.put('\u30DB', "\uFF8E"); 595 sHalfWidthMap.put('\u30DC', "\uFF8E\uFF9E"); 596 sHalfWidthMap.put('\u30DD', "\uFF8E\uFF9F"); 597 sHalfWidthMap.put('\u30DE', "\uFF8F"); 598 sHalfWidthMap.put('\u30DF', "\uFF90"); 599 sHalfWidthMap.put('\u30E0', "\uFF91"); 600 sHalfWidthMap.put('\u30E1', "\uFF92"); 601 sHalfWidthMap.put('\u30E2', "\uFF93"); 602 sHalfWidthMap.put('\u30E3', "\uFF6C"); 603 sHalfWidthMap.put('\u30E4', "\uFF94"); 604 sHalfWidthMap.put('\u30E5', "\uFF6D"); 605 sHalfWidthMap.put('\u30E6', "\uFF95"); 606 sHalfWidthMap.put('\u30E7', "\uFF6E"); 607 sHalfWidthMap.put('\u30E8', "\uFF96"); 608 sHalfWidthMap.put('\u30E9', "\uFF97"); 609 sHalfWidthMap.put('\u30EA', "\uFF98"); 610 sHalfWidthMap.put('\u30EB', "\uFF99"); 611 sHalfWidthMap.put('\u30EC', "\uFF9A"); 612 sHalfWidthMap.put('\u30ED', "\uFF9B"); 613 sHalfWidthMap.put('\u30EE', "\uFF9C"); 614 sHalfWidthMap.put('\u30EF', "\uFF9C"); 615 sHalfWidthMap.put('\u30F0', "\uFF72"); 616 sHalfWidthMap.put('\u30F1', "\uFF74"); 617 sHalfWidthMap.put('\u30F2', "\uFF66"); 618 sHalfWidthMap.put('\u30F3', "\uFF9D"); 619 sHalfWidthMap.put('\u30F4', "\uFF73\uFF9E"); 620 sHalfWidthMap.put('\u30F5', "\uFF76"); 621 sHalfWidthMap.put('\u30F6', "\uFF79"); 622 sHalfWidthMap.put('\u30FB', "\uFF65"); 623 sHalfWidthMap.put('\u30FC', "\uFF70"); 624 sHalfWidthMap.put('\uFF01', "!"); 625 sHalfWidthMap.put('\uFF02', "\""); 626 sHalfWidthMap.put('\uFF03', "#"); 627 sHalfWidthMap.put('\uFF04', "$"); 628 sHalfWidthMap.put('\uFF05', "%"); 629 sHalfWidthMap.put('\uFF06', "&"); 630 sHalfWidthMap.put('\uFF07', "'"); 631 sHalfWidthMap.put('\uFF08', "("); 632 sHalfWidthMap.put('\uFF09', ")"); 633 sHalfWidthMap.put('\uFF0A', "*"); 634 sHalfWidthMap.put('\uFF0B', "+"); 635 sHalfWidthMap.put('\uFF0C', ","); 636 sHalfWidthMap.put('\uFF0D', "-"); 637 sHalfWidthMap.put('\uFF0E', "."); 638 sHalfWidthMap.put('\uFF0F', "/"); 639 sHalfWidthMap.put('\uFF10', "0"); 640 sHalfWidthMap.put('\uFF11', "1"); 641 sHalfWidthMap.put('\uFF12', "2"); 642 sHalfWidthMap.put('\uFF13', "3"); 643 sHalfWidthMap.put('\uFF14', "4"); 644 sHalfWidthMap.put('\uFF15', "5"); 645 sHalfWidthMap.put('\uFF16', "6"); 646 sHalfWidthMap.put('\uFF17', "7"); 647 sHalfWidthMap.put('\uFF18', "8"); 648 sHalfWidthMap.put('\uFF19', "9"); 649 sHalfWidthMap.put('\uFF1A', ":"); 650 sHalfWidthMap.put('\uFF1B', ";"); 651 sHalfWidthMap.put('\uFF1C', "<"); 652 sHalfWidthMap.put('\uFF1D', "="); 653 sHalfWidthMap.put('\uFF1E', ">"); 654 sHalfWidthMap.put('\uFF1F', "?"); 655 sHalfWidthMap.put('\uFF20', "@"); 656 sHalfWidthMap.put('\uFF21', "A"); 657 sHalfWidthMap.put('\uFF22', "B"); 658 sHalfWidthMap.put('\uFF23', "C"); 659 sHalfWidthMap.put('\uFF24', "D"); 660 sHalfWidthMap.put('\uFF25', "E"); 661 sHalfWidthMap.put('\uFF26', "F"); 662 sHalfWidthMap.put('\uFF27', "G"); 663 sHalfWidthMap.put('\uFF28', "H"); 664 sHalfWidthMap.put('\uFF29', "I"); 665 sHalfWidthMap.put('\uFF2A', "J"); 666 sHalfWidthMap.put('\uFF2B', "K"); 667 sHalfWidthMap.put('\uFF2C', "L"); 668 sHalfWidthMap.put('\uFF2D', "M"); 669 sHalfWidthMap.put('\uFF2E', "N"); 670 sHalfWidthMap.put('\uFF2F', "O"); 671 sHalfWidthMap.put('\uFF30', "P"); 672 sHalfWidthMap.put('\uFF31', "Q"); 673 sHalfWidthMap.put('\uFF32', "R"); 674 sHalfWidthMap.put('\uFF33', "S"); 675 sHalfWidthMap.put('\uFF34', "T"); 676 sHalfWidthMap.put('\uFF35', "U"); 677 sHalfWidthMap.put('\uFF36', "V"); 678 sHalfWidthMap.put('\uFF37', "W"); 679 sHalfWidthMap.put('\uFF38', "X"); 680 sHalfWidthMap.put('\uFF39', "Y"); 681 sHalfWidthMap.put('\uFF3A', "Z"); 682 sHalfWidthMap.put('\uFF3B', "["); 683 sHalfWidthMap.put('\uFF3C', "\\"); 684 sHalfWidthMap.put('\uFF3D', "]"); 685 sHalfWidthMap.put('\uFF3E', "^"); 686 sHalfWidthMap.put('\uFF3F', "_"); 687 sHalfWidthMap.put('\uFF41', "a"); 688 sHalfWidthMap.put('\uFF42', "b"); 689 sHalfWidthMap.put('\uFF43', "c"); 690 sHalfWidthMap.put('\uFF44', "d"); 691 sHalfWidthMap.put('\uFF45', "e"); 692 sHalfWidthMap.put('\uFF46', "f"); 693 sHalfWidthMap.put('\uFF47', "g"); 694 sHalfWidthMap.put('\uFF48', "h"); 695 sHalfWidthMap.put('\uFF49', "i"); 696 sHalfWidthMap.put('\uFF4A', "j"); 697 sHalfWidthMap.put('\uFF4B', "k"); 698 sHalfWidthMap.put('\uFF4C', "l"); 699 sHalfWidthMap.put('\uFF4D', "m"); 700 sHalfWidthMap.put('\uFF4E', "n"); 701 sHalfWidthMap.put('\uFF4F', "o"); 702 sHalfWidthMap.put('\uFF50', "p"); 703 sHalfWidthMap.put('\uFF51', "q"); 704 sHalfWidthMap.put('\uFF52', "r"); 705 sHalfWidthMap.put('\uFF53', "s"); 706 sHalfWidthMap.put('\uFF54', "t"); 707 sHalfWidthMap.put('\uFF55', "u"); 708 sHalfWidthMap.put('\uFF56', "v"); 709 sHalfWidthMap.put('\uFF57', "w"); 710 sHalfWidthMap.put('\uFF58', "x"); 711 sHalfWidthMap.put('\uFF59', "y"); 712 sHalfWidthMap.put('\uFF5A', "z"); 713 sHalfWidthMap.put('\uFF5B', "{"); 714 sHalfWidthMap.put('\uFF5C', "|"); 715 sHalfWidthMap.put('\uFF5D', "}"); 716 sHalfWidthMap.put('\uFF5E', "~"); 717 sHalfWidthMap.put('\uFF61', "\uFF61"); 718 sHalfWidthMap.put('\uFF62', "\uFF62"); 719 sHalfWidthMap.put('\uFF63', "\uFF63"); 720 sHalfWidthMap.put('\uFF64', "\uFF64"); 721 sHalfWidthMap.put('\uFF65', "\uFF65"); 722 sHalfWidthMap.put('\uFF66', "\uFF66"); 723 sHalfWidthMap.put('\uFF67', "\uFF67"); 724 sHalfWidthMap.put('\uFF68', "\uFF68"); 725 sHalfWidthMap.put('\uFF69', "\uFF69"); 726 sHalfWidthMap.put('\uFF6A', "\uFF6A"); 727 sHalfWidthMap.put('\uFF6B', "\uFF6B"); 728 sHalfWidthMap.put('\uFF6C', "\uFF6C"); 729 sHalfWidthMap.put('\uFF6D', "\uFF6D"); 730 sHalfWidthMap.put('\uFF6E', "\uFF6E"); 731 sHalfWidthMap.put('\uFF6F', "\uFF6F"); 732 sHalfWidthMap.put('\uFF70', "\uFF70"); 733 sHalfWidthMap.put('\uFF71', "\uFF71"); 734 sHalfWidthMap.put('\uFF72', "\uFF72"); 735 sHalfWidthMap.put('\uFF73', "\uFF73"); 736 sHalfWidthMap.put('\uFF74', "\uFF74"); 737 sHalfWidthMap.put('\uFF75', "\uFF75"); 738 sHalfWidthMap.put('\uFF76', "\uFF76"); 739 sHalfWidthMap.put('\uFF77', "\uFF77"); 740 sHalfWidthMap.put('\uFF78', "\uFF78"); 741 sHalfWidthMap.put('\uFF79', "\uFF79"); 742 sHalfWidthMap.put('\uFF7A', "\uFF7A"); 743 sHalfWidthMap.put('\uFF7B', "\uFF7B"); 744 sHalfWidthMap.put('\uFF7C', "\uFF7C"); 745 sHalfWidthMap.put('\uFF7D', "\uFF7D"); 746 sHalfWidthMap.put('\uFF7E', "\uFF7E"); 747 sHalfWidthMap.put('\uFF7F', "\uFF7F"); 748 sHalfWidthMap.put('\uFF80', "\uFF80"); 749 sHalfWidthMap.put('\uFF81', "\uFF81"); 750 sHalfWidthMap.put('\uFF82', "\uFF82"); 751 sHalfWidthMap.put('\uFF83', "\uFF83"); 752 sHalfWidthMap.put('\uFF84', "\uFF84"); 753 sHalfWidthMap.put('\uFF85', "\uFF85"); 754 sHalfWidthMap.put('\uFF86', "\uFF86"); 755 sHalfWidthMap.put('\uFF87', "\uFF87"); 756 sHalfWidthMap.put('\uFF88', "\uFF88"); 757 sHalfWidthMap.put('\uFF89', "\uFF89"); 758 sHalfWidthMap.put('\uFF8A', "\uFF8A"); 759 sHalfWidthMap.put('\uFF8B', "\uFF8B"); 760 sHalfWidthMap.put('\uFF8C', "\uFF8C"); 761 sHalfWidthMap.put('\uFF8D', "\uFF8D"); 762 sHalfWidthMap.put('\uFF8E', "\uFF8E"); 763 sHalfWidthMap.put('\uFF8F', "\uFF8F"); 764 sHalfWidthMap.put('\uFF90', "\uFF90"); 765 sHalfWidthMap.put('\uFF91', "\uFF91"); 766 sHalfWidthMap.put('\uFF92', "\uFF92"); 767 sHalfWidthMap.put('\uFF93', "\uFF93"); 768 sHalfWidthMap.put('\uFF94', "\uFF94"); 769 sHalfWidthMap.put('\uFF95', "\uFF95"); 770 sHalfWidthMap.put('\uFF96', "\uFF96"); 771 sHalfWidthMap.put('\uFF97', "\uFF97"); 772 sHalfWidthMap.put('\uFF98', "\uFF98"); 773 sHalfWidthMap.put('\uFF99', "\uFF99"); 774 sHalfWidthMap.put('\uFF9A', "\uFF9A"); 775 sHalfWidthMap.put('\uFF9B', "\uFF9B"); 776 sHalfWidthMap.put('\uFF9C', "\uFF9C"); 777 sHalfWidthMap.put('\uFF9D', "\uFF9D"); 778 sHalfWidthMap.put('\uFF9E', "\uFF9E"); 779 sHalfWidthMap.put('\uFF9F', "\uFF9F"); 780 sHalfWidthMap.put('\uFFE5', "\u005C\u005C"); 781 } 782 783 /** 784 * Return half-width version of that character if possible. Return null if not possible 785 * @param ch input character 786 * @return CharSequence object if the mapping for ch exists. Return null otherwise. 787 */ tryGetHalfWidthText(char ch)788 public static CharSequence tryGetHalfWidthText(char ch) { 789 if (sHalfWidthMap.containsKey(ch)) { 790 return sHalfWidthMap.get(ch); 791 } else { 792 return null; 793 } 794 } 795 }